一、DroidPlugin的优点
- 宿主和插件完全隔离,插件不依赖宿主,可以独立安装运行
- 低入侵设计,插件不需要集成任何类,和正常的app是一样的
- 宿主程序集成DroidPlugin框架简单
- 支持四大组件,完全使用Android的API
二、DroidPlugin的缺点
- 插件启动速度慢
- 宿主只能调用插件为Launcher的Activity,宿主不能和插件中其他的Activity交互,也就是说插件是一个单独的模块,只能单一入口
三、集成DroidPlugin
- 下载DroidPlugin
下载地址:https://github.com/DroidPluginTeam/DroidPlugin
2. 创建一个新功能,新建一个plugin模块。(假设app为宿主工程,plugin为插件工程)
3. 导入DroidPlugin库
找到下载的DroidPlugin,将project\Libraries\DroidPlugin导入到工程中
4. 修改DroidPlugin中的build.gradle 文件
(原build.gradle文件)
(修改后build.gradle文件)
5. 修改DroidPlugin的AndroidManifest.xml文件,将所有的provide对应的authorities修改为自己的包名
因为AndroidManifest.xml文件中,provide对应的authorities使用的是变量,所以修改build.gradle文件即可,请参考tip2
6. 在宿主工程中添加DroidPlugin库。
四、集成中报错总结
错误一:
解决方案:
修改DroidPlugin中build.gradle文件的comileSdkVersion属性值,因为我app使用的28,所以也改为28
=================================
错误二:
解决方案:
修改DroidPlugin中build.gradle文件的buildToolsVersion 属性值,与app保持一致即可
=================================
错误三:
解决方案:
修改DroidPlugin中build.gradle文件,将“instrumentTest”属性改为“androidTest”
=================================
错误四:
解决方案:
将DroidPlugin中的AndroidManifext.xml中的minSDKVersion 去掉即可
=================================
错误五:
定位到DroidPlugin中build.gradle文件,删除一下语句:
=================================
错误六:
将compile关键字替换为implementation
=================================
错误七:
解决方案:
因为DroidPlugin使用的是lib文件夹,不是libs,所以将libs替换为lib,请参考tip1.
=================================
五、使用DroidPlugin插件
- 自定义Application,在onCreate 和attachBaseContext方法中添加如下代码:
1 public class PluginApplication extends Application { 2 @Override 3 public void onCreate() { 4 super.onCreate(); 5 //must be after super.onCreate() 6 PluginHelper.getInstance().applicationOnCreate(getBaseContext()); 7 } 8 9 @Override 10 protected void attachBaseContext(Context base) { 11 PluginHelper.getInstance().applicationAttachBaseContext(base); 12 super.attachBaseContext(base); 13 } 14 }
2. 开发宿主工程(app)
1 /** 2 * 安装插件的方法 3 * */ 4 private void installPlugin() { 5 // 获取插件 6 String path = Environment.getExternalStorageDirectory().toString() + "/plugins"; 7 File file = new File(path); 8 if(!file.exists()){ 9 file.mkdirs(); 10 } 11 plugins = file.listFiles(); 12 if(plugins == null || plugins.length == 0 ){ // 没有插件 13 Toast.makeText(this,"没有插件",Toast.LENGTH_SHORT).show(); 14 return; 15 } 16 // 安装第一个插件 17 try { 18 PluginManager.getInstance().installPackage(plugins[0].getAbsolutePath(), 19 PackageManagerCompat.INSTALL_REPLACE_EXISTING); 20 } catch (RemoteException e) { 21 e.printStackTrace(); 22 } 23 }
1 // 调用插件 2 findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() { 3 @Override 4 public void onClick(View v) { 5 PackageManager pm = getPackageManager(); 6 Intent intent = pm.getLaunchIntentForPackage("ayinger.plugin"); 7 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
8 startActivityForResult(intent,1); 9 10 } 11 });
注意:
- 上述方法为插件工程不安装,将插件工程直接放在了/storage/emulated/0/plugins目录下,调用installPlugin()来安装插件工程。
- 另外,还有一种是插件工程已经安装成功,直接,可省略installPlugin()步骤,直接调用插件,即可,这里注意一点,getLaunchIntentForPackage()方法 中,传入的参数是插件的applicationId 属性的值。
- 创建文件夹的时候,要注意检查是否授权,不然文件夹创建不成功。(入过坑)。
六、 其他
- 安装/更新
PluginManager.getInstance().installPackage(String packageName, int flags)
安装插件到插件系统中,packageName为插件apk路径,flags可以设置为0,如果要更新插件,则设置为PackageManagerCompat.INSTALL_REPLACE_EXISTING
2. 删除
PluginManager.getInstance().deletePackage(String packageName,int flags);
从插件系统中卸载某个插件,packageName: 需卸载插件包名, flags: 设置为0
3. 宿主和插件通信
1 // 宿主和插件如何互通SharedPreferences 2 try { 3 Context otherAppsContext = createPackageContext("HostPackageName", 4 Context.CONTEXT_IGNORE_SECURITY); 5 SharedPreferences sharedPreferences = otherAppsContext. 6 getSharedPreferences("test", Context.MODE_WORLD_READABLE); 7 if (sharedPreferences != null) { 8 String str1 = sharedPreferences.getString("key",null); 9 Toast.makeText(getApplicationContext(), "result: " + str, 10 Toast.LENGTH_SHORT).show(); 11 } 12 } catch (PackageManager.NameNotFoundException e) { 13 e.printStackTrace(); 14 }
七、 参考博客
https://blog.csdn.net/chaozhung_no_l/article/details/71439919
https://blog.csdn.net/wushipan/article/details/51577850