内容简介:温馨提示:阅读本文需要15-20分钟(一大波代码)今天,我们来解决一个问题:人生一切难题,知识给你答案。
温馨提示:阅读本文需要15-20分钟(一大波代码)
今天,我们来解决一个问题:
Activity插件化原理第一种方案:Hook Instrumentation
人生一切难题,知识给你答案。
Activity的插件化解决的一个根本性问题就是插件中的Activity并没有在宿主的AndroidManifest.xml中进行注册,也就是说我们需要启动一个未注册的Activity,因此需要对Activity的启动过程有个了解。
启动Activity时会请求AMS创建Activity,这里的AMS指的是ActivityManagerService,AMS所属的进程与宿主(发起者)不属于同一个进程,AMS位于SystemServer进程中。
应用程序进程与AMS之间的通信是通过Binder来实现的,AMS要管理所有APP的启动请求,因此我们不能在SystemServer进程中进行相应的Hook,那么我们只能在应用进程中进行相应的Hook。
如果我们启动一个未注册的Activity,AMS会去检查AndroidManifest中是否注册了该Activity,如果未注册会报错。
为了让AMS验证通过,需要启动一个预先在AndroidManifest中注册的Activity,我们称之为占坑,在启动插件Activity时替换为占坑Activity,达到一个欺上瞒下的作用,当AMS验证通过之后,需要将启动的占坑Activity替换为插件Activity。
总结下来Activity的插件化需要做两件事:
- 将请求启动的插件Activity替换为占坑Activity。
- 绕过AMS验证后,将占坑Activity替换为插件Activity。
什么时候将插件Activity替换为占坑Activity?又是什么时候还原插件Activity?这需要我们对Activity的启动流程有个相应的认识。
我们在Activity中调用startActivity方法如下:
@Override public void startActivity(Intent intent) { this.startActivity(intent, null); } @Override public void startActivity(Intent intent, @Nullable Bundle options) { if (options != null) { startActivityForResult(intent, -1, options); } else { startActivityForResult(intent, -1); } } 复制代码
调用startActivityForResult方法:
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { if (mParent == null) { //Activity启动 options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); if (ar != null) { mMainThread.sendActivityResult( mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData()); } if (requestCode >= 0) { mStartedActivity = true; } cancelInputsAndStartExitTransition(options); windows. } else { if (options != null) { mParent.startActivityFromChild(this, intent, requestCode, options); } else { mParent.startActivityFromChild(this, intent, requestCode); } } } 复制代码
startActivityForResult方法中通过调用mInstrumentation的execStartActivity方法来启动Activity,这个mInstrumentation是Activity的成员变量,在ActivityThread的performLaunchActivity方法中通过Activity的attach方法传入,同时Activity的创建也是在performLaunchActivity方法中创建的,通过mInstrumentation.newActivity。
//:/frameworks/base/core/java/android/app/ActivityThread.java private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ... try { java.lang.ClassLoader cl = appContext.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(); if (r.state != null) { r.state.setClassLoader(cl); } } ... activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback); ... } 复制代码
综上所述Instrumentation提供了execStartActivity方法来启动Activity,newActivity方法来创建Activity。因此,第一种方案就是用代理Instrumentation来替代Activity的Instrumentation,并在代理Instrumentation的execStartActivity方法中替换为占坑Activity,在newActivity方法还原插件Activity。
现在我们基于第一种方案Hook Instrumentation来实现Activity的插件化。
首先创建占坑Activity:
public class StubActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_stub); } } 复制代码
创建插件Activity:
public class TargetActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_target); } } 复制代码
并在AndroidManifest.xml中注册占坑Activity:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.glh.haiproject01"> <application android:name=".MyApplication" 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" tools:ignore="AllowBackup,GoogleAppIndexingWarning"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".StubActivity" /> </application> </manifest> 复制代码
在AndroidManifest.xml中没有注册插件Activity,这时如果启动插件Activity会报错。
最后Hook Instrumentation,将ActivityThread中的成员变量Instrumentation替换成代理的Instrumentation。
创建代理Instrumentation类:
public class InstrumentationProxy extends Instrumentation { private Instrumentation mInstrumentation; private PackageManager mPackageManager; public InstrumentationProxy(Instrumentation instrumentation, PackageManager packageManager) { this.mInstrumentation = instrumentation; this.mPackageManager = packageManager; } public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { List<ResolveInfo> resolveInfo = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL); //判断启动的插件Activity是否在AndroidManifest.xml中注册过 if (null == resolveInfo || resolveInfo.size() == 0) { //保存目标插件 intent.putExtra(HookHelper.REQUEST_TARGET_INTENT_NAME, intent.getComponent().getClassName()); //设置为占坑Activity intent.setClassName(who, "com.glh.haiproject01.StubActivity"); } try { Method execStartActivity = Instrumentation.class.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; } public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { String intentName=intent.getStringExtra(HookHelper.REQUEST_TARGET_INTENT_NAME); if(!TextUtils.isEmpty(intentName)){ return super.newActivity(cl,intentName,intent); } return super.newActivity(cl,className,intent); } } 复制代码
代理类InstrumentationProxy的execStartActivity方法先判断插件Activity是否在AndroidManifest.xml中注册过,如果没有注册过就需要替换占坑的Activity,在newActivity方法中还原插件Activity。
代理类InstrumentationProxy写完后,需要对ActivityThread的成员变量mInstrumentation进行替换。
public class MyApplication extends Application { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); hookActivityThreadInstrumentation(); } private void hookActivityThreadInstrumentation(){ try { Class<?> activityThreadClass=Class.forName("android.app.ActivityThread"); Field activityThreadField=activityThreadClass.getDeclaredField("sCurrentActivityThread"); activityThreadField.setAccessible(true); //获取ActivityThread对象sCurrentActivityThread Object activityThread=activityThreadField.get(null); Field instrumentationField=activityThreadClass.getDeclaredField("mInstrumentation"); instrumentationField.setAccessible(true); //从sCurrentActivityThread中获取成员变量mInstrumentation Instrumentation instrumentation= (Instrumentation) instrumentationField.get(activityThread); //创建代理对象InstrumentationProxy InstrumentationProxy proxy=new InstrumentationProxy(instrumentation,getPackageManager()); //将sCurrentActivityThread中成员变量mInstrumentation替换成代理类InstrumentationProxy instrumentationField.set(activityThread,proxy); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } 复制代码
这时我们在主界面点击跳转插件Activity:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn_startActivity).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent=new Intent(MainActivity.this,TargetActivity.class); startActivity(intent); } }); } } 复制代码
运行效果:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Jenkins kubernetes插件的原理
- Kubernetes 网络插件工作原理剖析
- Android插件化原理解析——剧终
- 深入浅出Service插件化原理
- 知识总结 插件化学习 Binder机制原理
- Mybatis插件扩展以及与Spring整合原理
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
与机器赛跑
[美]埃里克·布林约尔松(Erik Brynjolfsson)、[美]安德鲁·麦卡菲(Andrew McAfee) / 闾佳 / 2013-1-20 / 6.00
一场数字革命正在加速进行。 一些科幻小说里的场景已经在现实中发生:无人驾驶汽车开上了公路;智能设备能高效地翻译人类语言;人工智能系统在智力竞赛里击败了所有人类选手;工厂雇主开始购买更多的新机器,却不招新工人…… 这些例子都证明,数字技术正在快速地掌握原本只属于人类的技能,并深刻地影响了经济。虽然大多数影响是积极的:数字革新将提高效率、降低商品价格(甚至到免费),以及增加经济总量。 ......一起来看看 《与机器赛跑》 这本书的介绍吧!