一、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