Android 插件化系列文章目录

【Android 插件化】插件化简介 ( 组件化与插件化 )
【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 )
【Android 插件化】插件化原理 ( 类加载器 )

【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 运行应用 | 代码整理 )

【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )
【Android 插件化】Hook 插件化框架 ( Hook 实现思路 | Hook 按钮点击事件 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 )

【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )
【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )

【Android 插件化】Hook 插件化框架 ( hook 插件化原理 | 插件包管理 )
【Android 插件化】Hook 插件化框架 ( 通过反射获取 “插件包“ 中的 Element[] dexElements )
【Android 插件化】Hook 插件化框架 ( 通过反射获取 “宿主“ 应用中的 Element[] dexElements )
【Android 插件化】Hook 插件化框架 ( 合并 “插件包“ 与 “宿主“ 中的 Element[] dexElements | 设置合并后的 Element[] 数组 )
【Android 插件化】Hook 插件化框架 ( 创建插件应用 | 拷贝插件 APK | 初始化插件包 | 测试插件 DEX 字节码 )

【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | Hook 点分析 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 反射获取 IActivityManager 对象 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | AMS 启动前使用动态代理替换掉插件 Activity 类 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )

【Android 插件化】Hook 插件化框架 ( 反射工具类 | 反射常用操作整理 )

【Android 插件化】Hook 插件化框架 ( 插件包资源加载 )
【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )


文章目录

  • Android 插件化系列文章目录
  • 前言
  • 一、从源码角度分析加载资源流程
    • 1、ActivityThread 入口
    • 2、LaunchActivityItem
    • 3、ActivityThread.performLaunchActivity
    • 4、ContextImpl
  • 二、Hook 点选择
  • 三、资源冲突解决方案
  • 四、博客资源

前言

在之前的博客 【Android 插件化】Hook 插件化框架 ( 插件包资源加载 ) 中 , 实现了从插件包中获取资源 ;

但是这种方法对代码的侵入性较大 , 使用这种方式开发 , 插件应用 和 宿主应用 , 都需要对 Resources 进行特别处理 , 如重写 Activity 和 Application 的 public Resources getResources() 方法 ;

最好是使用 Hook 方式加载资源文件 , 实现插件包代码 0 侵入 , 开发插件应用 与 开发普通应用 , 基本一致 ;

一、从源码角度分析加载资源流程


在插件包中的 Activity , 如果加载 R.layout.activity_main , 拿到的是 “宿主” 应用中的资源 , 无法拿到插件包中的资源 ;

1、ActivityThread 入口

在 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 ) 博客中 , 分析了 Activity 在主线程启动前的一些操作 ;

在 ActivityThread 中的 mH 处理 EXECUTE_TRANSACTION 信号时 , 其中 ClientTransaction 中有 LaunchActivityItem ;

public final class ActivityThread extends ClientTransactionHandler {final H mH = new H();/** Reference to singleton {@link ActivityThread} */private static volatile ActivityThread sCurrentActivityThread;class H extends Handler {public static final int EXECUTE_TRANSACTION = 159;public void handleMessage(Message msg) {if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));switch (msg.what) {case EXECUTE_TRANSACTION:final ClientTransaction transaction = (ClientTransaction) msg.obj;mTransactionExecutor.execute(transaction);if (isSystem()) {// Client transactions inside system process are recycled on the client side// instead of ClientLifecycleManager to avoid being cleared before this// message is handled.transaction.recycle();}// TODO(lifecycler): Recycle locally scheduled transactions.break;}}}
}

源码路径 : /frameworks/base/core/java/android/app/ActivityThread.java

2、LaunchActivityItem

LaunchActivityItem 中的 execute 方法中的 ClientTransactionHandler client 参数 就是 ActivityThread ;

public class LaunchActivityItem extends ClientTransactionItem {@Overridepublic void execute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions) {Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,mPendingResults, mPendingNewIntents, mIsForward,mProfilerInfo, client);client.handleLaunchActivity(r, pendingActions, null /* customIntent */);Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);}
}

源码路径 : /frameworks/base/core/java/android/app/servertransaction/LaunchActivityItem.java

3、ActivityThread.performLaunchActivity

在 LaunchActivityItem 的 execute 方法中 , 调用 client.handleLaunchActivity 方法就是执行的 ActivityThread 的 handleLaunchActivity 方法 ;

在 ActivityThread 中的 handleLaunchActivity 方法中 , 调用了 performLaunchActivity 方法 ;

在 ActivityThread 中的 performLaunchActivity 方法中 , 调用

ContextImpl appContext = createBaseContextForActivity(r);

方法 , 创建了 ContextImpl 对象 ;

public final class ActivityThread extends ClientTransactionHandler {private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {final int displayId;try {displayId = ActivityManager.getService().getActivityDisplayId(r.token);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();// For debugging purposes, if the activity's package name contains the value of// the "debug.use-second-display" system property as a substring, then show// its content on a secondary display if there is one.String pkgName = SystemProperties.get("debug.second-display.pkg");if (pkgName != null && !pkgName.isEmpty()&& r.packageInfo.mPackageName.contains(pkgName)) {for (int id : dm.getDisplayIds()) {if (id != Display.DEFAULT_DISPLAY) {Display display =dm.getCompatibleDisplay(id, appContext.getResources());appContext = (ContextImpl) appContext.createDisplayContext(display);break;}}}return appContext;}
}

源码路径 : /frameworks/base/core/java/android/app/ActivityThread.java

4、ContextImpl

在 ActivityThread 中调用了

        ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);

方法 , 创建了 ContextImpl ;

下面分析 ContextImpl 的 createActivityContext 方法 ;

class ContextImpl extends Context {static ContextImpl createActivityContext(ActivityThread mainThread,LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,Configuration overrideConfiguration) {if (packageInfo == null) throw new IllegalArgumentException("packageInfo");String[] splitDirs = packageInfo.getSplitResDirs();ClassLoader classLoader = packageInfo.getClassLoader();if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");try {classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);} catch (NameNotFoundException e) {// Nothing above us can handle a NameNotFoundException, better crash.throw new RuntimeException(e);} finally {Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);}}ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,activityToken, null, 0, classLoader);// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)? packageInfo.getCompatibilityInfo(): CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;final ResourcesManager resourcesManager = ResourcesManager.getInstance();// Create the base resources for which all configuration contexts for this Activity// will be rebased upon.context.setResources(resourcesManager.createBaseActivityResources(activityToken,packageInfo.getResDir(),splitDirs,packageInfo.getOverlayDirs(),packageInfo.getApplicationInfo().sharedLibraryFiles,displayId,overrideConfiguration,compatInfo,classLoader));context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,context.getResources());return context;}
}

源码路径 : /frameworks/base/core/java/android/app/ContextImpl.java

在上述方法中 , 就有创建 Resources 资源的方法 :

        context.setResources(resourcesManager.createBaseActivityResources(activityToken,packageInfo.getResDir(),splitDirs,packageInfo.getOverlayDirs(),packageInfo.getApplicationInfo().sharedLibraryFiles,displayId,overrideConfiguration,compatInfo,classLoader));

二、Hook 点选择


修改应用 Resources 的 Hook 点有很多 , 通过改变 Activity 的 Resources , 甚至修改 ActivityThread 中创建 Resources 的流程 都可以实现 ;

在插件包中 , 使用了 AppCompatActivity , 可以直接替换 AppCompatActivity 中的 private Resources mResources 成员 ;

public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {private static final String DELEGATE_TAG = "androidx:appcompat";private AppCompatDelegate mDelegate;private Resources mResources;
}

只要可以拿到 AppCompatActivity 实例 , 就可以通过反射 , 替换掉 private Resources mResources 成员 ;

在 Instrumentation 中 , 调用了 newActivity 创建 Activity 实例 ;

    public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id,Object lastNonConfigurationInstance) throws InstantiationException, IllegalAccessException {Activity activity = (Activity)clazz.newInstance();ActivityThread aThread = null;// Activity.attach expects a non-null Application Object.if (application == null) {application = new Application();}activity.attach(context, aThread, this, token, 0 /* ident */, application, intent,info, title, parent, id,(Activity.NonConfigurationInstances)lastNonConfigurationInstance,new Configuration(), null /* referrer */, null /* voiceInteractor */,null /* window */, null /* activityConfigCallback */);return activity;}

源码路径 : /frameworks/base/core/java/android/app/Instrumentation.java

在 ActivityThread 中 , 有 Instrumentation mInstrumentation 成员变量 , ActivityThread 本身就是单例 , 通过获取其静态成员 private static volatile ActivityThread sCurrentActivityThread , 就可以获取到 ActivityThread ;

    /** Reference to singleton {@link ActivityThread} */private static volatile ActivityThread sCurrentActivityThread;Instrumentation mInstrumentation;

三、资源冲突解决方案


资源的 ID 在 AAPT 编译资源阶段就确定了 ;

固定类型的资源 , 编号是从一定的编号段开始的 , 如 layout 布局资源 , 第一个布局资源总是 2131361820 ;

不同类型的资源 , 布局 , 字符串 , 数值 , 主题 , 图片 , drawable 等 , 都有对应的资源编号范围 , 同时也要兼容系统的资源编号范围 ;

如果宿主应用启动 , 加载第一个布局资源 , 那么编号是 2131361820 ;
如果插件应用启动 , 加载第一个布局资源 , 那么编号也是 2131361820 ;
这样就出现了资源冲突 ;

使用不同的 AssetManager 加载不同的资源 , 可以解决资源冲突问题 ;

四、博客资源


博客资源 :

  • GitHub : https://github.com/han1202012/Plugin_Hook

【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )相关推荐

  1. Android开发知识(二十三)从源码角度分析ListView的滑动复用机制

    文章目录 前言 认识RecycleBin机制 ListView的布局方式 ListView的元素创建流程 ListView滑动加载过程 前言 ListView作为一个常用的列表控件,虽然现在基本被Re ...

  2. 从源码角度分析Android中的Binder机制的前因后果

    为什么在Android中使用binder通信机制? 众所周知linux中的进程通信有很多种方式,比如说管道.消息队列.socket机制等.socket我们再熟悉不过了,然而其作为一款通用的接口,通信开 ...

  3. php tp5框架新特性面试,tp5完整加载执行流程——thinkphp5 框架

    1,入口文件(tp5\public\index.php) 作用: 1)定义目录常量 2)加载框架引导目录 2.加载框架引导文件(tp5\thinkphp\start.php) 作用: 1)引导基础文件 ...

  4. android 换肤框架原理,换肤框架Android-skin-support 源码分析

    基本原理: Android-skin-support 这个框架的主要思路是: 1. 更换LayoutInflaterFactory,对从xml布局文件要解析出来的 View,如果有相对应的支持换肤功能 ...

  5. 从源码角度分析Android系统的异常捕获机制是如何运行的

    我们在开发的时候经常会遇到各种异常,当程序遇到异常,便会将异常信息抛到LogCat中,那这个过程是怎么实现的呢? 我们以一个例子开始: import android.app.Activity; imp ...

  6. HashMap,ArrayMap,SparseArray 源码角度分析,Android中的数据结构你该如何去选择?

    table = newTab; 可以看到当我们的table数组存储的节点值大于threshold时,会按我们的当前数组大小的两倍生成一个新的数组,并把旧数组上的数据复制到新数组上这就是我们的HashM ...

  7. 微慕插件二开wordpress微信小程序源码下载 星尘UI v1.3.1

    基于uni星茫多端开源小程序基础上升级优化下标题,圆角,阴影:简单且多彩,代码上语法更加简约,更兼容不同分辨率,更有细化的更新和优化,保留静态方便大神适配,星尘ui微信开源小程序wordpress小程 ...

  8. 【Android 系统开发】Android框架 与 源码结构

    一. Android 框架 Android框架层级 : Android 自下 而 上 分为 4层; -- Linux内核层; -- 各种库 和 Android运行环境层; -- 应用框架层; -- 应 ...

  9. 超好看的鬼刀画风扁平化粒子特效引导页HTML源码

    正文: 超好看的鬼刀画风扁平化粒子特效引导页HTML源码,给大家分享一个引导页页面吧,响应式布局的. 支持的功能有: 移动+PC 添加背景图片 美化高斯模糊 删除蒙版人物部分. 更图片人物画风更美好 ...

最新文章

  1. 独家 | 2020年22个广泛使用的数据科学与机器学习工具(附链接)
  2. 干货丨从线性回归到无监督学习,数据科学家需要掌握的十大统计技术
  3. 最老程序员创业札记:全文检索、数据挖掘、推荐引擎应用29
  4. 图论 用广搜搜邻接矩阵
  5. 【leetcode】 算法题1 两数之和
  6. autocad.net中ResultBuffer相关的常量值
  7. Nginx 图片防盗链
  8. opencv+python 霍夫圆检测原理
  9. python中函数的返回值
  10. thinkphp实现文件上传
  11. python网络爬虫项目——翻译英文单词
  12. 全国一级计算机考证软件
  13. iframe使用方法
  14. 研华服务器显示不了全屏啊,ppt不能全屏显示怎么办 ppt全屏显示不了的解决方法...
  15. 问卷调查:自定义表单设计vue
  16. 【教学类-06-01】测20以内加减法的最大数量(优化版 20220122 VS python 20以内加减法)
  17. 什么是物联网安全,为什么它很重要?
  18. Spring Cloud 微服务及相关技术总结
  19. 使用感受 2019-05-23
  20. revit二次开发 ExportContext

热门文章

  1. 数学图形之克莱因瓶(klein bottle)
  2. Cobbler 2.4.4 安装
  3. 解决Jenkins Email Extension Plugin发送邮件失败
  4. 【读书笔记】代码可为维护性标准(一)
  5. unittest单元测试框架之unittest案例(二)
  6. python 3389爆破机
  7. redmine + git
  8. Mysql-linux下密码修改,忘记密码修改,超级管理用户修改
  9. 利用cheat engine以及VC编写游戏修改器
  10. 也欢迎您访问我的个人主页http://www.april1985.com(原hesicong.com或april1985.com)