GMS::LocationClient(Android)中的DeadObjectException

3
在Android中,我们有一个类来包装GMS(Google Mobile Services)的LocationClient对象。(请注意,LocationClient实现了com.google.android.gms.common.GooglePlayServicesClient。)不幸的是,LocationClient对象有抛出DeadObjectExceptions的习惯(例如当我们调用locationClient.getLastLocation()时),我们通过几个日志机制检测到这一点。然而,奇怪的是,LocationClient没有被记录为抛出DeadObjectExceptions,而且我只能 ~ 1/40的时间捕获到这些DeadObjectExceptions。我们无法重现此问题,我个人从未见过它,但它发生在大量用户身上。
其他注意事项:
[a] "Caused by: java.lang.IllegalStateException: android.os.DeadObjectException"这行是什么意思?这两种异常类型没有祖先后代关系
[b] 我在Android论坛上发布了帖子,但他们将我的帖子拒绝为“错误的论坛”,而且没有GMS论坛,所以我完全没有运气。
总之,问题是:GMS触发了这种奇怪的无法捕获的异常,那么这是怎么回事,我该怎么做?
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)

------------ 补充 ------------- 以下是我们实际的代码。您会注意到我们始终在调用getLastLocation()之前检查mLocationClient.isConnected(),因此这不是问题所在。可能我们非常不幸地在调用isOnConnected()和getLastLocation()之间期间丢失了mLocationObject,但我觉得这似乎是不太可能的。我想我可以开始记录调用之前、之间和之后的日志并找出原因。

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);

你解决问题了吗? - Nicks
嗨,我遇到了同样的问题。你找到解决办法了吗? - Mr Mike
@MrMike 不是的。我已经离开了那家公司,在另一家公司短暂地看到过这个问题,但显然在2015年中期我们就解决了这个问题。从那时起,我就避免使用地理编码了。 - boboon
感谢您的回复。 - Mr Mike
1个回答

9
DeadObjectException文档中可以得知:
对象已死,因为其宿主进程不再存在。
这意味着您正在尝试访问不再可用的不同进程中的对象。例如,如果绑定到在不同进程中运行的服务(即Google移动服务),则使用的IBinder是一个本地对象,它“表示”远程进程中的对象。当远程对象不再可用,并且您正在尝试使用本地IBinder对象时,将会出现DeadObjectException
所以...
"Caused by: java.lang.IllegalStateException: android.os.DeadObjectException"这一行是什么意思?这两种异常类型没有祖先-后代关系
这两个异常没有任何联系。 IllegalStateException是实际异常,而DeadObjectException是根异常。
由于gms.location.LocationClient.getLastLocation()不想声明抛出公开内部实现的元素(如使用binder等),因此它不会声明。但是,当发生DeadObjectException等异常时,它仍然希望抛出异常,因此使用运行时异常IllegalStateException(不需要抛出声明)。
我发布到了Android论坛,但当然他们拒绝了我的帖子,说是“错误的论坛”,而且没有GMS论坛,所以我完全没有帮助。
:(
总之,问题是:GMS触发了这个奇怪的无法捕获的异常,那么情况如何,我该怎么做?
使用GMS LocationClient时,您需要在与客户端交互之前检查LocationClient.isConnected()。请注意,有时LocationClient.isConnected()会返回true,但是对LocationClient.getLastLocation()的后续调用仍可能抛出java.lang.IllegalStateException: android.os.DeadObjectException,原因是线程问题和竞态条件,其中客户端在您检查时已连接,但然后连接在您的实际操作之前失去了连接。
您应该做的是a)检查客户端是否已连接
if ( mLocationClient != null && mLocationClient.isConnected() ) {   
    mLocationClient.getLastLocation();
}
任务b: 捕捉 IllegalStateException 异常(而非 DeadObjectException
if ( mLocationClient != null && mLocationClient.isConnected() ) {   
    try {
        mLocationClient.getLastLocation();
    } catch (IllegalStateException ex) {
        // This will catch the exception, handle as needed
    }
}

我在原帖下面添加了评论(在这里写太长了)。另外请注意,handleDeadObjectException(Exception e) 处理所有类型的异常,并且一直有明确的逻辑来记录和小心处理 IllegalStateExceptions 和 DeadObjectExceptions,因此应该可以解决您的问题(b)。我们开始认为我们需要以合理的速度创建/销毁 LocationClient 对象 - 不要让它们保持太长时间,也不要在用户在活动之间切换时删除它们得太快。转换为绑定服务可能会帮助我们做到这一点。 - boboon
首先,在任何情况下,捕获异常都是您应该能够做到的。因此,实际崩溃的问题不应该存在。其次,您是否创建了多个位置客户端?也许您可以尝试仅管理一个LocationClient... - Eyal Biran
没错,我无法捕获异常是这个问题的最不真实之处。我们创建一个LocationClient作为单例并永远保持它的活性,但我认为GMS在某种内部情况下让它/某些东西被回收,因此出现了DOE。我的想法转向一种情况,在该情况下我们仍然在任何给定时间内只创建一个位置客户端,但我们确保偶尔断开连接。 - boboon
对我来说,这听起来不像一个万无一失的解决方案 - 它可能会减少问题,但不能修复它。确定一个位置客户端并找出异常的问题是我开始的地方。如果没有看到实际代码,要找出问题将非常困难。应该能行。 - Eyal Biran
1
顺便说一下,我们与一些谷歌代表交谈过,这可能是由于GooglePlayServices内部的竞争条件引起的。 - boboon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接