Android-第三方开源框架—插件化原理之Activity插件化(腾讯专家最佳插件化讲解)

//获取activityManager中的IActivityManagerSingleton字段

defaultSingleton= FieldUtil.getField(activityManageClazz ,null,“IActivityManagerSingleton”);

} else {

Class<?> activityManagerNativeClazz = Class.forName(“android.app.ActivityManagerNative”);

//获取ActivityManagerNative中的gDefault字段

defaultSingleton= FieldUtil.getField(activityManagerNativeClazz,null,“gDefault”);

}

Class<?> singletonClazz = Class.forName(“android.util.Singleton”);

Field mInstanceField= FieldUtil.getField(singletonClazz ,“mInstance”);//2

//获取iActivityManager

Object iActivityManager = mInstanceField.get(defaultSingleton);//3

Class<?> iActivityManagerClazz = Class.forName(“android.app.IActivityManager”);

Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),

new Class<?>[] { iActivityManagerClazz }, new IActivityManagerProxy(iActivityManager));

mInstanceField.set(defaultSingleton, proxy);

}

}

首先在注释1处对系统版本进行区分,最终获取的是 Singleton<IActivityManager>类型的IActivityManagerSingleton或者gDefault字段。注释2处获取Singleton类中的mInstance字段,从前面Singleton类的代码可以得知mInstance字段的类型为T,在注释3处得到IActivityManagerSingleton或者gDefault字段中的T的类型,T的类型为IActivityManager。最后动态创建代理类IActivityManagerProxy,用IActivityManagerProxy来替换IActivityManager。 自定义一个Application,在其中调用HookHelper 的hookAMS方法,如下所示。

MyApplication.java

public class MyApplication extends Application {

@Override

public void attachBaseContext(Context base) {

super.attachBaseContext(base);

try {

HookHelper.hookAMS();

} catch (Exception e) {

e.printStackTrace();

}

}

}

在MainActivity中启动TargetActivity,如下所示。

MainActivity.java

public class MainActivity extends Activity {

private Button bt_hook;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

bt_hook = (Button) this.findViewById(R.id.bt_hook);

bt_hook.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

Intent intent = new Intent(MainActivity.this, TargetActivity.class);

startActivity(intent);

}

});

}

}

点击Button时,启动的并不是TargetActivity而是SubActivity,同时Log中打印了"hook成功",说明我们已经成功用SubActivity通过了AMS的校验。

2.3 还原插件Activity

前面用占坑Activity通过了AMS的校验,但是我们要启动的是插件TargetActivity,还需要用插件TargetActivity来替换占坑的SubActivity,这一替换的时机就在图2的步骤2之后。 ActivityThread启动Activity的过程,如图3所示。

图3

ActivityThread会通过H将代码的逻辑切换到主线程中,H类是ActivityThread的内部类并继承自Handler,如下所示。 frameworks/base/core/java/android/app/ActivityThread.java

private class H extends Handler {

public static final int LAUNCH_ACTIVITY = 100;

public static final int PAUSE_ACTIVITY = 101;

public void handleMessage(Message msg) {

if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));

switch (msg.what) {

case LAUNCH_ACTIVITY: {

Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “activityStart”);

final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

r.packageInfo = getPackageInfoNoCheck(

r.activityInfo.applicationInfo, r.compatInfo);

handleLaunchActivity(r, null, “LAUNCH_ACTIVITY”);

Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

} break;

}

}

H中重写的handleMessage方法会对LAUNCH_ACTIVITY类型的消息进行处理,最终会调用Activity的onCreate方法。那么在哪进行替换呢?接着来看Handler的dispatchMessage方法: frameworks/base/core/java/android/os/Handler.java

public void dispatchMessage(Message msg) {

if (msg.callback != null) {

handleCallback(msg);

} else {

if (mCallback != null) {

if (mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

}

Handler的dispatchMessage用于处理消息,看到如果Handler的Callback类型的mCallback不为null,就会执行mCallback的handleMessage方法。因此,mCallback可以作为Hook点,我们可以用自定义的Callback来替换mCallback,自定义的Callback如下所示。 HCallback.java

public class HCallback implements Handler.Callback{

public static final int LAUNCH_ACTIVITY = 100;

Handler mHandler;

public HCallback(Handler handler) {

mHandler = handler;

}

@Override

public boolean handleMessage(Message msg) {

if (msg.what == LAUNCH_ACTIVITY) {

Object r = msg.obj;

try {

//得到消息中的Intent(启动SubActivity的Intent)

Intent intent = (Intent) FieldUtil.getField(r.getClass(), r, “intent”);

//得到此前保存起来的Intent(启动TargetActivity的Intent)

Intent target = intent.getParcelableExtra(HookHelper.TARGET_INTENT);

//将启动SubActivity的Intent替换为启动TargetActivity的Intent

intent.setComponent(target.getComponent());

} catch (Exception e) {

e.printStackTrace();

}

}

mHandler.handleMessage(msg);

return true;

}

}

HCallback实现了Handler.Callback,并重写了handleMessage方法,当收到消息的类型为LAUNCH_ACTIVITY时,将启动SubActivity的Intent替换为启动TargetActivity的Intent。接着我们在HookHelper中定义一个hookHandler方法如下所示。

HookHelper.java

public static void hookHandler() throws Exception {

Class<?> activityThreadClass = Class.forName(“android.app.ActivityThread”);

Object currentActivityThread= FieldUtil.getField(activityThreadClass ,null,“sCurrentActivityThread”);//1

Field mHField = FieldUtil.getField(activityThread,“mH”);//2

Handler mH = (Handler) mHField.get(currentActivityThread);//3

FieldUtil.setField(Handler.class,mH,“mCallback”,new HCallback(mH));

}

ActivityThread类中有一个静态变量sCurrentActivityThread,用于表示当前的ActivityThread对象,因此在注释1处获取ActivityThread中定义的sCurrentActivityThread对象。注释2处获取ActivityThread类的mH字段,接着在注释3处获取当前ActivityThread对象中的mH对象,最后用HCallback来替换mH中的mCallback。 在MyApplication的attachBaseContext方法中调用HookHelper的hookHandler方法,运行程序,当我们点击启动插件按钮,发现启动的是插件TargetActivity。

2.4 插件Activity的生命周期

插件TargetActivity确实启动了,但是它有生命周期吗?这里从源码角度来进行分析,Activity的finish方法可以触发Activity的生命周期变化,和Activity的启动过程类似,finish方法如下所示。 frameworks/base/core/java/android/app/Activity.java

public void finish() {

finish(DONT_FINISH_TASK_WITH_ACTIVITY);

}

private void finish(int finishTask) {

if (mParent == null) {

int resultCode;

Intent resultData;

synchronized (this) {

resultCode = mResultCode;

resultData = mResultData;

}

if (false) Log.v(TAG, “Finishing self: token=” + mToken);

try {

if (resultData != null) {

resultData.prepareToLeaveProcess(this);

}

if (ActivityManager.getService()

.finishActivity(mToken, resultCode, resultData, finishTask)) {//1

mFinished = true;

}

} catch (RemoteException e) {

// Empty

}

} else {

mParent.finishFromChild(this);

}

}

finish方法的调用链和Activity的启动过程是类似的,注释1处会调用的AMS的finishActivity方法,接着是AMS通过ApplicationThread调用ActivityThread,ActivityThread向H发送DESTROY_ACTIVITY类型的消息,H接收到这个消息会执行handleDestroyActivity方法,handleDestroyActivity方法又调用了performDestroyActivity方法,如下所示。

frameworks/base/core/java/android/app/ActivityThread.java

private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,

int configChanges, boolean getNonConfigInstance) {

ActivityClientRecord r = mActivities.get(token);//1

try {

r.activity.mCalled = false;

mInstrumentation.callActivityOnDestroy(r.activity);//2

} catch (SuperNotCalledException e) {

}

}

mActivities.remove(token);

StrictMode.decrementExpectedActivityCount(activityClass);

return r;

注释1处通过IBinder类型的token来获取ActivityClientRecord,ActivityClientRecord用于描述应用进程中的Activity。在注释2处调用Instrumentation的callActivityOnDestroy方法来调用Activity的OnDestroy方法,并传入了r.activity。前面的例子我们用SubActivity替换了TargetActivity通过了AMS的校验,这样AMS只知道SubActivity的存在,那么AMS是如何能控制TargetActivity生命周期的回调的?我们接着往下看,启动Activity时会调用ActivityThread的performLaunchActivity方法,如下所示。

frameworks/base/core/java/android/app/ActivityThread.java

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

java.lang.ClassLoader cl = appContext.getClassLoader();

activity = mInstrumentation.newActivity(

cl, component.getClassName(), r.intent);//1

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);

mActivities.put(r.token, r);//2

return activity;

}

注释1处根据Activity的类名用ClassLoader加载Acitivty,接着调用Activity的attach方法,将r.token赋值给Activity的成员变量mToken。在注释2处将ActivityClientRecord根据r.token存在mActivities中(mActivities类型为ArrayMap<IBinder, ActivityClientRecord> ),再结合Activity的finish方法的注释1处,可以得出结论:AMS和ActivityThread之间的通信采用了token来对Activity进行标识,并且此后的Activity的生命周期处理也是根据token来对Activity进行标识的。 回到我们这个例子来,我们在Activity启动时用插件TargetActivity替换占坑SubActivity,这一过程在performLaunchActivity方法调用之前,因此注释2处的r.token指向的是TargetActivity,在performDestroyActivity的注释1处获取的就是代表TargetActivity的ActivityClientRecord,可见TargetActivity是具有生命周期的。

3.Hook Instrumentation方案实现

Hook Instrumentation实现要比Hook IActivityManager实现要简洁一些,示例代码会和Hook IActivityManager实现有重复,重复的部分这里不再赘述。 Hook Instrumentation实现同样也需要用到占坑Activity,与Hook IActivityManager实现不同的是,用占坑Activity替换插件Activity以及还原插件Activity的地方不同。Acitivty的startActivity方法调用时序图如图4所示。

图4

从图4可以发现,在Activity通过AMS校验前,会调用Activity的startActivityForResult方法: frameworks/base/core/java/android/app/Activity.java

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,

@Nullable Bundle options) {

if (mParent == null) {

options = transferSpringboardActivityOptions(options);

Instrumentation.ActivityResult ar =

mInstrumentation.execStartActivity(

this, mMainThread.getApplicationThread(), mToken, this,

intent, requestCode, options);

} else {

}

}

startActivityForResult方法中调用了Instrumentation的execStartActivity方法来激活Activity的生命周期。 如图3所示,ActivityThread启动Activity的过程中会调用ActivityThread的performLaunchActivity方法,如下所示。 frameworks/base/core/java/android/app/ActivityThread.java

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

//创建要启动Activity的上下文环境

ContextImpl appContext = createBaseContextForActivity®;

Activity activity = null;

try {

java.lang.ClassLoader cl = appContext.getClassLoader();

//用类加载器来创建Activity的实例

activity = mInstrumentation.newActivity(

cl, component.getClassName(), r.intent);//1

} catch (Exception e) {

}

return activity;

}

注释1处调用了mInstrumentation的newActivity方法,其内部会用类加载器来创建Activity的实例。看到这里我们可以得到方案,就是在Instrumentation的execStartActivity方法中用占坑SubActivity来通过AMS的验证,在Instrumentation的newActivity方法中还原TargetActivity,这两部操作都和Instrumentation有关,因此我们可以用自定义的Instrumentation来替换掉mInstrumentation。首先我们自定义一个Instrumentation,在execStartActivity方法中将启动的TargetActivity替换为SubActivity,如下所示。

InstrumentationProxy.java

public class InstrumentationProxy extends Instrumentation {

private Instrumentation mInstrumentation;

private PackageManager mPackageManager;

public InstrumentationProxy(Instrumentation instrumentation, PackageManager packageManager) {

mInstrumentation = instrumentation;

mPackageManager = packageManager;

}

public ActivityResult execStartActivity(

Context who, IBinder contextThread, IBinder token, Activity target,

Intent intent, int requestCode, Bundle options) {

List infos = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL);

if (infos == null || infos.size() == 0) {

intent.putExtra(HookHelper.TARGET_INTENsT_NAME, intent.getComponent().getClassName());//1

intent.setClassName(who, “com.example.liuwangshu.pluginactivity.StubActivity”);//2

}

try {

Method execMethod = Instrumentation.class.getDeclaredMethod(“execStartActivity”,

Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class);

return (ActivityResult) execMethod.invoke(mInstrumentation, who, contextThread, token,

target, intent, requestCode, options);

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。

j-1711912863662)]
[外链图片转存中…(img-0pRUq7ob-1711912863662)]
[外链图片转存中…(img-94NpHJBi-1711912863663)]
[外链图片转存中…(img-5prF2pXc-1711912863663)]
[外链图片转存中…(img-KE1S73Fb-1711912863664)]
[外链图片转存中…(img-51VL9eY0-1711912863664)]
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-OsObkIRa-1711912863664)]

最后

由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 23
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值