应用运行正常但日志报Bad activity token错误,该如何修复?
这个问题我在维护Android老项目和处理内存泄漏问题时碰到过好几次,咱们先搞清楚错误的本质,再一步步解决:
错误本质
你看到的Bad activity token和ClassCastException: android.os.BinderProxy cannot be cast to com.android.server.am.ActivityRecord$Token,本质是你的代码在使用一个已经失效的Activity Token。
当系统因为内存不足、用户手动关闭等原因销毁Activity后,原来的ActivityRecord$Token(系统用来标识Activity的内部对象)会被回收,此时你代码里持有的引用会变成一个BinderProxy(Binder跨进程通信的空壳对象),当你拿着这个无效的Proxy去调用ActivityManagerService(AMS)的相关方法时,就会触发强转失败的异常。
从日志调用链也能看出来:ActivityRecord.forTokenLocked方法尝试把传入的Binder对象转成合法的Token,但拿到的是个Proxy,直接抛了异常。
具体修复方案
1. 清理Activity销毁后的异步/延迟任务
最常见的场景是:你在Activity里启动了AsyncTask、Handler延迟任务、后台线程,这些任务在Activity销毁后才执行,此时去调用需要Activity Token的方法(比如startActivityForResult、showDialog、或者某些依赖Activity的第三方SDK方法)就会报错。
解决方法:
- 在Activity的
onDestroy方法里,取消所有未完成的异步任务和Handler消息:@Override protected void onDestroy() { super.onDestroy(); // 清空Handler所有未执行的任务 mHandler.removeCallbacksAndMessages(null); // 如果有AsyncTask,调用cancel(true)终止任务 if (mAsyncTask != null && mAsyncTask.getStatus() != AsyncTask.Status.FINISHED) { mAsyncTask.cancel(true); } } - 优先用
LiveData替代Handler/AsyncTask,它会自动感知生命周期,Activity销毁后自动取消订阅。
2. 用弱引用持有Activity,避免内存泄漏
如果你需要在非Activity类里持有Activity引用,一定要用WeakReference,这样系统回收Activity时,不会因为强引用导致内存泄漏,也能避免使用无效的Token。
示例:
public class SomeBackgroundTask { private WeakReference<Activity> mActivityRef; public SomeBackgroundTask(Activity activity) { mActivityRef = new WeakReference<>(activity); } public void doSomething() { Activity activity = mActivityRef.get(); // 先检查Activity是否存活 if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) { // 这里执行安全的操作 activity.startActivity(new Intent(activity, TargetActivity.class)); } } }
3. 禁止跨进程传递Activity Token
如果你的App有跨进程通信(比如和Service、其他App交互),千万不要直接把Activity的getToken()结果传递过去。跨进程传递后,Token会变成BinderProxy,一旦原Activity销毁,这个Proxy就完全无效了。
解决方法:
- 跨进程时改用其他标识(比如任务ID、自定义的回调接口),或者确保操作只在原Activity存活时执行。
4. 排查第三方SDK的锅
有些第三方推送、统计、广告SDK可能会在后台偷偷持有Activity的引用,导致Token失效。可以暂时禁用这些SDK,看错误是否消失,定位到具体SDK后:
- 升级到该SDK的最新版本(通常开发者会修复这类生命周期问题)
- 查看SDK文档,是否有正确的生命周期集成方式(比如需要在
onDestroy里调用SDK的销毁方法)
额外建议
如果以上方法都没解决问题,可以注册Application.ActivityLifecycleCallbacks全局监听所有Activity的生命周期,在onActivityDestroyed回调里,清理所有和该Activity相关的全局引用或任务,确保没有漏网之鱼。
内容的提问来源于stack exchange,提问作者1-Mar

