1 简介

Android大型项目中为了减小apk的体积,可以采用插件化的方法,即一些不常用的功能独立成插件,当用户需要的使用的时候再从服务器上下载回来,动态加载。这样就避免了为了满足所有用户需求而把功能全部打包到apk,导致apk体积的膨胀。所谓的插件,其实也是一个apk,但是一般都依赖正式对外发布的app,也叫宿主。本篇不讨论插件化的原理和实现难点,只介绍怎么使用以及优缺点。
Android插件化常用实现方案有两种:

(1) DynamicLoadApk
(2) DroidPlugin

这两个是目前比较主流的Android插件化实现方案,在Github的星星数很高,两者的Github地址如下:

DynamicLoadApk的Github地址:https://github.com/singwhatiwanna/dynamic-load-apk
DroidPlugin的Github地址:https://github.com/Qihoo360/DroidPlugin

2 特点

(1) DynamicLoadApk是由团队维护的,但是目前已经很长时间没有更新了,途牛用的就是这个插件化框架
优点:

  • 插件不依赖宿主,对宿主开发者透明,提供三种依赖方式
  • 宿主和插件可以频繁交互,启动时间短

缺点:

  • 插件apk必须实现DLBasePluginActivity,属于侵入式的,以及不支持service
  • 宿主调用插件和插件内部的相互调用都要使用DL提供的方法,而不能使用Android原生的api,例如:启动Activity
  • 插件开发有一套规定,因此造成插件开发门槛高,学习成本高

(2) DroidPlugin是360公司开源的一个框架,已经在360手机助手上使用
优点:

  • 宿主和插件完全隔离,插件不依赖宿主,可以独立安装运行
  • 低入侵设计,插件不需要继承任何类
  • 插件apk和普通apk一样的,所以插件开发没有门槛
  • 有大公司维护,有360手机助手这样的商用app在使用

缺点:

  • 插件启动速度太慢,而且宿主只能调用插件的LaunchMode的Activity,不能调用其他Activity

3 使用方法

(1) 导入Dynamic-load-apk中的lib。
下载Dynamic-load-apk后解压,在Android Studio中新建工程DLTest(自己命名) –> new –> import module –>选择lib所在的目录:dynamic-load-apk-master\DynamicLoadApk\lib

(2) 新建插件模块plugin,宿主模块host,这两个模块都是application, 最后都要生成apk的。项目目录如下:

编译lib模块,命令是build菜单–>make module lib,目的是为了获得生成的jar文件,jar文件所在位置是lib\build\intermediates\bundles\debug\class.jar,复制jar文件重命名为lib.jar

(3) 导入lib.jar到plugin项目的libs目录下,开发plugin项目,注意Activity要继承DLBasePluginActivity ,R.layout.activity_test上就一个TextView,显示”这个界面来自Plugin”
插件项目Plugin的MainActivity:

public class MainActivity extends DLBasePluginActivity {    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_test);    }}

注意:plugin模块的buidle.gradle需要修改为如下:

dependencies {    provided fileTree(dir: 'libs', include: ['*.jar'])    .........}

provided 意思是编译时候使用,但不打包到APK中,这样做是因为我们的宿主项目host中已经包含了lib.jar,如果插件中也包含的话就会报找不到plugin中的Activity的错,原因是两个包重复,必须要用host中的DL框架来加载plugin,而不是plugin自带的DL框架

(4) 导入lib.jar到host项目的libs目录下,开发host项目
host项目的MainActivity:

public class MainActivity extends Activity {    private Button btnTest;    private TextView tvTip;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        this.btnTest = (Button) findViewById(R.id.btn_test);        this.tvTip = (TextView) findViewById(R.id.tv_tip);        this.init();    }    //初始化    private void init() {        //获取插件        String pluginFolder = "/mnt/sdcard/DynamicLoadHost";        File file = new File(pluginFolder);        File[] plugins = file.listFiles();        //判断有没有插件        if (plugins == null || plugins.length == 0) {            this.tvTip.setVisibility(View.VISIBLE);            return;        }        //调用第一个插件        File plugin = plugins[0];        final PluginItem item = new PluginItem();        item.pluginPath = plugin.getAbsolutePath();        item.packageInfo = DLUtils.getPackageInfo(this, item.pluginPath);        //获取插件的启动Activity的名称        if (item.packageInfo.activities != null && item.packageInfo.activities.length > 0) {            item.launcherActivityName = item.packageInfo.activities[0].name;        }        //获取插件启动Service的名称        if (item.packageInfo.services != null && item.packageInfo.services.length > 0) {            item.launcherServiceName = item.packageInfo.services[0].name;        }        //显示插件        tvTip.setText("检测到一个插件:" + item.pluginPath);        //加载插件        DLPluginManager.getInstance(this).loadApk(item.pluginPath);        //添加监听器        this.btnTest.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //提示                Toast.makeText(getApplicationContext(), "开始调用插件", Toast.LENGTH_SHORT).show();                //调用插件                usePlugin(item);            }        });    }    //调用插件    private void usePlugin(PluginItem pluginItem) {        DLPluginManager pluginManager = DLPluginManager.getInstance(this);        pluginManager.startPluginActivity(this, new DLIntent(pluginItem.packageInfo.packageName, pluginItem.launcherActivityName));    }    //插件Bean    public static class PluginItem {        public PackageInfo packageInfo;        public String pluginPath;        public String launcherActivityName;        public String launcherServiceName;        public PluginItem() {        }    }}

host的activity_main.xml:

<RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" >    <TextView  android:id="@+id/tv_tip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="没有检测到插件" />    <Button  android:id="@+id/btn_test" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/tv_tip" android:layout_marginTop="10dp" android:text="测试调用插件" /></RelativeLayout>

注意:Host的AndroidManifest.xml中需要额外的声明几个DL框架中的类,否则运行时候找不到Activity.

Host的AndroidManifest.xml:

<manifest  package="com.host" xmlns:android="http://schemas.android.com/apk/res/android">    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>    <application  android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme">        <activity  android:name=".MainActivity" android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar">            <intent-filter>                <action android:name="android.intent.action.MAIN"/>                <category android:name="android.intent.category.LAUNCHER"/>            </intent-filter>        </activity>        <activity  android:name="com.ryg.dynamicload.DLProxyActivity" android:label="@string/app_name">            <intent-filter>                <action android:name="com.ryg.dynamicload.proxy.activity.VIEW"/>                <category android:name="android.intent.category.DEFAULT"/>            </intent-filter>        </activity>        <activity  android:name="com.ryg.dynamicload.DLProxyFragmentActivity" android:label="@string/app_name">            <intent-filter>                <action android:name="com.ryg.dynamicload.proxy.fragmentactivity.VIEW"/>                <category android:name="android.intent.category.DEFAULT"/>            </intent-filter>        </activity>        <service android:name="com.ryg.dynamicload.DLProxyService"/>    </application></manifest>

(5) 编译plugin项目,将生成的plugin-debug.apk文件放入/mnt/sdcard/DynaminLoadHost目录下,然后运行Host,运行结果如下:

4 总结

  • 1 宿主和插件没有任何联系,但是插件需要继承DLBasePluginActivity,这个不太友好
  • 2 侵入式的,对插件apk的开发限制太多,例如:必须继承DLBasePluginActivity,启动时候必须调用startPluginActivity(new
    DLIntent(getPackageName(),TestActivity.class))
  • 3 这个框架学习成本高,限制多,联调不方便,不建议使用
  • 4 目前比较好的插件有360公司的DroidPlugin, 以及类似友盟的第三方解决方案ApkPlug

5 扩展

1 Android Studio中可以不用jar文件吗?

Host可以,但plugin不可以。宿主项目可以依赖于lib项目,但是plugin必须使用jar文件,原因参见下面第3条

2 Host项目如何直接依赖lib项目,而不用jar文件?

修改host文件的build.gradle文件

dependencies {    compile project(':lib')    .....}

修改lib的build.gradle文件,不修改的话就会和host项目中的support-v4包冲突

dependencies {    provided fileTree(dir: 'libs', include: ['*.jar'])}

3 为什么plugin必须使用jar文件?

Plugin不能将lib模块打包到apk中,所以不能使用compile,只能使用provided,所以如果不用jar则plugin模块的build.gradle只能如下:

dependencies {    provided project(':lib')    .....}

呵呵,可惜这样是不行的,project只能使用compile ,不能使用provided ,百度了半天没有解决这个问题,如果你有办法欢迎留言

6 参考博客:

(1) http://blog.csdn.net/singwhatiwanna/article/details/40283117

7 转载请注明来自“梧桐那时雨”的博客:http://blog.csdn.net/fuchaosz/article/details/51056947

Tips:
如果觉得这篇博客对你有帮助,就给博主留个言或者顶一下呗,鼓励一下博主创作出更多优质博客,Thank you

更多相关文章

  1. Android新建项目与简易Button事件
  2. Android(安卓)Stuido部分快捷键失灵
  3. Android小项目——社交类app(低仿微信)
  4. 简述MVC框架模式以及在你(Android)项目中的应用
  5. Unity3D中 Android插件findviewbyid返回null的解决办法
  6. 关于Android工程从eclipse迁移至android studio的过程
  7. [Android]用架构师角度看插件化(2)-Replugin 唯一hook点
  8. 【FastDev4Android框架开发】Android快速开发框架介绍(一)
  9. cocos2dx 3.x系列之Mac环境编译Android(安卓)apk须知

随机推荐

  1. Android中各种ontouch事件
  2. Android(安卓)利用addView 动态给Activit
  3. Android ADB=Android Debug Bridge帮助信
  4. Android 自定义控件打造史上最简单的侧滑
  5. Android的进程优先级与进程回收详解
  6. Android -- 解决Android Studio 和 Andro
  7. Ubuntu 下创建启动器
  8. 仿Android6.0联系人列表
  9. android复合控件
  10. android ios vue 互调