Android(安卓)插件化框架DroidPlugin
上一次项目迭代中,接触到了插件化框架。
使用场景:我们的app需要集成某一直播app。即在不安装第三方直播app到手机的情况下,点击我们app内部的某一连接能跳转到直播app中,运行里面原有的所有功能。
原理:也就是通过插件化框架,把第三方直播app的apk文件包下载到手机本地,然后在我们的app中,使用插件化框架解压第三方app的代码到我们app的安装目录下,使用classloader加载字节码到另一个进程中,与我们的app进程进行通讯。或许不太对,是个大概的思路。总之,好处就是,不用强制安装第三方app到手机桌面上,可使用第三方app的大部分功能,下载时会一起卸载第三方app的所有文件。
360插件化框架DroidPlugin地址
下面开始集成步骤:
一、下载插件代码,把library以module的形式加入到项目中:
解压下载 的源码,复制..\DroidPlugin-master\project\Libraries\DroidPlugin目录到项目的根目录,如图:
在主工程的settings.gradle文件中添加DroidPlugin库,如图:
编译程序。
二、修改插件内部配置:
如果上一步编译不通过,很正常,可能有些地方需要修改。先说插件内部:
把图中这些lib统一一下名称,都改成libs吧。
还是刚才那个文件,改成你的包名。现在应该可以编译通过了。
将插件中DroidPlugin/AndroidManifest.xml中的所有provider对应的authorities修改成自己的,其实改成如下即可:
android:authorities="${authorityName}_P01" , 使用${authorityName}变量,去取build.gradle中的值,便于迁移到其他项目中
并且修改PluginManager.STUB_AUTHORITY_NAME 为你的值:
PluginManager.STUB_AUTHORITY_NAME="com.example.droidplugin_stub"
如果发现程序中是这样的,就不用改了:
public static final String STUB_AUTHORITY_NAME = BuildConfig.AUTHORITY_NAME;
三、修改项目配置:
在自定义的application中相应的方法添加插件初始化代码:
@Override public void onCreate() { super.onCreate(); //这里必须在super.onCreate方法之后,顺序不能变 PluginHelper.getInstance().applicationOnCreate(getBaseContext()); } @Override protected void attachBaseContext(Context base) { PluginHelper.getInstance().applicationAttachBaseContext(base); super.attachBaseContext(base); }
差不多了。
四、功能代码(官网原话):
1、安装、更新插件,使用如下方法:
int PluginManager.getInstance().installPackage(String filepath, int flags)
说明:安装插件到插件系统中,filepath为插件apk路径,flags可以设置为0,如果要更新插件,则设置为PackageManagerCompat.INSTALL_REPLACE_EXISTING返回值及其含义请参见PackageManagerCompat类中的相关字段。
2、卸载插件,使用如下方法:
int PluginManager.getInstance().deletePackage(String packageName,int flags);
说明:从插件系统中卸载某个插件,packageName传插件包名即可,flags传0。
3、启动插件:启动插件的Activity、Service等都和你启动一个以及安装在系统中的app一样,使用系统提供的相关API即可。组件间通讯也是如此。
知道这么回事后,于是整理代码,干成一个工具类:
/** * @Author: duke * @DateTime: 2017-05-30 21:40 * @Description: 插件化安装工具类 */public class PluginUtils { /** * 启动插件 * * @param activity * @param packageName */ public static void startActivity(Activity activity, String packageName) { PackageManager pm = activity.getPackageManager(); Intent intent = pm.getLaunchIntentForPackage(packageName); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); activity.startActivity(intent); } /** * 删除apk * * @param activity * @param packageName */ public static void unInstallApk(Activity activity, String packageName) { if (!PluginManager.getInstance().isConnected()) { Toast.makeText(activity, "服务未连接", Toast.LENGTH_SHORT).show(); } else { try { PluginManager.getInstance().deletePackage(packageName, 0); Toast.makeText(activity, "删除完成", Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } } } /** * 应该在线程中安装,此方法仅供测试 * * @param context * @param apkPathAndName * @param packageName */ public static boolean installApk(Context context, String apkPathAndName, String packageName) { if (!PluginManager.getInstance().isConnected()) { //installTips(context,"插件服务正在初始化,请稍后再试。。。"); return false; } try { if (PluginManager.getInstance().getPackageInfo(packageName, 0) != null) { //installTips(context,"已经安装了,不能再安装"); return true; } else { //如果需要更新插件,则flag 设置为 PackageManagerCompat.INSTALL_REPLACE_EXISTING //int returnCode = PluginManager.getInstance().installPackage(filepath, PackageManagerCompat.INSTALL_REPLACE_EXISTING); int returnCode = PluginManager.getInstance().installPackage(apkPathAndName, 0); if (returnCode == PluginManager.INSTALL_FAILED_NO_REQUESTEDPERMISSION) { //安装失败,文件请求的权限太多 //installTips(context,"安装失败,文件请求的权限太多"); } else { //安装完成 //installTips(context,"安装完成"); return true; } } } catch (Exception e) { e.printStackTrace(); } return false; }}
五、具体场景使用:
剩下的就是实战了。在没有安装目标app的前提下,如果你想在你的app中,点击按钮跳转到目标app的某页面。
1、click事件跳转时,你的判断插件有没有安装。当然了,某次插件安装成功后,你得做标记了。判断是也就是拿这个标记判断了。工具类中提供检查插件包是否安装的方法。
2、如果没有安装,则启动子线程安装了。肯定,你的下载第三方包到手机存储的某个位置,不然怎么办。
boolean isInstall = PluginUtils.installApk(weakReference.get(), basePath + "/a.apk", packagename);
可能是耗时操作,需要异步。需要知道第三方app下载到的位置,需要知道第三方app的包名。需要私下协商告知的,不然两个公司怎么合作? 3、跳转进入第三方app:
@Override public void onClick(View v) { if (v.getId() == R.id.jumpBtn) { PluginUtils.startActivity(PluginTestActivity.this, packagename); } }
/** * 启动插件 * * @param activity * @param packageName */ public static void startActivity(Activity activity, String packageName) { PackageManager pm = activity.getPackageManager(); Intent intent = pm.getLaunchIntentForPackage(packageName); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); activity.startActivity(intent); }
必须知道第三方app的包名。还有,如果不是launch模式的intent的话,或者说启动的不是第三方app的启动页时,怎么办?
一样的,跟app内部跳转一样,或者给一个特定的action,或者知道第三方app某Activity的具体路径,即可跳转了。
最后,还有一个混淆配置,坑爹的货:
##----360插件-----------------------------------------keep class com.morgoo.droidplugin.**{*;}
没有上传真个项目,单独把配置好的插件部分上传了
下载地址:http://download.csdn.net/download/fesdgasdgasdg/9873964
更多相关文章
- Android插件化开发之OpenAtlas插件的安装与卸载、更新与回滚
- Android:Service生命周期方法与Service启动方式bindService与Star
- HP TouchPad 灵魂不死,Android(安卓)附身且带 APK 应用安装
- 电脑怎么安装安卓系统?安卓(Android)x86 4.4安装方法图文步骤
- Android(安卓):为你的启动页面SplashActivity 添加动画的几种方法
- 关于android创建快捷方式会启动两个应用的问题(二)
- Qt for Android(安卓)自定义启动页(解决启动页拉伸的问题)
- Android(安卓)App静默安装的解决方案
- [置顶] Ionic项目打包Android版本实战