android插件化(Activity篇)
来源:互联网 发布:wifi网络连接受限 编辑:程序博客网 时间:2024/06/14 16:10
本文承接上一篇博客,先贴出Activity启动关键步骤:
Context.startActivity --> ContextImpl.startActivity --> mMainThread.getInstrumentation().execStartActivity() -->ActivityManagerNative.getDefault().startActivity() --> ActivityManagerService.startActivity() --> startActivityAsUser --> ActivityStackSupervisor(android高版本对AMS管理进行的重构.startActivityMayWait --> ... --> ApplicationThread.scheduleLaunchActivity() --> ActivityThread.handleLaunchActivity --> performLaunchActivity()
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
mActivities.put(r.token, r);...
return activity;}
Demo关键代码:
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { hookAms(); setHandlerCallback(); } catch (Exception e) { e.printStackTrace(); } startActivity(new Intent(MainActivity.this,DestActivity.class));}
private void hookAms() throws Exception { Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative"); Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault"); gDefaultField.setAccessible(true); Object gDefault = gDefaultField.get(null); /** * 4.x以上的gDefault是一个 android.util.Singleton对象,我们取出这个单例里面的字段 */ Class<?> singleton = Class.forName("android.util.Singleton"); Field mInstanceField = singleton.getDeclaredField("mInstance"); mInstanceField.setAccessible(true); /** * ActivityManagerNative的gDefault对象里面原始的IActivityManager对象 */ Object rawIActivityManager = mInstanceField.get(gDefault); /** * 创建一个这个对象的代理对象,然后替换这个字段,让我们的代理对象帮忙干活 */ Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager"); Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{iActivityManagerInterface}, new IActivityManagerHandler(rawIActivityManager, getPackageName())); mInstanceField.set(gDefault, proxy);}
class IActivityManagerHandler implements InvocationHandler { private Object mBase; private String packageName; IActivityManagerHandler(Object base, String packageName) { this.mBase = base; this.packageName = packageName; } /** * 更改Intent参数,替换原始Activity启动别的Activity */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("startActivity".equals(method.getName())) { Intent originalIntent; int originalIntentPos = 0; Intent newIntent = new Intent(); for (int i = 0; i < args.length; i++) { if (args[i] instanceof Intent) { originalIntentPos = i; break; } } originalIntent = (Intent) args[originalIntentPos]; /** * 把真实要启动的Activity临时替换为StubActivity */ ComponentName componentName = new ComponentName(packageName, StubActivity.class.getCanonicalName()); newIntent.setComponent(componentName); newIntent.putExtra("src_intent", originalIntent); //保存原始的Intent args[originalIntentPos] = newIntent; return method.invoke(mBase, args); } return method.invoke(mBase, args); }}上面代码中,IActivityManagerHandler主要用于替换ActivityManagerNative的gDefault变量里的mInstance变量,由于AMS和PMS在android中比较常用,系统为我们维护了一个gDefault单例模式,其中ActivityManagerNative实例就保存在其中的mInstance变量中,在上面的代码中,我们hook住了ActivityManagerNative的startActivity方法,将Intent中真正要启动的Activity替换为我们事先注册好的StubActivity,进而达到欺骗系统的目的
void setHandlerCallback() throws Exception { Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread"); currentActivityThreadField.setAccessible(true); Object currentActivityThread = currentActivityThreadField.get(null); Field mHField = activityThreadClass.getDeclaredField("mH"); mHField.setAccessible(true); Handler mH = (Handler) mHField.get(currentActivityThread); Field mCallBackField = Handler.class.getDeclaredField("mCallback"); mCallBackField.setAccessible(true); mCallBackField.set(mH, new ActivityThreadHandlerCallback(mH)); }}class ActivityThreadHandlerCallback implements Handler.Callback { private Handler mBase; ActivityThreadHandlerCallback(Handler base) { mBase = base; } @Override public boolean handleMessage(Message msg) { switch (msg.what) { // ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100,避免麻烦直接硬编码 case 100: try { handleLaunchActivity(msg); } catch (Exception e) { e.printStackTrace(); } break; } mBase.handleMessage(msg); return true; } private void handleLaunchActivity(Message msg) throws Exception { //恢复真身 Field intent = msg.obj.getClass().getDeclaredField("intent"); intent.setAccessible(true); Intent raw = (Intent) intent.get(msg.obj); Intent target = raw.getParcelableExtra("src_intent"); raw.setComponent(target.getComponent()); }}
上面这段代码主要用于设置ActivityThread的Handler变量的Callback值,用于屏蔽真正的处理ACTIVITY_LAUNCH的逻辑,由于我们给系统传递了StubActivity,在返回到客户端处理逻辑时(ApplicationThread通过handler机制转移到ActivityThread中),我们要恢复真正要启动的Activity,在这里,可有有人会问,我们是把系统欺骗了,可是系统识别这个Activity吗,答案是yes,我们以onDestroy为例简要分析一下:
从Activity的finish方法开始跟踪,最终会通过ActivityManagerNative到AMS然后接着通过ApplicationThread到ActivityThread,然后通过Handler转发消息到ActivityThread的handleDestroyActivity,接着这个方法把任务交给performDestroyActivity完成,继续分析performDestroyActivity,关键代码如下:
private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,int configChanges, boolean getNonConfigInstance) { ActivityClientRecord r = mActivities.get(token); Class<? extends Activity> activityClass = null; if (r != null) { activityClass = r.activity.getClass();
...
}
...
}在demo中,r.activity是DestActivity还是StubActivity呢,按理说,由于我们欺骗了AMS,AMS应该只知道StubActivity的存在,它压根儿就不知道TargetActivity是什么,为什么它能正确完成对TargetActivity生命周期的回调呢?一切的秘密在token里面,AMS与ActivityThread之间对于Activity的生命周期的交互,并没有直接使用Activity对象进行交互,而是使用一个token来标识,这个token是binder对象,因此可以方便地跨进程传递,Activity里面有一个成员变量mToken代表的就是它,token可以唯一地标识一个Activity对象,它在Activity的attach方法里面初始化;在AMS处理Activity的任务栈的时候,使用这个token标记Activity,因此在我们的demo里面,AMS进程里面的token对应的是StubActivity,也就是AMS还在傻乎乎地操作StubActivity(关于这一点,你可以dump出任务栈的信息,可以观察到dump出的确实是StubActivity),但是在我们App进程里面,token对应的却是TargetActivity,因此,在ActivityThread执行回调的时候,能正确地回调到TargetActivity相应的方法,回到代码,ActivityClientRecord是在mActivities里面取出来的,确实是根据token取;那么这个token是什么时候添加进去的呢?我们看performLaunchActivity就完成明白了:它通过classloader加载了TargetActivity,然后完成一切操作之后把这个activity添加进了mActivities!另外,在这个方法里面我们还能看到对Ativity中attach方法的调用,它传递给了新创建的Activity一个token对象,而这个token是在ActivityClientRecord构造函数里面初始化的(参考文章:Android 插件化原理解析-Activity生命周期管理)
总结起来一句话:启动Activity是通过ActivityClientRecord里的Intent标识Activity,AMS会返回对应的token,期间对Activity的操作都会依据ActivityClientRecord里面的token值,我们更改了Intent,却没有更改token值,因此,AMS和ActivityThread可以对应处理不同的Activity
0 0
- android插件化(Activity篇)
- android插件化开发activity篇
- Android插件化开发 第四篇 [加载插件Activity]
- Android插件化开发 第四篇 [加载插件Activity]
- Android插件化系列第(五)篇---Activity的插件化方案(代理模式)
- android程序共享activity实现插件化
- Android插件化之Activity生命周期处理
- android 插件化之Activity生命周期之一
- Android插件化基础(4),动态启动插件中的Activity
- android插件化-apkplug从宿主启动插件Activity-06
- Android插件化系列第(一)篇---Hook技术之Activity的启动过程的拦截
- android 插件Activity生命周期管理
- 插件化Activity
- Android插件化探索(三)免安装运行Activity(上)
- Android插件化探索(四)免安装运行Activity(下)
- Android 插件化原理解析(5):Activity 生命周期管理(下)
- Android 插件化原理解析(5):Activity 生命周期管理(上)
- Android插件化原理系列(一)启动未声明的Activity
- Mac 下载配置maven,eclipse安装maven插件
- [Language]Python映像与集合--字典
- Let it Bead (POJ
- ButterKnife使用
- VIM窗口分隔以及相关操作总结
- android插件化(Activity篇)
- 2
- 源代码防泄密软件成焦点
- SpringBoot学习3之数据库集成mybatis
- echarts使用,[自适应、右上角的工具图标、标题、图例问题]
- 我的python进阶之路四
- WLAN安全学习
- 5-3 树的同构 (25分) PTA
- 4