問題
最近我在處理線上奔潰日志的時(shí)候發(fā)現(xiàn)一個由Activity中onResume方法觸發(fā)的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)
可見當(dāng)發(fā)生ActivityRecord not found
時(shí)夫否,isTopOfTask()
方法里拋出來了一個IllegalArgumentException
異常局待。引起ActivityRecord not found
的原因有多種燎猛,這里不太好針對ActivityRecord not found
進(jìn)行處理重绷,所以我想怎么來屏蔽這個異常昭卓,讓程序不奔潰候醒。
分析
首先我們來看看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系統(tǒng)上的onResume方法里才會調(diào)用這個isTopOfTask方法敌土,所以這個問題只有這幾個版本有返干。
網(wǎng)上有一種做法是直接try-catch
onResume
方法血淌,然后通過反射將mCalled
修改為true。由于這個問題不好復(fù)現(xiàn)躺坟,我沒有去驗(yàn)證這個方法的可行性瞳氓,但是這種簡單粗暴的方法看上去其實(shí)是有問題的,因?yàn)?code>isTopOfTask()拋出異常后店诗,mActivityTransitionState.onResume()
方法里的邏輯就沒有執(zhí)行捧弃。
我們分析下isTopOfTask()
方法擦囊,發(fā)現(xiàn)這個方法try-catch了一個RemoteException
異常瞬场,當(dāng)發(fā)生這個異常的時(shí)候?qū)⒎祷豧alse贯被,后面的流程能正常執(zhí)行彤灶,不會影響Activity,于是我們想這里可不可以將IllegalArgumentException
異常也try-catch一下吶诵姜,但是發(fā)現(xiàn)這是一個私有方法棚唆,無法重寫瑟俭,所以只能繼續(xù)往下追蹤摆寄。
來看看ActivityManagerNative.getDefault()
的實(shí)現(xiàn):
在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();
}
...
}
繼續(xù)看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;
}
};
...
}
我們發(fā)現(xiàn)getDefault()
方法返回的是一個IActivityManager
對象矩肩,所以實(shí)際上執(zhí)行的是IActivityManager
里的isTopOfTask()
方法黍檩。而IActivityManager
是一個單例刽酱,在7.0和7.1上這個單例對象在ActivityManagerNative
類里面的瞧捌,而從8.0開始放到了ActivityManager
里姐呐。而且通過上面的final IActivityManager am = IActivityManager.Stub.asInterface(b);
這句還知道了IActivityManager
其實(shí)是一個接口(懶得繼續(xù)翻源碼了^_^)头谜。
繼續(xù)看這個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;
}
}
}
到這里我們就可以想到使用反射和動態(tài)代理就可以對IActivityManager
里的方法進(jìn)行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來進(jìn)行反射儒老,需要這樣處理:
// 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()))
}
}
第四步:用動態(tài)代理替換原來的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
}
// 第三步:使用動態(tài)代理替換原來的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()))
}
}
}
最后在Application的onCreate()
方法里調(diào)用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住了船侧,這樣當(dāng)再次發(fā)生ActivityRecord not found
問題時(shí)就不會再奔潰拋出IllegalArgumentException
了吉挣。而且從日志可以看出深滚,以后我們想要處理其他方法時(shí)也可以這樣處理旭从。
后話
當(dāng)然漾肮,我們這里其實(shí)是沒有從本質(zhì)上解決這個ActivityRecord not found
問題的路星,這樣處理了即便這里不奔潰吊圾,也沒法保證其他地方不會因?yàn)?code>ActivityRecord not found而不奔潰。這里更多的只是提供一種解決問題的思路蝇裤。