Activity中onResume方法触发的ActivityRecord not found异常分析
https://www.jianshu.com/p/0c63a8e5c1ad
再次基础上 写了个Java版供参考
问题
最近我在处理线上奔溃日志的时候发现一个由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<IActivityManager> gDefault = new Singleton<IActivityManager>() {
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<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@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<T> {
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<out Any>?): 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<out Any>?): 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()))
}
}
}
java代码
public class IActivityManagerHook {
private final static String TAG = "IActivityManagerHook";
public static void iActivityManagerHook() {
if (Build.VERSION.SDK_INT < 24 || Build.VERSION.SDK_INT > 28) {
return;
}
Log.i(TAG, "IActivityManager hook ...");
try {
Class<?> singletonCls = Class.forName("android.util.Singleton");
// 第一步:通过反射拿到Singleton对象
Object iActivityManagerSingleton = null;
if (Build.VERSION.SDK_INT <= 25) {
iActivityManagerSingleton = getIActivityManagerSingletonInstanceN(singletonCls);
} else {
iActivityManagerSingleton = getIActivityManagerSingletonInstance(singletonCls);
}
if (iActivityManagerSingleton == null) return;
// 第二步:找出Singleton里的mInstance变量
Field instanceField = singletonCls.getDeclaredField("mInstance");
instanceField.setAccessible(true);
Object iActivityManager = instanceField.get(iActivityManagerSingleton);
if (iActivityManager == null) {
Log.w(TAG, "Not found IActivityManager instance.");
return;
}
// 第三步:使用动态代理替换原来的IActivityManager对象
IActivityManagerProxy proxy = new IActivityManagerProxy(iActivityManager);
Class<?> iActivityManagerCls = Class.forName("android.app.IActivityManager");
Object iActivityManageProxy = Proxy.newProxyInstance(iActivityManagerCls.getClassLoader(), new Class[]{iActivityManagerCls}, proxy);
instanceField.set(iActivityManagerSingleton, iActivityManageProxy);
Log.i(TAG, "IActivityManager hook success.");
} catch (Throwable e) {
Log.e(TAG, e);
}
}
private static Object getIActivityManagerSingletonInstanceN(Class<?> singletonCls) throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
// 找到ActivityManagerNative里的gDefault这个常量
// 这里通过变量的类型对比找出gDefault这个常量,
// 没有通过名字来查找,防止名字有变法
// 这里也可以直接使用activityManagerNativeCls.getDeclaredField("gDefault")来获取
Class<?> activityManagerNativeCls = Class.forName("android.app.ActivityManagerNative");
Object iActivityManagerSingleton = null;
for (Field field : activityManagerNativeCls.getDeclaredFields()) {
if (field.getType() == singletonCls) {
field.setAccessible(true);
iActivityManagerSingleton = field.get(null);
break;
}
}
if (iActivityManagerSingleton == null) {
Log.w(TAG, "Not found IActivityManager singleton field in class ActivityManagerNative.");
}
return iActivityManagerSingleton;
}
private static Object getIActivityManagerSingletonInstance(Class<?> singletonCls) throws ClassNotFoundException, IllegalAccessException {
// 找到ActivityManager里的IActivityManagerSingleton常量
// 这里通过变量的类型对比找出IActivityManagerSingleton这个常量,
// 没有通过名字来查找,防止名字有变法
// 这里也可以直接使用ActivityManager::class.java.getDeclaredField("IActivityManagerSingleton")来获取
Object iActivityManagerSingleton = null;
for (Field field : ActivityManager.class.getDeclaredFields()) {
if (field.getType() == singletonCls) {
field.setAccessible(true);
iActivityManagerSingleton = field.get(null);
break;
}
}
if (iActivityManagerSingleton == null) {
Log.w(TAG, "Not found IActivityManager singleton field in class ActivityManagerNative.");
}
return iActivityManagerSingleton;
}
private static class IActivityManagerProxy implements InvocationHandler {
private Object instance;
public IActivityManagerProxy(Object instance) {
this.instance = instance;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.i(TAG, "invoke: " + method.getName() + "()");
if (method.getName() == "isTopOfTask") {
boolean invoke = false;
try {
invoke = (boolean) method.invoke(instance, args);
Log.i(TAG, "isTopOfTask() invoke success");
} catch (Exception e) {
Log.w(TAG, "isTopOfTask() invoke exception: ", e);
}
return invoke;
}
return method.invoke(instance, args);
}
}
}