demo
如果要加载插件模块编译的apk插件包中的Activity类,需要执行如下流程:
1)加载类对象:使用DexClassLoader加载Activity对应的Class字节码类对象;
2)管理生命周期:处理加载进来的Activity类的生命周期,创建ProxyActivity,通过其生命周期回调,管理插件包中加载的未纳入应用管理的组件Activity类;
3)注入上下文:为加载进来的Activity类注入上下文;
4)加载资源:使用AssetManager将插件包apk中的资源主动加载进来。

app为宿主app
plugin 为插件app
plugin_core为插件化的核心加载文件
- //配置selinux权限:allow untrusted_app app_data_file:file { execute };
- public class MainActivity extends AppCompatActivity {
-
- @Override
-
- protected void onCreate(Bundle savedInstanceState) {
-
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.activity_main);
-
-
- if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
- && checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { //有存储权限
- // 路径为 /storage/emulated/0/Android/data/com.example.insert/files
- String path = getExternalFilesDir(null).getPath()+"/plugin-debug.apk"; //插件模块apk存放位置
-
- PluginManager.getInstance().loadPlugin( this, path); // 加载插件模块的apk文件
-
- } else { //没有存储权限,申请权限
-
- requestPermissions(new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
-
- }
-
- }
-
- public void onClick(View view) {
-
- //要跳转到plugin_core模块的代理Activity,首先要获取插件apk中的入口Activity类
-
- Intent intent = new Intent(this, ProxyActivity.class);
-
- ActivityInfo[] activityInfos = PluginManager.getInstance().getmPackageInfo().activities; // 获取插件apk中的Activity数组信息
-
- if (activityInfos.length > 0) { // 获取的插件包中的Activity不为空 , 才进行界面跳转
-
- // 取插件apk中的第1个Activity,次序就是在AndroidManifest.xml清单文件中定义Activity组件的次序,因此必须将Launcher Activity定义在第一个位置,不能在Launcher Activity之前定义Activity组件
-
- intent.putExtra("className", activityInfos[0].name); // 传入的是代理的目标组件的全类名,即插件apk中第一个activity的全类名
-
- startActivity(intent);
-
- }
-
- }
-
- }
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.insert">
-
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
- <application
- android:requestLegacyExternalStorage="true"
- 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/Theme.Insert">
- <activity
- android:name=".MainActivity"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
- </manifest>
定义一个Activity基类BaseActivity,继承AppCompatActivity并实现PluginActivityInterface接口。插件模块中的Activity类都继承该类,它是具体的Activity业务类的父类。
BaseActivity中涉及到的生命周期函数重复了,如AppCompatActivity中的onCreate()方法与PluginActivityInterface接口中的onCreate()方法是重复的,这里在每个方法前面加上@SuppressLint("MissingSuperCall")注解,忽略该报错。
- public class BaseActivity extends AppCompatActivity implements PluginActivityInterface {
-
- private Activity proxyActivity; //注入的Activity,代理该Activity类作为上下文
-
- @Override
-
- public void attach(Activity proxyActivity) {
-
- this.proxyActivity = proxyActivity; //注入代理Activity,在PluginActivity中将代理Activity组件注入进来
-
- }
-
- @Override
-
- public void setContentView(int layoutResID) {
-
- // 调用代理Activity中的setContentView方法
-
- if (proxyActivity != null) {
-
- proxyActivity.setContentView( layoutResID);
-
- }else{
-
- super.setContentView(layoutResID);
-
- }
-
- }
-
- @Override
-
- public void setContentView(View view) {
-
- if (proxyActivity != null) {
-
- proxyActivity.setContentView(view);
-
- }else{
-
- super.setContentView(view);
-
- }
-
- }
-
- @Override
-
- public
extends View> T findViewById(int id){ -
- if (proxyActivity != null) {
-
- return proxyActivity.findViewById(id);
-
- }else{
-
- return super.findViewById(id);
-
- }
-
- }
-
- @Override
-
- public void startActivity(Intent intent) {
-
- if (proxyActivity != null) {
-
- intent.putExtra("className", intent.getComponent().getClassName());
-
- proxyActivity.startActivity(intent);
-
- }else{
-
- super.startActivity(intent);
-
- }
-
- }
-
- @SuppressLint("MissingSuperCall")
-
- @Override
-
- public void onCreate(Bundle savedInstanceState) {
-
- }
-
- @SuppressLint("MissingSuperCall")
-
- @Override
-
- public void onStart() {
-
- }
-
- @SuppressLint("MissingSuperCall")
-
- @Override
-
- public void onResume() {
-
- }
-
- @SuppressLint("MissingSuperCall")
-
- @Override
-
- public void onPause() {
-
- }
-
- @SuppressLint("MissingSuperCall")
-
- @Override
-
- public void onStop() {
-
- }
-
- @SuppressLint("MissingSuperCall")
-
- @Override
-
- public void onDestroy() {
-
- }
-
- @SuppressLint("MissingSuperCall")
-
- @Override
-
- public void onSaveInstanceState(Bundle outState) {
-
- }
-
- }
- public interface PluginActivityInterface {
- //绑定代理Activity
-
- void attach(Activity proxyActivity);
-
- void onCreate(Bundle savedInstanceState);
-
- void onStart();
-
- void onResume();
-
- void onPause();
-
- void onStop();
-
- void onDestroy();
-
- void onSaveInstanceState(Bundle outState);
-
- boolean onTouchEvent(MotionEvent event);
-
- void onBackPressed();
-
- }
- public class PluginManager {
-
- private DexClassLoader mDexClassLoader;//类加载器,用于加载插件包apk中的classes.dex文件中的字节码对象
-
- private Resources mResources; //从插件包apk中加载的资源
-
- private PackageInfo mPackageInfo; //插件包信息类
-
- private Context mContext; //加载插件的上下文对象
-
- private static PluginManager instance;
-
- //获取单例类
-
- public static PluginManager getInstance(){
-
- if (instance == null) {
-
- instance = new PluginManager();
-
- }
-
- return instance;
-
- }
-
- // 加载插件,参数分别为加载插件的app的上下文、加载的插件包地址
-
- public void loadPlugin(Context context, String loadPath) {
-
- this.mContext = context;
-
- //DexClassLoader的optimizedDirectory目录必须是私有,即模式为Context.MODE_PRIVATE
-
- File optimizedDirectory = context.getDir( "cache_plugin", Context.MODE_PRIVATE);
- Log.e("TAG",loadPath);
-
- mDexClassLoader = new DexClassLoader( loadPath, optimizedDirectory.getAbsolutePath(), null, context.getClassLoader()); // 创建类加载器
-
- // 加载资源
-
- try {
-
- AssetManager assetManager = AssetManager.class.newInstance(); //通过反射创建AssetManager
-
- Method addAssetPathMethod = assetManager.getClass().getDeclaredMethod("addAssetPath", String.class); //通过反射获取AssetManager中的addAssetPath隐藏方法
-
- addAssetPathMethod.invoke( assetManager, loadPath); //调用反射方法
-
- mResources = new Resources( assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration()); //获取资源
-
- mPackageInfo = context.getPackageManager().getPackageArchiveInfo(loadPath, PackageManager.GET_ACTIVITIES); // 获取插件包中的Activity类信息
-
- } catch (Exception e) {
- e.printStackTrace();
-
- }
-
- }
-
- //获取类加载器
-
- public DexClassLoader getmDexClassLoader(){
-
- return mDexClassLoader;
-
- }
-
- //获取插件包中的Package信息
-
- public PackageInfo getmPackageInfo() {
-
- return mPackageInfo;
-
- }
-
- //获取插件包中的资源
-
- public Resources getmResources() {
-
- return mResources;
-
- }
-
- }
在宿主apk中需要跳转到插件apk时,首先创建一个跳转到ProxyActivity(在核心库里)的intent,在intent中加上插件apk的入口activity。这样ProxyActivity作为代理Activity ,它持有从插件apk中加载的PluginActivity类对象。
ProxyActivity是空的Activity,没有任何实际的业务逻辑,只是作为一个生命周期的转接代理接口。但是ProxyActivity有着完整的生命周期回调机制,在进入该界面时会回调onCreate、onStart、onResume生命周期方法,在退出该界面时会回调 onPause、onStop、onDestroy生命周期方法。因此只需要在ProxyActivity的生命周期方法中,调用PluginActivity相应的生命周期方法即可。而且ProxyActivity运行时会有上下文,PluginActivity使用上下文时调用ProxyActivity的上下文。
注意:插件模块的包名可以和宿主的包名一样也可以不一样。当插件apk的包名和宿主一样时,要注意插件apk里的Activity名字一定不要和宿主apk中Activity的名字一样,否则包名和Activity名都一样就无法跳转到插件Activity了。
- //该Activity只是个空壳,主要用于持有从插件apk加载的Activity类,并在ProxyActivity生命周期方法中调用对应PluginActivity类的生命周期方法
-
- public class ProxyActivity extends Activity {
-
- private String className = ""; //被代理的目标Activity组件的全类名
-
- private PluginActivityInterface pluginActivity;//插件包中的Activity界面组件
-
- @Override
-
- protected void onCreate(Bundle savedInstanceState) {
-
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.activity_proxy);
-
- className = getIntent().getStringExtra("className"); //得到要代理的目标组件的全类名,即插件apk中的入口activity的全类名
-
- try {
-
- Class> clazz = getClassLoader().loadClass(
- className); //使用类加载器加载插件中的界面组件。注意这里类加载器必须是PluginManager中的类加载器,即getClassLoader()是重写的方法,该方法返回PluginManager中的类加载器
-
- Activity activity = (Activity) clazz.newInstance(); //使用反射创建插件apk中的Activity
-
- if (activity instanceof PluginActivityInterface) { // 判断Activity组件是否是PluginActivityInterface接口类型的
-
- this.pluginActivity =
- (PluginActivityInterface) activity; // 如果是PluginActivityInterface类型 , 则强转为该类型
-
- pluginActivity.attach(
- this); // 上下文注入。将ProxyActivity绑定注入到插件包的PluginActivity类中。该ProxyActivity具有运行的上下文,一旦绑定注入成功,则被代理的PluginActivity也具有了上下文
-
- pluginActivity.onCreate(savedInstanceState); //调用pluginActivity的onCreate生命周期
-
- }
-
- } catch (Exception e) {
-
- e.printStackTrace();
-
- }
-
- }
-
- @Override
-
- protected void onStart() {
-
- super.onStart();
-
- pluginActivity.onStart();
-
- }
-
- @Override
-
- protected void onResume() {
-
- super.onResume();
-
- pluginActivity.onResume();
-
- }
-
- @Override
-
- protected void onPause() {
-
- super.onPause();
-
- pluginActivity.onPause();
-
- }
-
- @Override
-
- protected void onStop() {
-
- super.onStop();
-
- pluginActivity.onStop();
-
- }
-
- @Override
-
- protected void onDestroy() {
-
- super.onDestroy();
-
- pluginActivity.onDestroy();
-
- }
-
- @Override
-
- protected void onSaveInstanceState(Bundle outState) {
-
- super.onSaveInstanceState(outState);
-
- pluginActivity.onSaveInstanceState(outState);
-
- }
-
- @Override
-
- public boolean onTouchEvent(MotionEvent event) {
-
- super.onTouchEvent(event);
-
- return pluginActivity.onTouchEvent(event);
-
- }
-
- @Override
-
- public void onBackPressed() {
-
- //super.onBackPressed();
-
- //pluginActivity.onBackPressed();
-
- finish();
-
- }
-
- //重写getClassLoader方法,因为这里需要使用插件包加载的类加载器
-
- @Override
-
- public ClassLoader getClassLoader() {
-
- return PluginManager.getInstance().getmDexClassLoader();
-
- }
-
- //重写getResources方法,因为这里需要使用插件包加载的资源
-
- @Override
-
- public Resources getResources() {
-
- return PluginManager.getInstance().getmResources();
-
- }
- }
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.plugin_core">
- <application>
- <activity android:name=".ProxyActivity"/>
- </application>
-
- </manifest>
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.plugin">
-
- <application
- 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/Theme.Insert">
- <activity
- android:name=".PluginActivity"
- android:exported="true">
- </activity>
- </application>
-
- </manifest>
所有的插件包中的Activity都要继承 BaseActivity。
这样写的目的是为了方便在代理Activity中可以随意调用插件apk中的Activity类的生命周期函数,这些生命周期函数都是protected方法,不能直接调用,否则每个方法调用时,还要先反射修改访问性,才能调用
- public class PluginActivity extends BaseActivity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
- }
代码完成,接下来看如何运行:
①首先编译插件模块,生成插件apk安装包:
②拷贝插件apk
将编译的插件包apk拷贝到 /storage/emulated/0/Android/data/com.example.insert/files目录中。在 " Device FIle Explorer " 面板中 , 右键点击/storage/emulated/0/DCIM目录 , 点击"Upload " 即可将apk上传到指定位置。
③运行宿主模块,在onCreate方法中就会加载解析插件apk,解析dex文件与资源文件。此时点击跳转按钮,即可跳转到插件模块Activity中。