文章目录

  • Activity通信
    • 通过宿主来加载Plugin Activity
      • 1. 环境准备
      • 2. 加载
      • 3. 启动插件
      • 4. 插件Activity的生命周期
    • 插件内部 Activity 加载
  • Service通信
  • 动态广播的使用

占位式,也叫插装式。运行的APP,也称之为宿主。

Activity通信

通过宿主来加载Plugin Activity

1. 环境准备

项目分为3个基础模块,分别是 宿主module(可以启动),插件module(最终会打包成单独apk文件),标准module(Android Library)。宿主和插件分别依赖标准

  1. 新建 插件module: plugin_package,该module只是为打包成apk文件。
  2. 新建 标准module:standard,该module是为了维护宿主和插件。是一个activity library
  3. 添加依赖关系,宿主和插件分别依赖标准stander

标准制定(组件的管理):

  1. 制定标准

    /*** standard 模块* 制定组件Activity的标准*/
    public interface ActivityInterface {/*** 把宿主的环境给插件* @param appActivity*/void insertAppActivity(Activity appActivity);void onCreate(Bundle savedInstanceState);void onResume();void onPause();void onStop();void onDestroy();}
    
  2. 插件的组件实现该标准
    /*** 插件 module 中的 Activity组件基类*/
    public class BaseActivity extends Activity implements ActivityInterface {protected Activity appActivity; //宿主的 Activity环境@Overridepublic void insertAppActivity(Activity appActivity) {this.appActivity = appActivity;}@SuppressLint("MissingSuperCall")@Overridepublic void onCreate(Bundle savedInstanceState) {}@SuppressLint("MissingSuperCall")@Overridepublic void onResume() {}@SuppressLint("MissingSuperCall")@Overridepublic void onPause() {}@SuppressLint("MissingSuperCall")@Overridepublic void onStop() {}@SuppressLint("MissingSuperCall")@Overridepublic void onDestroy() {}
    }

插件的特点:插件没有组件环境,可以将宿主的组件环境给插件,可以通过标准传递给插件
加载插件:①加载插件中的Activity组件,②加载插件中的资源文件

2. 加载

在宿主APP,通过用户触发加载插件apk文件;而APK文件通过服务器下发,保存在本地存储。
加载包含两部分:①加载插件的Class ②加载插件的layout

/*** 加载插件:* 1. 加载activity* 2.加载layout*/public void loadPlugin() {try {// 1. 加载plugin classFile file = getPluginPath();if (!file.exists()) {Log.e(TAG, "loadPlugin: 插件不存在 ");return;}String pluginPath = file.getAbsolutePath();// dexClassLoader需要一个缓存目录// getDir 生成路径: /data/data/当前应用包名/pDir。String optimizedDirectory = mContext.getDir("pDir", Context.MODE_PRIVATE).getAbsolutePath();/*** 加载class* @param String dexPath,  插件路径* @param String optimizedDirectory,  加载插件apk 需要一个缓存目录* @param String librarySearchPath, 底层c/c++ 库的目录* @param ClassLoader parent ClassLoader*/mDexClassLoader = new DexClassLoader(pluginPath, optimizedDirectory, null, mContext.getClassLoader());// 2. 加载plugin layout////// public final class AssetManager 不能直接new  需要通过反射调用AssetManager assetManager = AssetManager.class.newInstance();// 执行 android.content.res.AssetManager#addAssetPath ,将插件包的路径添加进去,加载resource  可以加载zip apk文件/*** @param parameterTypes 不是真正的类型,而是类类型*/Method addAssetPathMethod = assetManager.getClass().getMethod("addAssetPath", String.class);///*** @param obj 执行的方法所对应的类对象* @param args 执行方法的参数*/addAssetPathMethod.invoke(assetManager,pluginPath);// 至此,assetManager就可以加载资源文件/////***  @param assets  资源管理类*  @param metrics 资源的配置信息  对应不同的资源分辨率*  @param config*/Resources res = mContext.getResources(); // 宿主资源配置信息mResources = new Resources(assetManager, res.getDisplayMetrics(), res.getConfiguration());} catch (Exception e) {e.printStackTrace();}}

3. 启动插件

Activity启动采用任务栈的启动,由于插件Activity没有安装是不能启动的,采用代理Activity启动插件Activity

宿主Activity中,触发启动插件。

    // 启动插件里面的Activitypublic void startPluginActivity(View view) {PackageManager packageManager = getPackageManager();String pluginPath = PluginManager.getPluginPath().getAbsolutePath();PackageInfo packageInfo = packageManager.getPackageArchiveInfo(pluginPath, PackageManager.GET_ACTIVITIES);
//        for (int i = 0; i < packageInfo.activities.length; i++) {
//            Log.e(TAG, "packageInfo.activities:" + packageInfo.activities[i]);
//        }// 拿到第一个activityActivityInfo activityInfo = packageInfo.activities[0];Intent intent = new Intent(this, ProxyActivity.class);intent.putExtra("className", activityInfo.name);startActivity(intent);}

在代理Activity中做真正的启动,首先要将宿主的环境注入到插件

@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);String className = getIntent().getStringExtra("className");//真正的加载 插件里面的Activity// 动态获取className , 不能写死(包名+类名)// String className = null;try {// 拿到插件包中的第一个ActivityClass pluginClass = getClassLoader().loadClass(className);// 实例化插件包中的ActivityConstructor constructor = pluginClass.getConstructor(new Class[]{});Object pluginActivity = constructor.newInstance(new Object[]{});// 强转为ActivityInterfaceactivityInterface = (ActivityInterface) pluginActivity;// 将宿主的环境注入给插件activityInterface.insertAppActivity(this);// 执行插件onCreate 方法// 可以从宿主 携带参数 给 插件Bundle bundle = new Bundle();bundle.putString("fromAppInfo", "我是来自宿主的一条信息");// 间接调用pluginActivity的onCreate方法activityInterface.onCreate(bundle);} catch (Exception e) {e.printStackTrace();}}

插件Activity 的onCreate方法:

 // com.purang.plugin_package.PluginMainActivity#onCreate@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 在父类BaseActivity 通过 宿主环境 appActivity 来加载 ResourcesetContentView(R.layout.activity_main_plugin);Log.e(TAG, "onCreate: ");// toast 不能通过this,只能通过宿主传递过来的appActivity 环境Toast.makeText(appActivity, "我是插件", Toast.LENGTH_SHORT).show();// 子类复写了父类的onCreate方法, 继续调用onCreateString info = savedInstanceState.getString("fromAppInfo");Log.e(TAG, "子类收到宿主传递的信息: " + info);}

4. 插件Activity的生命周期

在 宿主的ProxyActivity中,通过activityInterface.onCreate(bundle);启动pluginActivity。因为插件的Activity实现了这一标准。所以,插件的Activity的生命周期也可以通过这种方式来加载:

/*** 代理/占位Activity  用来启动插件 Activity*/
public class ProxyActivity extends Activity {private ActivityInterface activityInterface;@Overrideprotected void onStart() {super.onStart();if (activityInterface != null)activityInterface.onStart();}@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);// ... 省略代码// 强转为ActivityInterfaceactivityInterface = (ActivityInterface) pluginActivity;   activityInterface.onCreate(bundle);// ... 省略代码}@Overrideprotected void onResume() {super.onResume();if (activityInterface != null)activityInterface.onResume();}@Overrideprotected void onPause() {super.onPause();if (activityInterface != null)activityInterface.onPause();}
}

插件PluginMainActivity代码:

public class PluginMainActivity extends BaseActivity {private static final String TAG = PluginMainActivity.class.getSimpleName();@Overridepublic void onStart() {super.onStart();Log.e(TAG, "onStart: ");}@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 在父类BaseActivity 通过 宿主环境 appActivity 来加载 ResourcesetContentView(R.layout.activity_main_plugin);Log.e(TAG, "onCreate: ");}@Overridepublic void onResume() {super.onResume();Log.e(TAG, "onResume:");}@Overridepublic void onPause() {super.onPause();Log.e(TAG, "onPause:");}
}

日志输出:

2020-02-22 22:00:05.065 7863-7863/com.purang.plugin E/PluginMainActivity: onCreate:
2020-02-22 22:00:05.072 7863-7863/com.purang.plugin E/PluginMainActivity: 子类收到宿主传递的信息: 我是来自宿主的一条信息
2020-02-22 22:00:05.074 7863-7863/com.purang.plugin E/PluginMainActivity: onStart:
2020-02-22 22:00:05.075 7863-7863/com.purang.plugin E/PluginMainActivity: onResume:
2020-02-22 22:00:27.941 7863-7863/com.purang.plugin E/PluginMainActivity: onPause:

可以看出,先调用了onCreate来加载Activity,然后才是onStart onResume

插件内部 Activity 加载

在插件内部实现跳转到新的Activity实现分析:
由于是在插件内部没有上下文环境,所以所有涉及到上下文环境的操作,都必须借助宿主 ProxyActivity注入给插件的上下文 appActivity来实现。如:findViewById startActivity等等。Activity跳转桥接到ProxyActivity,在ProxyActivity内部实现自己跳转自己

查看Activity task stack

    Running activities (most recent first):TaskRecord{459ab29 #11615 A=com.purang.plugin U=0 StackId=1967 sz=3}Run #2: ActivityRecord{4528a48 u0 com.purang.plugin/.ProxyActivity t11615}Run #1: ActivityRecord{40d3487 u0 com.purang.plugin/.ProxyActivity t11615}Run #0: ActivityRecord{43aa009 u0 com.purang.plugin/.MainActivity t11615}

改变ProxyActivity launchMode,尝试采用Activity的四种启动模式,查看任务栈及生命周期方法。具体分析查看

Service通信

service实现思路与activity思路一致,具体查看代码。

动态广播的使用

架构之占位式插件化框架 --组件通信相关推荐

  1. 【Android 插件化】“ 插桩式 “ 插件化框架 ( 代理 Activity 组件开发 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  2. 【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  3. 【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  4. 【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  5. 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  6. 【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  7. 【Android 插件化】Hook 插件化框架 ( 使用 Hook 方式替换插件 Activity 的 mResources 成员变量 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

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

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  9. 【Android 插件化】Hook 插件化框架 ( 加载插件包资源 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

最新文章

  1. oracle自动化,Oracle 自动化备份脚本
  2. Python 初学者进阶的九大技能(附代码)
  3. 微信小游戏开发教程-游戏实现3
  4. jQuery UI Widget(1.8.1)工作原理--转载
  5. boost::mpl::aux::largest_int相关用法的测试程序
  6. 从零开始学习OpenCL开发(一)架构
  7. MyBatis笔记——配置文件完成增删改查
  8. 牛客网--2019校招--瞌睡
  9. 常见分数值归一化方法
  10. Java 语言实现的 I/O 模型
  11. 蓝牙协议栈中的 OSAL
  12. php获取表单ip,PHP获取用户IP代码实现
  13. 孩子数学成绩不好怎么办_我孩子数学成绩不好,怎么办
  14. php滑动轮播效果,js实现移动端手指滑动轮播图效果
  15. Java设计模式——代理模式实现及原理
  16. 迪杰斯特拉模板-刘汝佳紫书
  17. 计算机电子电路原理图,学看电路原理图入门知识积累 - 全文
  18. Linux学习第一周作业。
  19. zooKeeper篇-zk的选举机制
  20. 计算机上的按键名有哪些,电脑键盘上各个按键名称与功能作用

热门文章

  1. MyBatis12 结果集映射
  2. 【小迪安全】Day04基础入门-30 余种加密编码进制Web数据库系统代码参数值
  3. 利用Metasploit控制目标电脑(Win7_x64系统)
  4. 曾因“贿赂”苹果被罚款 10.3 亿美元,高通上诉成功
  5. 颜色空间转换及切割车牌(python)
  6. 抖音流量密码时间段,一天中这个时候发推荐几率大
  7. 统计学习导论之R语言应用(四):分类算法R语言代码实战
  8. cocoscreator的游戏背景适配方案
  9. 如何ping通github
  10. 抖音橱窗等级被降低了是什么原因造成的?怎么办?