Activity的插件化解決的一個根本性問題就是插件中的Activity并沒有在宿主的AndroidManifest.xml中進行注冊,也就是說我們需要啟動一個未注冊的Activity,因此需要對Activity的啟動過程有個了解,Android各個版本源碼啟動流程略有不同,但大致流程一樣,這里給出9.0和10.0實現(xiàn)的二種方式。
1.繼承Instrumentation的方式,這種方式比較簡單在9.0和10.0都能用
- 1.重寫一個類繼承Instrumentation
public class InstrumentationProxy extends Instrumentation {
private Instrumentation mInstrumentation;
public InstrumentationProxy(Instrumentation instrumentation) {
mInstrumentation = instrumentation;
}
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
List<ResolveInfo> resolveInfo = who.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_ALL);
LogUtil.logE("execStartActivityexecStartActivity");
//判斷啟動的插件Activity是否在AndroidManifest.xml中注冊過
if (resolveInfo.size() == 0) {
//保存目標(biāo)插件
intent.putExtra(Const.INTENT_DATA, intent.getComponent().getClassName());
//設(shè)置為占坑Activity
intent.setClassName(who, "com.example.client.hook.ProxyActivity");
}
try {
Method execStartActivity = getClass().getSuperclass().getDeclaredMethod("execStartActivity",
Context.class, IBinder.class, IBinder.class, Activity.class,
Intent.class, int.class, Bundle.class);
return (ActivityResult) execStartActivity.invoke(mInstrumentation, who, contextThread, token, target, intent, requestCode, options);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
@Override
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
String data = intent.getStringExtra(Const.INTENT_DATA);
if (!TextUtils.isEmpty(data)) {
return super.newActivity(cl, data, intent);
}
return super.newActivity(cl, className, intent);
}
}
- 2.在Application中onCreate方法中去替換我們自己的Instrumentation
private void hook1() {
try {
Class<?> clazz = Class.forName("android.app.ActivityThread");
//獲取ActivityThread
Field sCurrentActivityThreadFiled = clazz.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThreadFiled.setAccessible(true);
Object sCurrentActivityThread = sCurrentActivityThreadFiled.get(null);
//獲取instrumentation
Field instrumentationFiled = clazz.getDeclaredField("mInstrumentation");
instrumentationFiled.setAccessible(true);
Instrumentation instrumentation = (Instrumentation) instrumentationFiled.get(sCurrentActivityThread);
//設(shè)置自己的instrumentation
InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation);
instrumentationFiled.set(sCurrentActivityThread,instrumentationProxy);
} catch (Exception e) {
}
}
<application
android:name=".hook.App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--代理的Activity-->
<activity android:name=".ProxyActivity">
</activity>
</application>
2.使用動態(tài)代理的方式,攔截啟動activity的方法替換intent
大體的時序圖

a.jpg
private void hook() {
try {
//1.獲取IActivityManagerSingleton
Object IActivityManagerSingleton = getSingletonByVersion();
//2.獲取mInstance
Class<?> singletonclazz = Class.forName("android.util.Singleton");
Field mInstanceField = singletonclazz.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
if (Build.VERSION.SDK_INT == 29) {
//Q上需要動態(tài)執(zhí)行create方法
Method getMethod = singletonclazz.getMethod("get");
getMethod.setAccessible(true);
getMethod.invoke(IActivityManagerSingleton);
}
Object mInstance = mInstanceField.get(IActivityManagerSingleton);
Logutils.LogE(mInstance+"");
//3.動態(tài)代理設(shè)置自己的mInstance
Object proxyInstance = Proxy.newProxyInstance(getClassLoader(),
mInstance.getClass().getInterfaces(),
new MyInvocationHandler(mInstance));
//4.設(shè)置代理的proxyInstance
mInstanceField.set(IActivityManagerSingleton, proxyInstance);
//5.獲取ActivityThread實例
Class<?> ActivityThreadclass = Class.forName("android.app.ActivityThread");
Field sCurrentActivityThreadFiled = ActivityThreadclass.getDeclaredField(
"sCurrentActivityThread");
sCurrentActivityThreadFiled.setAccessible(true);
Object sCurrentActivityThread = sCurrentActivityThreadFiled.get(null);
//6.獲取mH實例
Field mHFiled = ActivityThreadclass.getDeclaredField("mH");
mHFiled.setAccessible(true);
Object mH = mHFiled.get(sCurrentActivityThread);
Field mCallbackFiled = Handler.class.getDeclaredField("mCallback");
mCallbackFiled.setAccessible(true);
//7.設(shè)置進入我們自己的Callback
mCallbackFiled.set(mH, new MyHandlerCallback((Handler) mH));
} catch (Exception e) {
e.printStackTrace();
Log.e("TAG------", e.toString());
}
}
需要注意的是10.0在獲取mInstance的時候必須動態(tài)執(zhí)行下create方法,否則拿不到mInstance 的實例.
private Object getSingletonByVersion() {
try {
if (Build.VERSION.SDK_INT == 28) {
Class<?> clazz = Class.forName("android.app.ActivityManager");
Field field = clazz.getDeclaredField("IActivityManagerSingleton");
field.setAccessible(true);
return field.get(null);
} else if (Build.VERSION.SDK_INT == 29) {
Class<?> clazz = Class.forName("android.app.ActivityTaskManager");
Field field = clazz.getDeclaredField("IActivityTaskManagerSingleton");
field.setAccessible(true);
return field.get(null);
}
} catch (Exception e) {
e.printStackTrace();
Logutils.LogE(e.toString());
}
return null;
}
10.0和9.0獲取SingleTon的方法也略有不同,10.0把幾個類修改了下,但大致流程相同
private class MyInvocationHandler implements InvocationHandler {
private Object mIActivityManager;
public MyInvocationHandler(Object IActivityManager) {
mIActivityManager = IActivityManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("startActivity")) {
for (Object arg : args) {
if (arg instanceof Intent) {
Intent intent = (Intent) arg;
//把插件的Activity類名傳入
intent.putExtra(INTENT_DATA, intent.getComponent().getClassName());
//設(shè)置代理的activity
intent.setClass(getApplicationContext(), ProxyActivity.class);
}
}
}
return method.invoke(mIActivityManager, args);
}
}
使用動態(tài)讓代理攔截startActivity方法替換intent里面的class繞過AndroidManifest的檢測,并把我們設(shè)置的class當(dāng)作參數(shù)傳遞。
private class MyHandlerCallback implements Handler.Callback {
private Handler mHandler;
public MyHandlerCallback(Handler handler) {
mHandler = handler;
}
@Override
public boolean handleMessage(@NonNull Message msg) {
if (msg.what == 159) {
Object obj = msg.obj;
try {
//獲取ClientTransaction中的mActivityCallbacks集合
Class<?> clazz = Class.forName("android.app.servertransaction" +
".ClientTransaction");
Field mActivityCallbacksFiled = clazz.getDeclaredField("mActivityCallbacks");
mActivityCallbacksFiled.setAccessible(true);
List list = (List) mActivityCallbacksFiled.get(obj);
if (list != null && list.size() > 0) {
//得到集合中的LaunchActivityItem
Object o = list.get(0);
//獲取LaunchActivityItem中的mIntent
Class<?> LaunchActivityItemClazz = Class.forName("android.app" +
".servertransaction.LaunchActivityItem");
Field mIntentFiled = LaunchActivityItemClazz.getDeclaredField("mIntent");
mIntentFiled.setAccessible(true);
Intent intent = (Intent) mIntentFiled.get(o);
//得到我們設(shè)置的class 替換進去
if (intent.getStringExtra(INTENT_DATA) != null) {
String className = intent.getStringExtra(INTENT_DATA);
intent.setClassName(getApplicationContext(), className);
}
}
} catch (Exception e) {
e.printStackTrace();
Logutils.LogE(e.toString());
}
}
mHandler.handleMessage(msg);
return true;
}
}
在ActivityThread回調(diào)的handlemessage方法中在創(chuàng)建activity時來替換我們原來設(shè)置的Activity,從intent中獲取我們設(shè)置的參數(shù), 有了上面的代碼后在Activity中可以正常運行但是在AppcompatActivity中還是報錯需要再次Hook
private void hook1() {
try {
Class<?> ActivityThreadclass = Class.forName("android.app.ActivityThread");
Field sCurrentActivityThreadFiled = ActivityThreadclass.getDeclaredField(
"sCurrentActivityThread");
sCurrentActivityThreadFiled.setAccessible(true);
Object sCurrentActivityThread = sCurrentActivityThreadFiled.get(null);
Field sPackageManagerFiled = ActivityThreadclass.getDeclaredField(
"sPackageManager");
sPackageManagerFiled.setAccessible(true);
Object sPackageManager = sPackageManagerFiled.get(null);
Object proxyInstance = Proxy.newProxyInstance(getClassLoader(),
sPackageManager.getClass().getInterfaces(),
new PackageManagerHandler(sPackageManager));
sPackageManagerFiled.set(sCurrentActivityThread, proxyInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
private class PackageManagerHandler implements InvocationHandler {
private Object IPackageManager;
public PackageManagerHandler(Object IPackageManager) {
this.IPackageManager = IPackageManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("getActivityInfo".equals(method.getName())) {
args[0] = new ComponentName(getPackageName(), ProxyActivity.class.getName());
}
return method.invoke(IPackageManager, args);
}
}
在getActivityInfo時候找不到我們的Activity,依然使用動態(tài)代理的方式替換我們代理的Activity。
看下最后效果

aa.gif