架构之占位式插件化框架 --组件通信
文章目录
- Activity通信
- 通过宿主来加载Plugin Activity
- 1. 环境准备
- 2. 加载
- 3. 启动插件
- 4. 插件Activity的生命周期
- 插件内部 Activity 加载
- Service通信
- 动态广播的使用
占位式,也叫插装式。运行的APP,也称之为宿主。
Activity通信
通过宿主来加载Plugin Activity
1. 环境准备
项目分为3个基础模块,分别是 宿主module(可以启动),插件module(最终会打包成单独apk文件),标准module(Android Library)。宿主和插件分别依赖标准
- 新建 插件module:
plugin_package
,该module只是为打包成apk文件。 - 新建 标准module:
standard
,该module是为了维护宿主和插件。是一个activity library - 添加依赖关系,宿主和插件分别依赖
标准stander
标准制定(组件的管理):
- 制定标准
/*** standard 模块* 制定组件Activity的标准*/ public interface ActivityInterface {/*** 把宿主的环境给插件* @param appActivity*/void insertAppActivity(Activity appActivity);void onCreate(Bundle savedInstanceState);void onResume();void onPause();void onStop();void onDestroy();}
- 插件的组件实现该标准
/*** 插件 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思路一致,具体查看代码。
动态广播的使用
架构之占位式插件化框架 --组件通信相关推荐
- 【Android 插件化】“ 插桩式 “ 插件化框架 ( 代理 Activity 组件开发 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】Hook 插件化框架 ( 使用 Hook 方式替换插件 Activity 的 mResources 成员变量 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】Hook 插件化框架 ( 加载插件包资源 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
最新文章
- oracle自动化,Oracle 自动化备份脚本
- Python 初学者进阶的九大技能(附代码)
- 微信小游戏开发教程-游戏实现3
- jQuery UI Widget(1.8.1)工作原理--转载
- boost::mpl::aux::largest_int相关用法的测试程序
- 从零开始学习OpenCL开发(一)架构
- MyBatis笔记——配置文件完成增删改查
- 牛客网--2019校招--瞌睡
- 常见分数值归一化方法
- Java 语言实现的 I/O 模型
- 蓝牙协议栈中的 OSAL
- php获取表单ip,PHP获取用户IP代码实现
- 孩子数学成绩不好怎么办_我孩子数学成绩不好,怎么办
- php滑动轮播效果,js实现移动端手指滑动轮播图效果
- Java设计模式——代理模式实现及原理
- 迪杰斯特拉模板-刘汝佳紫书
- 计算机电子电路原理图,学看电路原理图入门知识积累 - 全文
- Linux学习第一周作业。
- zooKeeper篇-zk的选举机制
- 计算机上的按键名有哪些,电脑键盘上各个按键名称与功能作用