I am having problem with chatting app , I am trying to run chat receiver functionality using handler such that as soon as messages are received they are taken care of and displayed on screen . But it fails when I try to go back and resume the chatting, since Handler keeps on running so is the message object associated with it , and it fails to reinitialize it. Following is the code :
public class hotListener extends ListActivity {
private HotspotService service;
private XMPPConnection connection;
private IBinder binder;
private Handler mHandler = new Handler();
private ArrayList<String> messages = new ArrayList<String>();
ArrayList<ChatMessage> messagex= new ArrayList<ChatMessage>();
ChattingAdapter adaptex= new ChattingAdapter(hotListener.this, messagex);;
HotspotService.MyBinder binderx;
Intent mIntent ;
private ListView listview;
EditText sender_message ;
String msg;
Thread t;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listener);
setListAdapter(adaptex);
System.out.println("inside on create");
Button send_button = (Button) findViewById(R.id.chat_send_message);
sender_message = (EditText) findViewById(R.id.chat_input);
send_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
msg = sender_message.getText().toString();
sender_message.setText("");
if(!(msg.length()==0)){
messagex.add(new ChatMessage(msg, true));
//addNewMessage(new ChatMessage(msg, true));
adaptex.notifyDataSetChanged();
getListView().setSelection(messagex.size()-1);
}
}
});
if(!isMyServiceRunning()){
System.out.println("seems like service not running");
startService(new Intent(this,HotspotService.class));
System.out.print(" now started ");
}
}
#Override
protected void onStart(){
super.onStart();
System.out.println("in onstart");
}
private void receivespots(XMPPConnection connection2, final ChattingAdapter adaptex2) {
connection2.getChatManager().addChatListener(new ChatManagerListener() {
#Override
public void chatCreated(Chat arg0, boolean arg1) {
arg0.addMessageListener(new MessageListener() {
#Override
public void processMessage(Chat chat, Message message) {
//final String from = message.getFrom();
final String body = message.getBody();
mHandler.post(new Runnable() {
#Override
public void run() {
messagex.add(new ChatMessage(body, false));
for(int i=0;i<messagex.size();i++){
ChatMessage xc = messagex.get(i);
System.out.println(xc.message);
}
adaptex.notifyDataSetChanged();
getListView().setSelection(messagex.size()-1);
Toast.makeText(hotListener.this,body,Toast.LENGTH_SHORT).show();
}
});
}
});
}
});
}
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for(RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)){
if(HotspotService.class.getName().equals(service.service.getClassName())){
return true;
}
}
return false;
}
#Override
protected void onResume() {
bindService(new Intent(this, HotspotService.class), mConnection, Context.BIND_AUTO_CREATE);
adaptex.notifyDataSetChanged();
System.out.println("inside on resume");
super.onResume();
}
#Override
protected void onDestroy(){
super.onDestroy();
System.out.println("in on destroy");
unbindService(mConnection);
mHandler.removeCallbacksAndMessages(null);
}
#Override
protected void onPause() {
System.out.println("inside on pause");
super.onPause();
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
connection = null;
service = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
service = ((HotspotService.MyBinder)binder).getService();
connection = service.getConnection();
receivespots(connection,adaptex);
}
};
}
Is it right way to run such methods ? Definitely not , I can also try to save messages in sqlite and reload on display but that will also fail , since messagex associated with mhandler does not reinitializes and fails to display any message received on screen after resume of activity . It does work properly for first time . But moment messagex is used in handler it keeps on appending messages to old messagex and fails to display after resume on activity
Related
I'm have some project, realtime sending RSSI and MAC of Bluetooth Low Energy.
I've seen questions regarding MQTT reconnecting. But I'm confused when it is applied to my origin code.
MainActivity.Java
public class MainActivity extends AppCompatActivity {
private Button turnon, changeLayout;
MqttAndroidClient client;
private boolean state=false;
private BluetoothAdapter bluetoothAdapter;
public static final int REQUEST_ACCESS_COARSE_LOCATION = 1;
public static final int REQUEST_ENABLE_BLUETOOTH = 11;
public static String mqtt_server,mqtt_port,mqtt_id,mqtt_topic;
private TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().hide();
setContentView(R.layout.activity_main);
Log.d("Logger", "On Create Android");
turnon = findViewById(R.id.turnon);
changeLayout = findViewById(R.id.mqttSet);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
textView = findViewById(R.id.textView4);
textView.setText("id "+mqtt_id+" port "+mqtt_port+" server "+mqtt_server);
client = new MqttAndroidClient(this.getApplicationContext(), "tcp://"+mqtt_server+":"+mqtt_port,mqtt_id);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
stateCheck();
Log.d("Logger", "State Check");
handler.postDelayed(this, 1000);
}
}, 1000);
turnon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!state){
turnon.setText("Turn Off");
Log.d("Logger", "Turn On State");
if(mqtt_server!=null||mqtt_id!=null||mqtt_port!=null||mqtt_topic!=null){
try {
Log.d("Logger", "Try ");
IMqttToken token = client.connect();
token.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.d("Logger", "Connect MQTT");
Toast.makeText(MainActivity.this,"connected!!",Toast.LENGTH_LONG).show();
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.d("Logger", "Connect Failed");
Toast.makeText(MainActivity.this,"connection failed!!",Toast.LENGTH_LONG).show();
}
});
} catch (MqttException e) {
e.printStackTrace();
Log.d("Logger", "error"+e);
}}
state = true;
}else{
turnon.setText("Turn On");
state = false;
}
}
});
changeLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this,MqttActivity.class));
}
});
}
public void stateCheck(){
if (state){
Log.d("Logger", "Discover");
}
}
}
The code I made has not been able to reconnect when my server restarts.
Can anyone help me? But I'm asking for help with indicating which code I should change. Thanks
I have a service listening for UDP packets that is bound to my MainActivity (which is the only activity in the app). The service runs on its own thread and I can see the UDP messages as well as the parsed messages in logcat. I created a setParsedMessage() and a public getParsedMessage() in order to get the parsed string and send it to my main activity in order to change a TextView and an ImageView depending on what the parsed message is, however it does not appear to be retrieving the String for some reason. I read about this method on the Developer.Android website, however I've also seen something about using Handler to do this instead. Here is my code:
MainActivity:
public class MainActivity extends Activity {
AlertAssignments mAlertAssignments;
Button startListeningButton;
boolean started;
int counter;
boolean mBound = false;
Context context;
ListenerService mListenerService;
TextView mTextView;
TextView mBlinkView;
ImageView mImageView;
private StartListening _StartListeningTask;
String messageFromService = "";
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//start listener service
Intent listenerServiceIntent = new Intent(MainActivity.this, ListenerService.class);
this.bindService(listenerServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
mImageView = (ImageView) findViewById(R.id.image_view);
mTextView = (TextView) findViewById(R.id.alert_text);
mBlinkView = (TextView) findViewById(R.id.blinking_text);
Animation mAnimation = new AlphaAnimation(0.0f, 1.0f);
mAnimation.setDuration(50);
mAnimation.setStartOffset(20);
mAnimation.setRepeatCount(Animation.INFINITE);
mAnimation.setRepeatMode(Animation.REVERSE);
mBlinkView.startAnimation(mAnimation); //animation value
mAlertAssignments = new AlertAssignments();
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
ListenerService.LocalBinder binder = (ListenerService.LocalBinder) service;
mListenerService = binder.getService();
mBound = true;
if(mBound) {
Log.e("UDP", "Service has been bound successfully");
}
else {
Log.e("UDP", "Service has not been bound");
}
readFromService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
#Override
protected void onStart() {
super.onStart();
}
#Override
protected void onStop() {
super.onStop();
//unbind from service
if(mBound) {
this.unbindService(mConnection);
mBound = false;
}
}
private void readFromService() {
try {
Integer parsedMessage = Integer.valueOf(mListenerService.getParsedMessage());
mImageView.setImageResource(mAlertAssignments.alarmImages[parsedMessage]);
if(parsedMessage >= 10 && parsedMessage <= 19 && parsedMessage != 0) {
mTextView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
} else {
mBlinkView.setText(mAlertAssignments.alertTextMessages[parsedMessage]);
}
} catch(NumberFormatException e) {
e.printStackTrace();
}
}
}
I had read that using the public getter like this:
Integer parsedMessage = Integer.valueOf(mListenerService.getParsedMessage());
would allow me to access the string value of mListenerService.getParsedMessage, however I'm guessing that may only work for started services, not bound services.
AlertAssignments is a simple enumeration that uses ordinal arrays to bind images and Strings to values, so mImageView.setImageResource(mAlertAssignments.alarmImages[parsedMessage]) would set the ImageView to an image. Finally, here is the Service:
public class ListenerService extends Service{
public String the_alarm_S;
public String parsedMessage = "";
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
ListenerService getService() {
return ListenerService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
DatagramSocket socket;
Thread UDPBroadcastThread;
void startListenForUDPBroadcast() {
UDPBroadcastThread = new Thread(new Runnable() {
public void run() {
try {
while (shouldRestartSocketListen) {
try {
socket = new DatagramSocket(12001);
socket.setReuseAddress(true);
String message = "";
byte[] recvBuf = new byte[1024];
DatagramPacket packet = new DatagramPacket(recvBuf, 1024);
Log.e("UDP", "Waiting for UDP broadcast");
try {
socket.receive(packet);
Log.e("UDP", "Received Packet");
} catch (IOException e) {
e.printStackTrace();
}
message = new String(packet.getData());
Log.e("UDP", "Got UDB broadcast message: " + message);
setParsedMessage(message);
if(socket != null) {
socket.close();
}
} catch (SocketException e) {
e.printStackTrace();
}
}
//if (!shouldListenForUDPBroadcast) throw new ThreadDeath();
} catch (Exception e) {
Log.i("UDP", "no longer listening for UDP broadcasts cause of error " + e.getMessage());
}
}
});
UDPBroadcastThread.start();
}
private Boolean shouldRestartSocketListen = true;
private void setParsedMessage(String messageContents) {
the_alarm_S = messageContents;
String parseMessage[] = the_alarm_S.split("!!!");
Log.e("UDP", "Parsed message with value " + parseMessage[1]);
parsedMessage = parseMessage[1];
}
public String getParsedMessage() {
return parsedMessage;
}
private void stopListen() {
shouldRestartSocketListen = false;
if(socket != null) {
socket.close();
}
}
#Override
public void onCreate() {
startListenForUDPBroadcast();
}
#Override
public void onDestroy() {
stopListen();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
shouldRestartSocketListen = true;
startListenForUDPBroadcast();
Log.i("UDP", "Service started");
return START_STICKY;
}
}
Can someone give me the simplest method of getting the String from the service to the main activity, or if I already have it, where I am going wrong in using it? I would like to avoid having to rewrite my Service as an IntentService unless it's absolutely necessary to do so since this is a relatively simple object to pass to MainActivity
Thanks
You could try subscribing to the service. What I mean is pass some interface that the service calls to notify the activity about changes, here's an example I just tested:
A Subscriber interface
public interface ServiceSubscriber {
void messageCallback(String message);
}
Subscribe to the service using the Subscriber
public class TestService extends Service {
ArrayList<ServiceSubscriber> subscribers = new ArrayList<>();
private TestBinder testBinder = new TestBinder();
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(){
#Override
public void run() {
while(true){
//this is where you are receiving UDP packets
doStuff();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
return super.onStartCommand(intent, flags, startId);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return testBinder;
}
private void doStuff() {
System.out.println("Service is doing stuff!");
//loop through your subscribers and notify them of your changes
//a loop here isn't very costly, if there aren't many subscribers
for (ServiceSubscriber subscriber : subscribers) {
subscriber.messageCallback("I'm doing stuff");
}
}
public class TestBinder extends Binder {
public TestService getService() {
return TestService.this;
}
}
public void subscribeToMessages(ServiceSubscriber subscriber) {
subscribers.add(subscriber);
}
public void unSubscribeToMessages(ServiceSubscriber subscriber) {
subscribers.remove(subscriber);
}
}
Now for the usual Binding Activity, where you define what you need to do with the Message Callback:
public class MainActivity extends AppCompatActivity {
private TestService testService;
private Subscriber subscriber;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, TestService.class),serviceConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
testService = ((TestService.TestBinder)service).getService();
subscriber = new ServiceSubscriber() {
#Override
public void messageCallback(String message) {
//I'm just printing out the message received
//Be careful if you need to do UI stuff to use a
//Handler
System.out.println(message);
}
}
testService.subscribeToMessages(subscriber );
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
}
Of course don't forget to unsubscribe on destroy.
Updating UI often doesn't break your app if you do it by using a handler
//activity fields
Handler handler
//in activity constructor
handler = new Handler();
//update UI by calling
handler.post(new Runnable(){
#Override
public void run(){
//update the UI here
}
EDIT: I forgot to keep a reference of the subscriber, to unsubscribe later. Changed from anonymous instance to a field.
Make below method to your sevice class:
private void sendMessage() {
Intent intent = new Intent("message");
intent.putExtra("message", your_message);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
And put the below code in your activity class:
#Override
public void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this)
.registerReceiver(mMessageReceiver,
new IntentFilter("message"));
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String yourMessage = intent.getIntExtra("message",-1);
}
};
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(mMessageReceiver);
super.onPause();
}
Note: -1 is for default value
I am creating a BLE app that needs to continuously start and stop scanning for predefined intervals. The way I have implemented it is with two runnables that call each other as follows:
private Runnable scan = new Runnable() {
#Override
public void run() {
scanHandler.postDelayed(stopScan, SCAN_PERIOD);
mLEScanner.startScan(filters, settings, mScanCallback);
Log.e("BLE_Scanner", "Start Scan");
}
};
private Runnable stopScan = new Runnable() {
#Override
public void run() {
mLEScanner.stopScan(mScanCallback);
scanHandler.postDelayed(scan, STOP_PERIOD);
Log.e("BLE_Scanner", "Stop Scan");
}
};
I am attempting to start the continuous scan and pause on a button click. The start button starts the process fine but I am having trouble with stopping the scanning.
//scan button functionality
scanButton=(Button)findViewById(R.id.scan_button);
scanButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
spinner.setVisibility(View.VISIBLE);
scan.run();
}
});
//stop scan button functionality
stopButton=(Button)findViewById(R.id.stop_button);
stopButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
spinner.setVisibility(View.INVISIBLE);
scanHandler.removeCallbacks(scan);
scanHandler.removeCallbacks(stopScan);
}
});
If I press the stop button during the stop interval the scan will stop. However, if I press the stop button while the scan runnable is running it appears to remove the callbacks for the stopScan runnable while leaving the scan runnable continuously running. What I need is for both of the runnables to stop on the button press. To provide more detail, my entire code is provided below. Thanks for the help.
public class MainActivity extends Activity {
private BluetoothAdapter mBluetoothAdapter;
private int REQUEST_ENABLE_BT = 1;
private static final long SCAN_PERIOD = 5000;
private static final long STOP_PERIOD = 1000;
private BluetoothLeScanner mLEScanner;
private ScanSettings settings;
private List<ScanFilter> filters;
private BluetoothGatt mGatt;
private Button scanButton;
private Button stopButton;
//private String proximityUUID = "0000180f-0000-1000-8000-00805f9b34fb";
private ProgressBar spinner;
private Handler scanHandler;
private String[] filterList = {
"D9:ED:5F:FA:0E:02",
"FF:37:3A:25:56:C7",
"F4:57:89:69:93:91"
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
scanHandler = new Handler();
//determine if device supports BLE
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE Not Supported",
Toast.LENGTH_SHORT).show();
finish();
}
//set up bluetooth manager
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
//scan progress bar
spinner=(ProgressBar)findViewById(R.id.progressBar);
spinner.setVisibility(View.GONE);
//scan button functionality
scanButton=(Button)findViewById(R.id.scan_button);
scanButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
spinner.setVisibility(View.VISIBLE);
scan.run();
}
});
//stop scan button functionality
stopButton=(Button)findViewById(R.id.stop_button);
stopButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
spinner.setVisibility(View.INVISIBLE);
scanHandler.removeCallbacks(scan);
scanHandler.removeCallbacks(stopScan);
}
});
}
#Override
protected void onResume() {
super.onResume();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
//scan settings
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
//scan filter
//populate the filter list
filters = new ArrayList<ScanFilter>();
for (int i=0; i< filterList.length ; i++) {
ScanFilter filter = new ScanFilter.Builder().setDeviceAddress(filterList[i]).build();
filters.add(filter);
}
}
}
#Override
protected void onPause() {
super.onPause();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
}
}
#Override
protected void onDestroy() {
if (mGatt == null) {
return;
}
mGatt.close();
mGatt = null;
super.onDestroy();
}
//start scan
private Runnable scan = new Runnable() {
#Override
public void run() {
scanHandler.postDelayed(stopScan, SCAN_PERIOD);
mLEScanner.startScan(filters, settings, mScanCallback);
Log.e("BLE_Scanner", "Start Scan");
}
};
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
BluetoothDevice device = result.getDevice();
int mRSSI = result.getRssi();
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
#Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
//stop scan
private Runnable stopScan = new Runnable() {
#Override
public void run() {
mLEScanner.stopScan(mScanCallback);
scanHandler.postDelayed(scan, STOP_PERIOD);
Log.e("BLE_Scanner", "Stop Scan");
}
};
private static double calculateAccuracy(int txPower, double rssi) {
if (rssi == 0) {
return -1.0; // if we cannot determine accuracy, return -1.
}
double ratio = -rssi*1.0/txPower;
if (ratio < 1.0) {
return Math.pow(ratio,10);
}
else {
double accuracy = (0.89976)*Math.pow(ratio,7.7095) + 0.111;
return accuracy;
}
}
}
I imagine you just want to call startScan immediately on start button press (not in a Runnable, not scheduled through a Handler). The call is asynchronous, so nothing will block, and Android will do all the scanning in another thread. If you then want to schedule a call to stop in the future, then you use the Handler to post a Runnable that calls stopScan at the delay you need.
The button for stopping the scan can also just directly call stopScan() if know that a scan was previously in progress. You might want to use a boolean to gate the call to stopScan only if there was a previous call to startScan().
So, I ended up finding a way to get it to work as intended. I don't know if the way I'm doing things is best practice as I'm new to Android and Java, but this is what worked for me. All I did was call the stopScan method in the stop button after removing the handler callbacks.
//stop scan button functionality
stopButton=(Button)findViewById(R.id.stop_button);
stopButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
spinner.setVisibility(View.INVISIBLE);
scanHandler.removeCallbacksAndMessages(null);
mLEScanner.stopScan(mScanCallback);
}
});
Is it possible to bindService to a Service from a Thread?
I try to pass a String to the Service running, and the run a method to show a notification?
Any idea what's the correct way to do it?
Thanks in advance.
May you should try this way:
First write your Service class.
public class ShowNotifyService extends Service {
private Messenger msg = new Messenger(new ShowNotifyHanlder());
#Override
public IBinder onBind(Intent arg0) {
return msg.getBinder();
}
class ShowNotifyHanlder extends Handler {
#Override
public void handleMessage(Message msg) {
// This is the action
int msgType = msg.what;
switch(msgType) {
case SHOW_FIRST_NOTIFY: {
try {
// Incoming data
String data = msg.getData().getString("data");
Message resp = Message.obtain(null, SHOW_FIRST_NOTIFY_RESPONSE);
Bundle bResp = new Bundle();
bResp.putString("respData", first_notify_data);// here you set the data you want to show
resp.setData(bResp);
msg.replyTo.send(resp);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
default:
super.handleMessage(msg);
}
}
}
Then write your Activity class.
public class TestActivity {
..
private ServiceConnection sConn;
private Messenger messenger;
..
#Override
protected void onCreate(Bundle savedInstanceState) {
// Service Connection to handle system callbacks
sConn = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
messenger = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
// We are conntected to the service
messenger = new Messenger(service);
}
};
...
// We bind to the service
bindService(new Intent(this, ShowNotifyService.class), sConn,
Context.BIND_AUTO_CREATE);
..
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String val = edt.getText().toString();
Message msg = Message.obtain(null, ShowNotifyService.SHOW_FIRST_NOTIFY);
msg.replyTo = new Messenger(new ResponseHandler());
// We pass the value
Bundle b = new Bundle();
b.putString("data", val);
msg.setData(b);
try {
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
// This class handles the Service response
class ResponseHandler extends Handler {
#Override
public void handleMessage(Message msg) {
int respCode = msg.what;
switch (respCode) {
case ShowNotifyService.SHOW_FIRST_NOTIFY_RESPONSE: {
result = msg.getData().getString("respData");
//then you show the result data from service here
}
}
}
}
}
I got this idea from here.
How to pass handler from an activity to service? I am trying to update the activity UI on the state of Bluetooth connection by using Handler as shown below from service class.
mHandler.obtainMessage(MenuActivity.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
In the activity, I implemented this:
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_STATE_CHANGE:
if (true)
Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
switch(msg.arg1){
case BluetoothService.STATE_CONNECTED:
showToast("Connected to " + mConnectedDeviceName, Toast.LENGTH_SHORT);
break;
I tried to use a constructor to pass the handler from the activity to service like this:
public BluetoothService(Handler handler, BluetoothAdapter mBluetoothAdapter) {
mAdapter = mBluetoothAdapter;
mState = STATE_NONE;
mHandler = handler;
}
But there was an error which shows Unable to instantiate service and found that the service needs to have a public no-args constructor. But after removing the constructor, the handler did not get passed into the service.
How to solve this problem?
You have to bind to the service from activity and establish a ServiceConnection and then get the instance of service and set your handler.
Here is the activity and service class which i use for one of my media player application.....
public class MainActivity extends Activity
{
private CustomService mService = null;
private boolean mIsBound;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
startService(new Intent(this.getBaseContext(), CustomService.class));
doBindService();
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
{
mService = ((CustomService.LocalBinder)iBinder).getInstance();
mService.setHandler(yourHandler);
}
#Override
public void onServiceDisconnected(ComponentName componentName)
{
mService = null;
}
};
private void doBindService()
{
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(new Intent(this,
CustomService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
private void doUnbindService()
{
if (mIsBound)
{
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
#Override
protected void onDestroy()
{
super.onDestroy();
doUnbindService();
}
}
CustomService Code ....
public class CustomService extends Service
{
private final IBinder mIBinder = new LocalBinder();
private Handler mHandler = null;
#Override
public void onCreate()
{
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flag, int startId)
{
return START_STICKY;
}
#Override
public void onDestroy()
{
if(mHandler != null)
{
mHandler = null;
}
}
#Override
public IBinder onBind(Intent intent)
{
return mIBinder;
}
public class LocalBinder extends Binder
{
public CustomService getInstance()
{
return CustomService.this;
}
}
public void setHandler(Handler handler)
{
mHandler = handler;
}
}