涉及到的同系列往期文章:
- Android插件化原理(一)—— 插件类加载、类加载原理、(双亲委托机制)
- StartActivity原理(一)——Android9.0 pauseActivity原理
- StartActivity原理(二)——Android9.0 startActivity原理,Activity启动原理
- 再论Handler—Handler事件分发、Handler线程切换原理解析
本篇文章目录:
1 原理
1.1 涉及到的Framework原理
回顾第一篇Android插件化原理(一)—— 插件类加载、类加载原理、(双亲委托机制)两点:
类加载器
为什么要说ClassLoader呢,因为class 都是由ClassLoader加载的,应用的所有类都在类加载器中。
DexClassLoader
8.0之前提供给系统使用,8.0之后与PathClassLoader并无区别PathClassLoader
,8.0之前提供给开发者使用,用于加载应用class文件,例如CustomActivity、CustomeFragment,包括三方jar、apk、库的class;8.0之后与DexClassLoader并无区别;BootClassLoader
:用于加载Framework的class文件,例如Activity、Fragment
- 加载插件类原理:
- 创建插件的DexClassLoader类加载器,通过反射获取插件的dexElements
- 获取宿主的PathClassLoader类加载器,通过反射获取宿主的dexElements
- 合并宿主的dexElements和插件的dexElements,生成新的Element[]对象
- 通过反射把新的Element[]复制给宿主的dexElements。
以及StartActivity原理(二)——Android9.0 startActivity原理,Activity启动原理提到的
- Activity启动原理:
app进程告知AMS要启动activity,AMS检测Activity存在后,返回目标activity的类信息,并通知app进程构造插件Activity实例;
1.1 核心思想
- 启动插件的activity;
- 检测到启动的是插件Acitivity,将其hook成宿主app的ProxyActivity;
- AMS检测完成后,app启动ProxyActivity时,将其hook成我们的插件Activity。
1.2 具体原理
上面三步虽然简单,但是要明白为什么这么做?这么做原因有两点:
- 目标插件activity不在宿主Manifest清单文件中;
- 启动Activity需要通过AMS检测(manifest)。
所以在启动插件Activity的时候需要将intent的插件Activity hook成宿主的ProxyActivity去通过AMS检测,ProxyActivity在宿主app中,可以添加到Manifest清单文件中,自然可以通过检测;最后在启动阶段,将ProxyActivity再次hook成我们的插件Activity,最后在app进程可以构造其实例;
实现步骤:
- 加载插件apk/dex
- 启动插件Activity,并添加标志位表明启动插件Activity;
- Hook AMS,检测到intent中的标志位,替换插件Activity为ProxyActivity;
- hook ActivityThread的Handle——H,在handleLaunchActivity对应的的messageWhat,检测intent中的标志位;
- 将ProxyActivity重新替换成插件Activity;
附简单流程图:
2 实现
- 1.准备工作
public class ProxyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
public abstract class BaseActivity extends Activity {
protected void startPluginActivity(Intent intent) {
intent.putExtra(LoadUtil.INTENT_LOAD_PLUGIN, true);
startActivity(intent);
}
}
- 2.Hook AMS
public class LoadUtil{
private static final String TAG = "LoadUtil";
private final static String INTENT_ORIGIN_INTENT = "intent_origin_intent";
private final static String METHOD_START_ACTIVITY = "startActivity";
public final static String INTENT_LOAD_PLUGIN = "intent_load_plugin";
public final static int WHAT_LAUNCH_ACTIVITY = 100;
//9.0版本H 处理启动activity的流程
public final static int WHAT_EXECUTE_TRANSACTION = 159;
/**
* Hook AMS 的startActivity,用ProxyActivity替换plugin 的Activity,以欺骗AMS的manifest校验
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
@SuppressLint("PrivateApi")
public static void hookAMS() {
try {
//- 获取singleton(它作为单例持有了IActivityManager实例)
Field singletonFiled;
//8.0
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
Class<?> clazz = Class.forName("android.app.ActivityManagerNative");
singletonFiled = clazz.getDeclaredField("gDefault");
} else {
final Class<?> activityManagerClass = Class.forName("android.app.ActivityManager");
singletonFiled = activityManagerClass.getDeclaredField("IActivityManagerSingleton");
}
singletonFiled.setAccessible(true);
final Object singleton = singletonFiled.get(null);//静态对象,直接反射
//- 获取IActivityManager 对象
final Class<?> singletonClass = Class.forName("android.util.Singleton");
final Field mInstanceFiled = singletonClass.getDeclaredField("mInstance");
mInstanceFiled.setAccessible(true);
final Object mInstance = mInstanceFiled.get(singleton);
final Class<?> iActivityManagerClass = Class.forName("android.app.IActivityManager");
//- 代理IActivityManager
Object proxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[]{iActivityManagerClass},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (methodName.equals(METHOD_START_ACTIVITY)) {
Intent intent = null;
int intentIndex = -1;
//找到intent
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent) {
intent = (Intent) args[i];
intentIndex = i;
break;
}
}
//启动的是插件的activity才进行替换
if (intent != null && intent.getBooleanExtra(INTENT_LOAD_PLUGIN, false)) {
final Intent proxyIntent = new Intent();
proxyIntent.putExtra(INTENT_LOAD_PLUGIN, true);
//使用包含ProxyActivity的intent替换原来的intent,以骗过AMS
proxyIntent.setClassName("com.margin.host", "com.margin.host.ProxyActivity");
//保存原来的intent
proxyIntent.putExtra(INTENT_ORIGIN_INTENT, intent);
//替换原来的intent
args[intentIndex] = proxyIntent;
}
}
return method.invoke(mInstance, args);
}
});
//替换系统的IActivityManager
mInstanceFiled.set(singleton, proxyInstance);
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
- 3. Hook ActivityThread的Handler-H
这里需要再补充一点Handler的知识,在我另一篇帖子再论Handler—Handler事件分发、Handler线程切换原理解析有说到Handler处理消息有三种方式:
- 派生Handler,重写handleMessage;
- Message的Runnable回调;
- 给Handler添加Callback回调。
这里我们选择第三种方式(这个H-Handler并未添加Callback)在Callback.handleMesage中把ProxyActivity替换成目标Activity,切记,不要拦截,不要影响后续的流程。
这里9.0版本和之前的版本原理不一样,如果不理解的,烦请回看我的
- StartActivity原理(一)——Android9.0 pauseActivity原理
- StartActivity原理(二)——Android9.0 startActivity原理,Activity启动原理
/**
* Hook ActivityThread 的H-Handler,将ProxyActivity还原为 plugin的Activity
*/
public static void hoodH() {
final Handler.Callback callback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
try {
Intent proxyIntent;
Field intentField = null;
// 不同版本的 H 中 launchActivity what值不一样
if (msg.what == WHAT_LAUNCH_ACTIVITY) {
intentField = msg.obj.getClass().getDeclaredField("intent");
proxyIntent = (Intent) intentField.get(msg.obj);
if (proxyIntent != null
&& proxyIntent.getBooleanExtra(INTENT_LOAD_PLUGIN, false)) {
intentField.setAccessible(true);
//替换为原来的intent
Intent originIntent = proxyIntent.getParcelableExtra(INTENT_ORIGIN_INTENT);
if (originIntent != null) {
intentField.set(msg.obj, originIntent);
}
}
//28-9.0
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
&& msg.what == WHAT_EXECUTE_TRANSACTION) {
final Field mActivityCallbacksField = msg.obj.getClass().getDeclaredField("mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
final List mActivityCallbacks = (List) mActivityCallbacksField.get(msg.obj);
int launchItemIndex = -1;
for (int i = 0; i < mActivityCallbacks.size(); i++) {
if (mActivityCallbacks.get(i).getClass().getName().equals("android.app.servertransaction.LaunchActivityItem")) {
launchItemIndex = i;
break;
}
}
if (launchItemIndex > -1) {
final Object launchActivityItem = mActivityCallbacks.get(launchItemIndex);
intentField = launchActivityItem.getClass().getDeclaredField("mIntent");
intentField.setAccessible(true);
proxyIntent = (Intent) intentField.get(launchActivityItem);
Intent originIntent = proxyIntent.getParcelableExtra(INTENT_ORIGIN_INTENT);
if (originIntent != null) {
intentField.set(launchActivityItem, originIntent);
}
}
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
//切记返回false不拦截,这样H 可以处理后续流程
return false;
}
};
try {
//-1.获取ActivityThread 实例
final Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
final Field sCurrentActivityThread = activityThreadClazz.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThread.setAccessible(true);
final Object activityThread = sCurrentActivityThread.get(null);
//-2.获取ActivityThread的mH实例
final Field mHFiled = activityThreadClazz.getDeclaredField("mH");
mHFiled.setAccessible(true);
final Handler mH = (Handler) mHFiled.get(activityThread);
//-3.获取H的mCallback变量
final Class<?> handlerClazz = mH.getClass();
final Field mCallbackField = handlerClazz.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
//-4.注入我们的Handler.Callback
mCallbackField.set(mH, callback);
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
- 4.启用hook
public class HostApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
final String pluginApkPath = "/sdcard/plugin-debug.apk";
LoadUtil.loadClass(this, pluginApkPath);
LoadUtil.hookAMS();
LoadUtil.hoodH();
}
}
- 5.启动插件Activity
startPluginActivity(new Intent()
.setComponent(new ComponentName("com.margin.plugin", "com.margin.plugin.MainActivity")));
验证结果我不再贴出。
如何打包插件Dex以及加载插件类,这里不再赘述,您可以回看我的系列第一篇:Android插件化原理(一)—— 插件类加载、类加载原理、(双亲委托机制)
好了,本篇就到此结束啦,非常感谢您的阅读,如果有什么问题,还望您不吝赐教,批评指正,如果您看的满意,麻烦您给我点个赞,非常感谢。