Let's imagine we have a simple Android application, which connects to a remote service via IPC, schedules a relatively long task, then continues working while awaiting for callback with some results. AIDL interfaces:
IRemoteService.aidl
package com.var.testservice;
import com.var.testservice.IServCallback;
interface IRemoteService {
void scheduleHeavyTask(IServCallback callback);
}
IRemoteService.aidl
package com.var.testservice;
interface IServCallback {
void onResult(int result);
}
Code for activity:
package com.var.testclient;
import com.var.testservice.IServCallback;
import com.var.testservice.IRemoteService;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
public class MainActivity extends Activity {
private static final String TAG = "TestClientActivity";
private IServCallback.Stub servCallbackListener = new IServCallback.Stub(){
#Override
public void onResult(int result) throws RemoteException {
Log.d(TAG, "Got value: " + result);
}
};
private ServiceConnection servConnection = new ServiceConnection(){
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
service = IRemoteService.Stub.asInterface(binder);
}
#Override
public void onServiceDisconnected(ComponentName name) {
service = null;
}
};
private IRemoteService service;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(!bindService(new Intent(IRemoteService.class.getName()), servConnection, Context.BIND_AUTO_CREATE)){
Log.d(TAG, "Service binding failed");
} else {
Log.d(TAG, "Service binding successful");
}
}
#Override
protected void onDestroy() {
if(service != null) {
unbindService(servConnection);
}
super.onDestroy();
}
public void onButtonClick(View view){
Log.d(TAG, "Button click");
if(service != null){
try {
service.scheduleHeavyTask(servCallbackListener);
} catch (RemoteException e) {
Log.d(TAG, "Oops! Can't schedule task!");
e.printStackTrace();
}
}
}
}
Code for service:
package com.var.testservice;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
public class TestService extends Service {
private static final String TAG = "TestService";
class TestServiceStub extends IRemoteService.Stub {
private IServCallback servCallback;
//These 2 fields will be used a bit later
private Handler handler;
private int result;
//The simpliest implementation. In next snippets I will replace it with
//other version
#Override
public void scheduleHeavyTask(IServCallback callback)
throws RemoteException {
servCallback = callback;
result = doSomethingLong();
callback.onResult(result);
}
private int doSomethingLong(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
return 42;
}
}
}
#Override
public IBinder onBind(Intent intent) {
return new TestServiceStub();
}
}
This version, while being really dumb (it makes UI thread from application hang for 5 seconds, causing ANR), it successfully executes all calls via IPC, delivering result to activity.
Problems start if I try to put calculations into separate thread:
#Override
public void scheduleHeavyTask(IServCallback callback)
throws RemoteException {
servCallback = callback;
Runnable task = new Runnable(){
#Override
public void run() {
result = doSomethingLong();
try {
Log.d(TAG, "Sending result!");
servCallback.onResult(result);
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
new Thread(task).start();
}
In this case the callback is just not delivered to activity: service successfully calls servCallback.onResult(result);, but nothing is called within activity. No exceptions, no clues, no survivors: perfect invocation murder. I couldn't find any information about possible cause of such behavior, so I'd be grateful if someone could clarify what happens here. My suggestion's that there's some kind of security mechanism, tracking which exact threads were bound, and ignoring "unsafe" calls from other threads (something similar happens when we try to mess with UI elements from non-UI thread), but I can't be sure.
The most obvious solution is to post callback invocation to the bound thread, so I made this:
#Override
public void scheduleHeavyTask(IServCallback callback)
throws RemoteException {
Log.d(TAG, "Schedule request received.");
servCallback = callback;
if(Looper.myLooper() == null) {
Looper.prepare();
}
handler = new Handler();
Runnable task = new Runnable(){
#Override
public void run() {
result = doSomethingLong();
Log.d(TAG, "Posting result sender");
handler.post(new Runnable(){
#Override
public void run() {
try {
Log.d(TAG, "Sending result!");
servCallback.onResult(result);
} catch (RemoteException e) {
e.printStackTrace();
}
Looper.myLooper().quit();
Log.d(TAG, "Looper stopped");
}
});
}
};
new Thread(task).start();
Looper.loop();
}
Here I faced 2 more problems:
I had to call Looper.loop() to enable processing of callback runnables, but it blocks IPC, so I have the same result as in the beginning - no actual multithreading;
Registering for callback second time (after first cycle finished and returned value) results in exception:
java.lang.RuntimeException: Handler (android.os.Handler) sending message to a Handler on a dead thread
at android.os.MessageQueue.enqueueMessage
at android.os.Handler.sendMessageAtTime
at android.os.Handler.sendMessageDelayed
at android.os.Handler.post
at com.var.testservice.TestService$TestServiceStub$1.run
at java.lang.Thread.run
This lefts me completely puzzled: I make a fresh instance from actual Looper, how can it point to dead thread?
The whole idea of service being able of queueing tasks and making callbacks when they finish doesn't sound insane to me, so I hope someone more experienced could explain me:
Why can't I actually make IPC calls from different threads?
What's wrong with my Handler?
What instruments/architecture should I use to make a clean, proper queue mechanism, so it could call IPC methods on the right thread without constantly calling Looper.loop()/Looper.quit()?
Thank you.
I can't explain why your program isn't working. But the version involving threads and an asynchronous callback:
#Override
public void scheduleHeavyTask(IServCallback callback)
throws RemoteException {
servCallback = callback;
Runnable task = new Runnable(){
#Override
public void run() {
result = doSomethingLong();
try {
Log.d(TAG, "Sending result!");
servCallback.onResult(result);
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
new Thread(task).start();
}
should work just fine.
Here's how Android arranges threads for AIDL and other types of Binder transaction.
If the caller and the callee are in the same process, it becomes a simple method call. So, you should fine that scheduleHeavyTask is called from the very same thread as onButtonClick. Similarly, you should fine that the call to onResult should be a simple method call, from the thread running the task.
If the caller and callee are in different processes, Android will run the Binder transactions from a pool of threads within the callee process (they are called Binder #1 etc.) Even here it's quite clever - so if onButtonClick called scheduleHeavyTask which called back to onResult from the same thread, then onButtonClick would appear directly to call onResult within the caller process.
There are absolutely no mechanisms to avoid calls from "unsafe" or "unbound" threads - so this simple approach you posted should work. As you say, it's a common pattern, and I've used it lots of times. I'd therefore recommend proceeding with this rather than fiddling with extra loopers and handlers.
Here are some ideas:
Run it in a debugger. Assuming your service and activity are in the same process, you should be able to set breakpoints and see sensible method calls taking place.
Even if you can't, fire up ddms from the command line, or show the Devices and Threads views within Eclipse. This will give you a view of exactly what each thread is doing - you can get a callstack. It's normally possible to use this even in cases where a full-on debugger would be inconvenient.
Do you have the word synchronized anywhere? What could be happening is that onResult wants to go ahead and run, but it's blocked on some monitor. As soon as it becomes unblocked, it might well run.
Related
I go through this link Migrating from Firebase JobDispatcher to WorkManager ,
I found there is Worker and ListenableWorker, where to use these both? any advantage on using any one of them?
Worker :
import android.content.Context;
import androidx.work.Data;
import androidx.work.ListenableWorker.Result;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
class MyWorker extends Worker {
public MyWorker(#NonNull Context appContext, #NonNull WorkerParameters params) {
super(appContext, params);
}
#Override
public ListenableWorker.Result doWork() {
// Do your work here.
Data input = getInputData();
// Return a ListenableWorker.Result
Data outputData = new Data.Builder()
.putString(“Key”, “value”)
.build();
return Result.success(outputData);
}
#Override
public void onStopped() {
// Cleanup because you are being stopped.
}
}
ListenableWorker:
import android.content.Context;
import androidx.work.ListenableWorker;
import androidx.work.ListenableWorker.Result;
import androidx.work.WorkerParameters;
import com.google.common.util.concurrent.ListenableFuture;
class MyWorker extends ListenableWorker {
public MyWorker(#NonNull Context appContext, #NonNull WorkerParameters params) {
super(appContext, params);
}
#Override
public ListenableFuture<ListenableWorker.Result> startWork() {
// Do your work here.
Data input = getInputData();
// Return a ListenableFuture<>
}
#Override
public void onStopped() {
// Cleanup because you are being stopped.
}
}
Workers run synchronously on a background thread. ListenableWorkers are expected to run asynchronously - they are invoked on the main thread and you are supposed to offer all the threading (such as moving them to a background thread). Workers are simpler and are expected to be the basic building block of your app. You can read more about them here:
https://developer.android.com/reference/androidx/work/Worker
https://developer.android.com/reference/androidx/work/ListenableWorker
If you're interested, you can also check out CoroutineWorker and RxWorker.
i have made a java server and java client programms that communicate with TCP sockets sending String messages. Those java programs work perfectly. The client logic is that it has a UI and a thread, which is waiting all the time for new messages and updates the UI accordingly what message it recieved (e.x. add buttons,set buttons visible, change texts).
Now, im totally new to android and i want to make an equal client for android but i faced those problems:
1) I can't update the UI from a background thread just passing parameters(like java).
2) I want that one thread to update 2 or 3 Activies(i need 2-3 because the lack of screen space)
I have read about AsyncTask but they say it is recommended for sort tasks, then i read about threads with handlers but they say is difficult to work with and i got confused. So my question is what method should i use on android to achieve that "message listener/UI updater" thread.
UPDATE: Ok, i used threads and handlers and my main activity got updated, but now how i can update the second Activitie from that thread too??
well, you can start a thread for some specific time and after that check if there is any message from server, if not, restart the thread or else do your work. hope this is helpful.
you can implement it like this
package com.example.a41264.myapplication;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Created by 41264 on 04/23/17.
*/
public class Client implements Runnable{
final CopyOnWriteArrayList<MsgListener> listeners = new CopyOnWriteArrayList<>();
#Override
public void run() {
Socket socket = new Socket();
try {
int yourPort = 0;
SocketAddress address = new InetSocketAddress("your ip",yourPort);
socket.connect(address);
int your_buffer_size = 1024;
byte[] buffers = new byte[your_buffer_size];
InputStream is = socket.getInputStream();
int resultLength;
String msg;
while(true){
resultLength = is.read(buffers);
msg = new String(buffers,0,resultLength);
if(listeners.size() != 0){
for(MsgListener msgListener: listeners){
msgListener.onMsgRecived(msg);
}
}
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void addListener(MsgListener listener){
listeners.add(listener);
}
public void removeListener(MsgListener listener){
listeners.remove(listener);
}
public interface MsgListener{
void onMsgRecived(String msg);
}
}
and do your work at activity like this
package com.example.a41264.myapplication;
import android.app.Activity;
import android.util.Log;
/**
* Created by 41264 on 04/23/17.
*/
public class YourActivity extends Activity{
private Client client; //you need to get your clent;
private Client.MsgListener msgListener = new Client.MsgListener() {
#Override
public void onMsgRecived(final String msg) {
runOnUiThread(new Runnable() {
#Override
public void run() {
//do your own work in main thread
Log.i("tag",msg);
}
});
}
};
#Override
protected void onRestart() {
super.onRestart();
client.addListener(msgListener);
}
#Override
protected void onStop() {
super.onStop();
client.removeListener(msgListener);
}
}
I am using the Google Distance Matrix API to compute distance between different lat longs. I am calling the respective API as mentioned below
public void computeDistanceAndTime()
{
try
{
DistanceMatrixApiRequest req = DistanceMatrixApi.newRequest(Context.getContext()).origins(src)
.destinations(dst).mode(TravelMode.WALKING);
DistanceMatrix matrix = req.await();
}
catch (OverQueryLimitException o)
{
System.out.println(o.getMessage());
}
catch (Exception e) {
e.printStackTrace();
}
}
Actually I want to set a callback using setCallback function DistanceMatrixApiRequest. So I am assuming rather catching the excepetion the callback would be get called and could avoid the
com.google.maps.internal.OkHttpPendingResult retry
exception from google.
Now I am not aware how to set the callback like
what is the signature of the call back?
Do we need to inherit any class to set the callback
What would be the parameters of that function?
I am assuming that needs to called before calling the await function, am I correct?
I could see one example for Geocoding as like below,
req.setCallback(new PendingResult.Callback<GeocodingResult[]>() {
#Override
public void onResult(GeocodingResult[] result) {
// Handle successful request.
}
#Override
public void onFailure(Throwable e) {
// Handle error.
}
});
I am not successfull even if I follow in similar manner for Distance Matrix.
req.setCallback(new PendingResult.Callback<DistanceMatrix[]>() {
#Override
public void onResult(DistanceMatrix[] result) {
// Handle successful request.
}
#Override
public void onFailure(Throwable e) {
System.out.println("There is a failure in Distance function");
}
});
Here is an example of using DistanceMatrixApi with a callback instead of await(). Note, I need to use a CountDownLatch due to OkHTTP invoking the callback on a background thread.
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.maps.DistanceMatrixApi;
import com.google.maps.DistanceMatrixApiRequest;
import com.google.maps.GeoApiContext;
import com.google.maps.PendingResult;
import com.google.maps.model.DistanceMatrix;
import java.util.concurrent.CountDownLatch;
public class DistanceMatrixApp {
public static void main(String[] args) throws InterruptedException {
GeoApiContext context = new GeoApiContext();
context.setApiKey("[API KEY]");
DistanceMatrixApiRequest req = DistanceMatrixApi.getDistanceMatrix(context,
new String[]{"Sydney Town Hall"},
new String[]{"Parramatta Station"});
final CountDownLatch latch = new CountDownLatch(1);
req.setCallback(new PendingResult.Callback<DistanceMatrix>() {
#Override
public void onResult(DistanceMatrix result) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println(gson.toJson(result));
latch.countDown();
}
#Override
public void onFailure(Throwable e) {
System.out.println("Exception thrown: "+e);
latch.countDown();
}
});
// We have to hold the main thread open until callback is called by OkHTTP.
latch.await();
}
}
I've created a service class and a worker class that is executed in a separate thread. I would like to setup a communication between them, so the worker could send some status back to the service.
I've tried to convert my worker Thread to HandlerThread and setup a Handler on the service side, but then I don't know how to actually send a message from the worker. It looks like I can't grasp the concept.
Here's my classes (without communication logic):
Service class
package com.example.app;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class ConnectionService extends Service {
protected ConnectionWorker thread;
#Override
public void onCreate() {
super.onCreate();
// Creating a connection worker thread instance.
// Not starting it yet.
this.thread = new ConnectionWorker();
Log.d(this.getClass().getName(), "Service created");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(this.getClass().getName(), "Service started");
// Checking if worker thread is already running.
if (!this.thread.isAlive()) {
Log.d(this.getClass().getName(), "Starting working thread");
// Starting the worker thread.
this.thread.start();
}
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
Log.d(this.getClass().getName(), "Stopping thread");
// Stopping the thread.
this.thread.interrupt();
Log.d(this.getClass().getName(), "Stopping service");
super.onDestroy();
Log.d(this.getClass().getName(), "Service destroyed");
}
}
Worker class
package com.example.app;
import android.os.SystemClock;
import android.util.Log;
public class ConnectionWorker extends Thread {
public ConnectionWorker() {
super(ConnectionWorker.class.getName());
}
#Override
public void run() {
super.run();
Log.d(this.getClass().getName(), "Thread started");
// Doing the work indefinitely.
while (true) {
if (this.isInterrupted()) {
// Terminating this method when thread is interrupted.
return;
}
// #todo: send a message to the service
Log.d(this.getClass().getName(), "Doing some work for 3 seconds...");
SystemClock.sleep(3 * 1000);
}
}
}
How do I implement a HandlerThread, send messages from the worker thread and receive them in my service?
I will highly appreciate a code example.
Update
Actually, it looks like I can't use Looper inside of my thread cause it will block the thread execution and I don't want that. Is it possible to send messages from the thread without using Looper?
It looks like I've finally nailed it!
In order to pass data from thread back to a service you will need to do this:
Subclass a Handler class inside of your service (call it e.g. a LocalHandler). You will have to make it static. Override a handleMessage method, it will receive messages from the thread.
Add a Handler argument to your Thread constructor. Instantiate your LocalHandler class in a service and inject it to your thread via constructor.
Save reference to the Handler inside of your thread and use it to send messages whenever appropriate.
Here's the complete example:
Service Class
package com.example.app;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
public class ConnectionService extends Service {
protected ConnectionWorker thread;
static class LocalHandler extends Handler {
#Override
public void handleMessage(Message msg) {
Log.d(this.getClass().getName(), "Message received: " + (String) msg.obj);
}
}
protected Handler handler;
#Override
public void onCreate() {
super.onCreate();
// Instantiating overloaded handler.
this.handler = new LocalHandler();
// Creating a connection worker thread instance.
// Not starting it yet.
// Injecting our handler.
this.thread = new ConnectionWorker(this.handler);
Log.d(this.getClass().getName(), "Service created");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(this.getClass().getName(), "Trying to start the service");
// Checking if worker thread is already running.
if (!this.thread.isAlive()) {
Log.d(this.getClass().getName(), "Starting working thread");
// Starting the worker thread.
this.thread.start();
Log.d(this.getClass().getName(), "Service started");
}
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
Log.d(this.getClass().getName(), "Stopping thread");
// Stopping the thread.
this.thread.interrupt();
Log.d(this.getClass().getName(), "Stopping service");
super.onDestroy();
Log.d(this.getClass().getName(), "Service destroyed");
}
}
Worker Class (Thread)
package com.example.app;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
public class ConnectionWorker extends Thread {
// Reference to service's handler.
protected Handler handler;
public ConnectionWorker(Handler handler) {
super(ConnectionWorker.class.getName());
// Saving injected reference.
this.handler = handler;
}
#Override
public void run() {
super.run();
Log.d(this.getClass().getName(), "Thread started");
// Doing the work indefinitely.
while (true) {
if (this.isInterrupted()) {
// Terminating this method when thread is interrupted.
return;
}
Log.d(this.getClass().getName(), "Doing some work for 3 seconds...");
// Sending a message back to the service via handler.
Message message = this.handler.obtainMessage();
message.obj = "Waiting for 3 seconds";
this.handler.sendMessage(message);
SystemClock.sleep(3 * 1000);
}
}
}
I hope it's a valid implementation. If you now a better approach - please let me know.
Slava, actually, your answer doesn't work. Let me explain why.
Why does Android have HandlerThread when Thread already exists?
This is because as the documentation says, HandleThread is a
Handy class for starting a new thread that has a looper. The looper
can then be used to create handler classes. Note that start() must
still be called.
How is a Looper used to create a Handler?
A Looper should be passed in the Handler's constructor to tell the Handler which Looper it is working with.
So each HandlerThread has one Looper and there can be many Handlers handling messages the Looper loops through.
When you created a new Handler in your Service class using the default constructor, the Handler gets associated to the current Looper, as explained in it's documentation.
Handler ()
Default constructor associates this handler with the Looper for the
current thread. If this thread does not have a looper, this handler
won't be able to receive messages so an exception is thrown.
You can confirm the problem still exists using by printing this
Thread.currentThread().getId()
at appropriate places - within the Service's onCreate(), within the Handler's handleMessage() and within the run() of ConnectionWorker. How to solve your problem then?
You can create a HandlerThread, even within the service, and when it's Looper is ready, create the Handler using that Looper.
new HandlerThread("ConnectionWorker") {
#Override
protected void onLooperPrepared() {
new Handler(getLooper()).post(new Runnable() {
#Override
public void run() {
// do your stuff
getLooper().quitSafely();
}
});
}
}.start();
Hope it isn't too late an answer :P
I'm a newbie in android and I always see Exception when I'm running my code.
So, somebody can tell me Can I call a method before app go to crash anywhere without "try-catch".
This would be better way to handle uncaught exception:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
appInitialization();
}
private void appInitialization() {
defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(_unCaughtExceptionHandler);
}
private UncaughtExceptionHandler defaultUEH;
// handler listener
private Thread.UncaughtExceptionHandler _unCaughtExceptionHandler = new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
ex.printStackTrace();
// TODO handle exception here
}
};
}
Application is a Base class for those who need to maintain global application state. And hence here, it will be a better place to handle such exceptions.
EDIT:
The above code will handle uncaught exceptions if they are thrown inside UI thread.
If an exception has occurred in worker thread, you can handle it in following way:
private boolean isUIThread(){
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
// Setup handler for uncaught exceptions.
Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler()
{
#Override
public void uncaughtException (Thread thread, Throwable e)
{
handleUncaughtException (thread, e);
}
});
public void handleUncaughtException(Thread thread, Throwable e) {
e.printStackTrace(); // not all Android versions will print the stack trace automatically
if (isUIThread()) {
// exception occurred from UI thread
invokeSomeActivity();
} else { //handle non UI thread throw uncaught exception
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
invokeSomeActivity();
}
});
}
}
I think what you search is the UncaughtExceptionHandler. It is notified whenever an Exception is fired and not catched on its way bubbling up through your application.
See http://www.intertech.com/Blog/android-handling-the-unexpected/ for more details on implementing this in android.
Try this way
1) create class
import android.content.Context;
import android.content.Intent;
import android.os.Process;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.Thread.UncaughtExceptionHandler;
public class CrashReportHandler implements UncaughtExceptionHandler {
public static void attach(Context context) {
Thread.setDefaultUncaughtExceptionHandler(
new CrashReportHandler(context)
);
}
///////////////////////////////////////////// implementation
private CrashReportHandler(Context context) {
m_context = context;
}
public void uncaughtException(Thread thread, Throwable exception) {
StringWriter stackTrace = new StringWriter();
exception.printStackTrace(new PrintWriter(stackTrace));
//You will get call back here when app crashes.
// from RuntimeInit.crash()
Process.killProcess(Process.myPid());
System.exit(10);
}
private Context m_context;
}
How to use this class?
write this line in your activity onCreate() method
CrashReportHandler.attach(this);
There is method called Uncaught exception which is called just before force close dialog , you can write ur piece of code there .. Please check Using Global Exception Handling on android
Use CrashLytics for crash reporter free of cost and easy to implement
https://www.crashlytics.com/