问题
最近我在处理线上奔溃日志的时候发现一个由Activity中onResume方法触发的ActivityRecord not found异常,具体信息如下:
java.lang.RuntimeException: Unable to resume activity {com.xxx.xx/com.xxx.xxx.RegisterActivity}: java.lang.IllegalArgumentException
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4025)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4057)
at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:51)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:145)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1960)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7097)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:975)
Caused by: java.lang.IllegalArgumentException
at android.os.Parcel.createException(Parcel.java:1970)
at android.os.Parcel.readException(Parcel.java:1934)
at android.os.Parcel.readException(Parcel.java:1884)
at android.app.IActivityManager$Stub$Proxy.isTopOfTask(IActivityManager.java:7845)
at android.app.Activity.isTopOfTask(Activity.java:6551)
at android.app.Activity.onResume(Activity.java:1404)
at androidx.fragment.app.FragmentActivity.onResume(ProGuard:1)
at com.xxx.xxx.BaseMVPActivity.onResume(ProGuard:1)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1416)
at android.app.Activity.performResume(Activity.java:7585)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4017)
... 11 more
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.am.ActivityManagerService.isTopOfTask(ActivityManagerService.java:18293)
at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2058)
at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:4174)
at android.os.Binder.execTransact(Binder.java:739)
可见当发生ActivityRecord not found
时,isTopOfTask()
方法里抛出来了一个IllegalArgumentException
异常。引起ActivityRecord not found
的原因有多种,这里不太好针对ActivityRecord not found
进行处理,所以我想怎么来屏蔽这个异常,让程序不奔溃。
分析
首先我们来看看Activity
的源码(API 24):
...
@CallSuper
protected void onResume() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
getApplication().dispatchActivityResumed(this);
mActivityTransitionState.onResume(this, isTopOfTask());
mCalled = true;
}
...
private boolean isTopOfTask() {
if (mToken == null || mWindow == null) {
return false;
}
try {
return ActivityManagerNative.getDefault().isTopOfTask(getActivityToken());
} catch (RemoteException e) {
return false;
}
}
注意,在7.0到9.0系统上的onResume方法里才会调用这个isTopOfTask方法,所以这个问题只有这几个版本有。
网上有一种做法是直接try-catch
onResume
方法,然后通过反射将mCalled
修改为true。由于这个问题不好复现,我没有去验证这个方法的可行性,但是这种简单粗暴的方法看上去其实是有问题的,因为isTopOfTask()
抛出异常后,mActivityTransitionState.onResume()
方法里的逻辑就没有执行。
我们分析下isTopOfTask()
方法,发现这个方法try-catch了一个RemoteException
异常,当发生这个异常的时候将返回false,后面的流程能正常执行,不会影响Activity,于是我们想这里可不可以将IllegalArgumentException
异常也try-catch一下呐,但是发现这是一个私有方法,无法重写,所以只能继续往下追踪。
来看看ActivityManagerNative.getDefault()
的实现:
在API 24、25上:
public abstract class ActivityManagerNative {
...
static public IActivityManager getDefault() {
return gDefault.get();
}
...
private static final Singleton gDefault = new Singleton() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
...
}
在API 26+上:
public abstract class ActivityManagerNative {
...
static public IActivityManager getDefault() {
return ActivityManager.getService();
}
...
}
继续看ActivityManager.getService()
:
public class ActivityManager {
...
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
...
private static final Singleton IActivityManagerSingleton =
new Singleton() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
...
}
我们发现getDefault()
方法返回的是一个IActivityManager
对象,所以实际上执行的是IActivityManager
里的isTopOfTask()
方法。而IActivityManager
是一个单例,在7.0和7.1上这个单例对象在ActivityManagerNative
类里面的,而从8.0开始放到了ActivityManager
里。而且通过上面的final IActivityManager am = IActivityManager.Stub.asInterface(b);
这句还知道了IActivityManager
其实是一个接口(懒得继续翻源码了^_^)。
继续看这个Singleton
:
public abstract class Singleton {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
到这里我们就可以想到使用反射和动态代理就可以对IActivityManager
里的方法进行Hook,然后就可以处理这个IllegalArgumentException
异常了。
处理方法
第一步:通过反射拿到Singleton
对象:
// Singleton是一个隐藏类,无法直接访问,所以这里通过Class.forName来加载,备用
val singletonCls = Class.forName("android.util.Singleton")
在API 24、25上:
private fun getIActivityManagerSingletonInstanceN(singletonCls: Class<*>): Any? {
// 找到ActivityManagerNative里的gDefault这个常量
// 这里通过变量的类型对比找出gDefault这个常量,
// 没有通过名字来查找,防止名字有变法
// 这里也可以直接使用activityManagerNativeCls.getDeclaredField("gDefault")来获取
val activityManagerNativeCls = Class.forName("android.app.ActivityManagerNative")
var iActivityManagerSingleton: Any? = null
for (field in activityManagerNativeCls.declaredFields) {
if (field.type == singletonCls) {
field.isAccessible = true
iActivityManagerSingleton = field.get(null)
break
}
}
if (iActivityManagerSingleton == null) {
Logger.w(TAG, "Not found IActivityManager singleton field in class ActivityManagerNative.")
}
return iActivityManagerSingleton
}
在API 26+上:
private fun getIActivityManagerSingletonInstance(singletonCls: Class<*>): Any? {
// 找到ActivityManager里的IActivityManagerSingleton常量
// 这里通过变量的类型对比找出IActivityManagerSingleton这个常量,
// 没有通过名字来查找,防止名字有变法
// 这里也可以直接使用ActivityManager::class.java.getDeclaredField("IActivityManagerSingleton")来获取
var iActivityManagerSingleton: Any? = null
for (field in ActivityManager::class.java.declaredFields) {
if (field.type == singletonCls) {
field.isAccessible = true
iActivityManagerSingleton = field.get(null)
break
}
}
if (iActivityManagerSingleton == null) {
Logger.w(TAG, "Not found IActivityManager singleton field in class ActivityManager.")
}
return iActivityManagerSingleton
}
第二步:拿到Singleton
里的mInstance
字段:
// 注意这里的iActivityManagerSingleton是Singleton的一个匿名子类
// 如果要用iActivityManagerSingleton来进行反射,需要这样处理:
// iActivityManagerSingleton::class.java.superclass.getDeclaredField("mInstance")
val instanceField = singletonCls.getDeclaredField("mInstance")
instanceField.isAccessible = true
val iActivityManager = instanceField.get(iActivityManagerSingleton)
第三步:把代理类写出来
(本方法的处理核心就在这里)
private class IActivityManagerProxy(private val instance: Any): InvocationHandler {
override fun invoke(proxy: Any?, method: Method, args: Array?): Any? {
Logger.i(TAG, "invoke: ${method.name}()")
if (method.name == "isTopOfTask") {
return try {
val result = method.invoke(instance, *(args ?: emptyArray())) as Boolean
Logger.i(TAG, "isTopOfTask() invoke success")
result
} catch (e: Exception) {
Logger.w(TAG, "isTopOfTask() invoke exception: $e")
false
}
}
return method.invoke(instance, *(args ?: emptyArray()))
}
}
第四步:用动态代理替换原来的IActivityManager对象
val proxy = IActivityManagerProxy(iActivityManager)
val iActivityManagerCls = Class.forName("android.app.IActivityManager")
val iActivityManageProxy = Proxy.newProxyInstance(iActivityManagerCls.classLoader, arrayOf(iActivityManagerCls), proxy)
instanceField.set(iActivityManagerSingleton, iActivityManageProxy)
完整代码如下:
object IActivityManagerHook {
private const val TAG = "IActivityManagerHook"
@SuppressLint("PrivateApi")
fun iActivityManagerHook() {
if (Build.VERSION.SDK_INT < 24 || Build.VERSION.SDK_INT > 28) {
return
}
Logger.i(TAG, "IActivityManager hook ...")
try {
val singletonCls = Class.forName("android.util.Singleton")
// 第一步:通过反射拿到Singleton对象
val iActivityManagerSingleton = if (Build.VERSION.SDK_INT <= 25) {
getIActivityManagerSingletonInstanceN(singletonCls)
} else {
getIActivityManagerSingletonInstance(singletonCls)
} ?: return
// 第二步:找出Singleton里的mInstance变量
val instanceField = singletonCls.getDeclaredField("mInstance")
instanceField.isAccessible = true
val iActivityManager = instanceField.get(iActivityManagerSingleton)
if (iActivityManager == null) {
Logger.w(TAG, "Not found IActivityManager instance.")
return
}
// 第三步:使用动态代理替换原来的IActivityManager对象
val proxy = IActivityManagerProxy(iActivityManager)
val iActivityManagerCls = Class.forName("android.app.IActivityManager")
val iActivityManageProxy = Proxy.newProxyInstance(iActivityManagerCls.classLoader, arrayOf(iActivityManagerCls), proxy)
instanceField.set(iActivityManagerSingleton, iActivityManageProxy)
Logger.i(TAG, "IActivityManager hook success.")
} catch (e: Throwable) {
Logger.w(TAG, "IActivityManager hook fail: $e")
}
}
@SuppressLint("PrivateApi")
private fun getIActivityManagerSingletonInstanceN(singletonCls: Class<*>): Any? {
// 找到ActivityManagerNative里的gDefault这个常量
// 这里通过变量的类型对比找出gDefault这个常量,
// 没有通过名字来查找,防止名字有变法
// 这里也可以直接使用activityManagerNativeCls.getDeclaredField("gDefault")来获取
val activityManagerNativeCls = Class.forName("android.app.ActivityManagerNative")
var iActivityManagerSingleton: Any? = null
for (field in activityManagerNativeCls.declaredFields) {
if (field.type == singletonCls) {
field.isAccessible = true
iActivityManagerSingleton = field.get(null)
break
}
}
if (iActivityManagerSingleton == null) {
Logger.w(TAG, "Not found IActivityManager singleton field in class ActivityManagerNative.")
}
return iActivityManagerSingleton
}
private fun getIActivityManagerSingletonInstance(singletonCls: Class<*>): Any? {
// 找到ActivityManager里的IActivityManagerSingleton常量
// 这里通过变量的类型对比找出IActivityManagerSingleton这个常量,
// 没有通过名字来查找,防止名字有变法
// 这里也可以直接使用ActivityManager::class.java.getDeclaredField("IActivityManagerSingleton")来获取
var iActivityManagerSingleton: Any? = null
for (field in ActivityManager::class.java.declaredFields) {
if (field.type == singletonCls) {
field.isAccessible = true
iActivityManagerSingleton = field.get(null)
break
}
}
if (iActivityManagerSingleton == null) {
Logger.w(TAG, "Not found IActivityManager singleton field in class ActivityManager.")
}
return iActivityManagerSingleton
}
private class IActivityManagerProxy(private val instance: Any): InvocationHandler {
override fun invoke(proxy: Any?, method: Method, args: Array?): Any? {
Logger.i(TAG, "invoke: ${method.name}()")
if (method.name == "isTopOfTask") {
return try {
val result = method.invoke(instance, *(args ?: emptyArray())) as Boolean
Logger.i(TAG, "isTopOfTask() invoke success")
result
} catch (e: Exception) {
Logger.w(TAG, "isTopOfTask() invoke exception: $e")
false
}
}
return method.invoke(instance, *(args ?: emptyArray()))
}
}
}
最后在Application的onCreate()
方法里调用IActivityManagerHook.iActivityManagerHook()
即可完成全局的IActivityManager
Hook了。
启动下看看:
1588489555.734 I/[2:iActivityManagerHook(IActivityManagerHook.kt:55):IActivityManagerHook]: IActivityManager hook ...
1588489555.757 I/[2:iActivityManagerHook(IActivityManagerHook.kt:90):IActivityManagerHook]: IActivityManager hook success.
1588489555.784 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: registerReceiver()
1588489555.796 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: getActivityDisplayId()
1588489555.876 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: getContentProvider()
1588489555.881 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: setTaskDescription()
1588489555.901 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: checkPermission()
1588489555.902 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: checkPermission()
1588489555.978 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: getActivityStackId()
1588489556.023 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: getActivityOptions()
1588489556.024 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: getActivityOptions()
1588489556.047 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: reportSizeConfigurations()
1588489556.049 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: isTopOfTask()
1588489556.049 I/[2:invoke(IActivityManagerHook.kt:102):IActivityManagerHook]: isTopOfTask() invoke success
1588489556.061 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: setRenderThread()
1588489556.068 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: activityResumed()
1588489556.255 I/[2:invoke(IActivityManagerHook.kt:98):IActivityManagerHook]: invoke: activityIdle()
完美,isTopOfTask
成功Hook住了,这样当再次发生ActivityRecord not found
问题时就不会再奔溃抛出IllegalArgumentException
了。而且从日志可以看出,以后我们想要处理其他方法时也可以这样处理。
后话
当然,我们这里其实是没有从本质上解决这个ActivityRecord not found
问题的,这样处理了即便这里不奔溃,也没法保证其他地方不会因为ActivityRecord not found
而不奔溃。这里更多的只是提供一种解决问题的思路。