DeadObjectException in GMS::LocationClient (Android)

跟風遠走 提交于 2019-12-19 04:06:35

问题


On Android we have a class that wraps a LocationClient object from GMS (Google Mobile Services). (Note that LocationClient implements com.google.android.gms.common.GooglePlayServicesClient).

Unfortunately the LocationClient object has a habit of throwing DeadObjectExceptions (e.g. when we invoke locationClient.getLastLocation()), which we detect through several of our logging mechanisms. What's weird, however, is that LocationClient isn't documented as throwing DeadObjectExceptions, and furthermore I'm only able to catch said DeadObjectExceptions ~ 1/40th of the time they occur o_0. We have no repro for this issue and I've personally never seen it, however it occurs for a large number of our users.

Other notes:

[a] what is the line "Caused by: java.lang.IllegalStateException: android.os.DeadObjectException" about? Those two Exceptions types do not have an ancestor-descendant relationship

[b] I posted to the Android forum, but of course they rejected my post as 'wrong forum,' and there's no GMS forum so I'm totally out of luck.

In summary, the question is: GMS is triggering this oddly uncatchable exception, so what's up with that and what can I do?

Here's a stack trace:
com.myapp.android.service.AsyncExecutionException
     at com.myapp.android.service.AsyncService$ExceptionThrower.run(MyApp:120)
     at android.os.Handler.handleCallback(Handler.java:615)
     at android.os.Handler.dispatchMessage(Handler.java:92)
     at android.os.Looper.loop(Looper.java:137)
     at android.app.ActivityThread.main(ActivityThread.java:4794)
     at java.lang.reflect.Method.invokeNative(Method.java)
     at java.lang.reflect.Method.invoke(Method.java:511)
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:556)
     at dalvik.system.NativeStart.main(NativeStart.java)
Caused by: java.lang.IllegalStateException: android.os.DeadObjectException
     at com.google.android.gms.internal.ey.getLastLocation()
     at com.google.android.gms.internal.ez.getLastLocation()
     at com.google.android.gms.location.LocationClient.getLastLocation()
     ***at com.myapp.GoogleLocationProvider.getLastLocation(MyApp:92)***
     at com.myapp.LocationProducer.getLocation(MyApp:183)
     at com.myapp.LocationProducer.getLocationHeader(MyApp:194)
     at com.myapp.API.onExecute(MyApp:344)
     ...
     at java.lang.Thread.run(Thread.java:856)
Caused by: android.os.DeadObjectException
     at android.os.BinderProxy.transact(Binder.java)
     at com.google.android.gms.internal.ex$a$a.a()
     at com.google.android.gms.internal.ey.getLastLocation()
     at com.google.android.gms.internal.ez.getLastLocation()
     ***at com.google.android.gms.location.LocationClient.getLastLocation()***
     at com.myapp.GoogleLocationProvider.getLastLocation(MyApp:92)
     at com.myapp.LocationProducer.getLocation(MyApp:183)
     at com.myapp.LocationProducer.getLocationHeader(MyApp:194)
     ...
     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
     at java.util.concurrent.FutureTask.run(FutureTask.java:137)
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
     at java.lang.Thread.run(Thread.java:856)

------------ ADDENDUM ------------- Here is our actual code. You'll notice we always check whether mLocationClient.isConnected() beforehand, so that's not the issue. It's possible we're getting extremely unlucky and mLocationObject dies between invoking isOnConnected() and getLastLocation(), however that seems improbable to me. I suppose I can start to log before, between, and after the calls and find out.

LocationClient mLocationClient; // populated somewhere

public Location getLastLocation() {
    if (!mLocationClient.isConnected()) {
        return null;
    }
    Location location = null;
    try {
        location = mLocationClient.getLastLocation();
    } catch (Exception e) {
        if (!handleDeadObjectException(e)) {
            throw e;
        }
    }
    return location;
}

// logs, attempts to handle depending on user configuration
private boolean handleDeadObjectException(Exception e);

回答1:


From the documentation DeadObjectException:

The object you are calling has died, because its hosting process no longer exists.

Meaning, you are trying to reach an object in a different process that is not available anymore. For example, if you bind to a service that runs in a different process (i.e. Google Mobile Services) the IBinder you use is a local object that "represents" an object in the remote process. When the remote object is not available any more, and you are trying to use the local IBinder object, you will get the DeadObjectException.

So...

a] what is the line "Caused by: java.lang.IllegalStateException: android.os.DeadObjectException" about? Those two Exceptions types do not have an ancestor-descendant relationship

The two exceptions are not connected in any way. The IllegalStateException is the actual exception and the DeadObjectException is the root exception.

Since gms.location.LocationClient.getLastLocation() does not want to declare throw elements that expose inner implementations - working with binders and such - it simply don't. But when an exception such as DeadObjectException happens it still wants to throw and so it uses a runtime exception IllegalStateException (which doesn't need throw declaration).

[b] I posted to the Android forum, but of course they rejected my post as 'wrong forum,' and there's no GMS forum so I'm totally out of luck.

:(

In summary, the question is: GMS is triggering this oddly uncatchable exception, so what's up with that and what can I do?

When working with the GMS LocationClient you need to check if LocationClient.isConnected() before interacting with the client. Note that sometimes LocationClient.isConnected() will return true but following invocation to LocationClient.getLastLocation() might still throw java.lang.IllegalStateException: android.os.DeadObjectException and the reason for that is threading issues and race conditions where the client was connected when you checked but then connection got lost before your actual action.

What you should do is a) Check if client is connected

if ( mLocationClient != null && mLocationClient.isConnected() ) {   
    mLocationClient.getLastLocation();
}

b) Catch the IllegalStateException (and not the DeadObjectException)

if ( mLocationClient != null && mLocationClient.isConnected() ) {   
    try {
        mLocationClient.getLastLocation();
    } catch (IllegalStateException ex) {
        // This will catch the exception, handle as needed
    }
}


来源:https://stackoverflow.com/questions/24288685/deadobjectexception-in-gmslocationclient-android

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!