Reputation: 300
I need to listen indefinitely for key-presses
from an external hardware in my app, and to do so I would like to create a bound service
that does the work as long as my application is running.
The problem is, this work needs to be done on a separate thread
and so far I have only been able to get my service to communicate preset values back to my activity
. After doing some research, I understand that this is because Services by default run on the ui thread
.
...But if I run my thread in a service I have no idea how to send the processed result back to my activity.
Back when I was trying to do this process directly in an activity I would just use the runOnUiThread()
method to update a text view as soon as the key press was processed, but because I am using a service
now I don't have access to that method.
My DeviceService
class uses a Handler and Messenger to receive responses from the client activity:
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case (MSG_START_REST):
// This starts a worker thread.
// I DON'T KNOW HOW TO GET THE RESULT BACK TO THE UI THREAD
Log.v(TAG, "Starting listening for keypad");
startREST(msg);
// THIS IS WHERE I'M HAVING TROUBLE
// I want to set this to the result of the above call...
String keyPressed = "ice cream";
try {
// Response
Message keyResponse = Message.obtain(null, KEYPRESS);
Bundle bundle = new Bundle();
bundle.putString("keyPressed", keyPressed);
keyResponse.setData(bundle);
msg.replyTo.send(keyResponse);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
// This works.
case (TO_UPPERCASE):
try {
// Incoming data
String data = msg.getData().getString("data");
// Response
Message resp = Message.obtain(null, TO_UPPERCASE_RESPONSE);
Bundle bResp = new Bundle();
bResp.putString("respData", data.toUpperCase());
resp.setData(bResp);
msg.replyTo.send(resp);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
// Target we publish for clients to send messages to IncomingHandler.
final Messenger mMessenger = new Messenger(new IncomingHandler());
I won't show all of my thread processing (it works fine), but here is where it finishes and gets it's result. It is passed a single line of json from which is extracts the keycode.
Again, my problem is I don't know how to get this keycode back to my activity.
private void sendKeyboardEvent(final String keyboardEvent) {
Log.v(TAG, "Keyboard event : " + keyboardEvent);
// Parse json to find keycode
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(keyboardEvent);
// THIS IS THE KEYCODE. IT WILL BE SOMETHING LIKE "30" or "31"
String keycode = jsonObject.getString("pressedKeycodes");
Log.v(TAG, "Keycode : " + keycode);
} catch (JSONException e) {
e.printStackTrace();
}
}
My MainActivity
also has a Handler and Messenger to interpret responses from the Service.
// Handler of incoming messages from clients.
class ResponseHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case (DeviceService.KEYPRESS):
String result4 = msg.getData().getString("keyPressed");
Toast.makeText(getApplicationContext(), "A key was pressed! -"+ result4, Toast.LENGTH_SHORT).show();
break;
case (DeviceService.TO_UPPERCASE_RESPONSE):
String result = msg.getData().getString("respData");
TextView tvResult = (TextView) findViewById(R.id.textView_result);
tvResult.setText(result);
break;
default:
super.handleMessage(msg);
}
}
}
And here is where I send calls to the service. This also seems to work fine.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnResult = (Button) findViewById(R.id.btnResult);
btnResult.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String val = "hello";
Message msg = Message.obtain(null, DeviceService.TO_UPPERCASE);
msg.replyTo = new Messenger(new ResponseHandler());
Bundle b = new Bundle();
b.putString("data", val);
msg.setData(b);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
Button btnStart = (Button) findViewById(R.id.btn_start_keypad_listener);
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message msg = Message.obtain(null, DeviceService.MSG_START_REST);
msg.replyTo = new Messenger(new ResponseHandler());
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
Tldr, I need to send the result from a thread running outside my MainActivity, back to my MainActivity and the UI thread.
Please help?
Upvotes: 0
Views: 93
Reputation: 479
If you need to keep Service and Activity decoupled, Android allows you to use Broadcasts. You can check: https://developer.android.com/guide/components/broadcasts.html In your case, the Activity will register to wait for Broadcasts send from your Service so, when your Service complete the job, or any task, could send a broadcast which will be listen for your Activity. Here there is a sample: Using a broadcast intent/broadcast receiver to send messages from a service to an activity Hope it helps.
Upvotes: 2