Android插件化开发—RePlugin插件化框架

时间:2022-03-02 16:32:44

1.什么是插件化开发

首先我们先来看看通过插件化开发后的APP是什么样的效果。这里就用最典型的插件化APP 360手机卫士 来演示一下什么是插件化的APP。
Android插件化开发—RePlugin插件化框架
可以看到,打开应用后在切换到工具箱中有很多功能,我的工具中先是有8个自带的功能,然后点击更多工具可以去添加,点添加后先是下载,下载完成就可以打开使用了。

Android插件化开发—RePlugin插件化框架
我们再来首先看看APP的大小,才15.62M,是不是很惊讶啊。
在上面演示的图片中那些工具其实都是插件,而且在我的工具中8个自带的工具是我后面要说的内置插件,而我添加的那个“健康管理”工具就是外置插件,区别就是内置插件在安装完主体应用后就安装好了,而外置插件是在主体应用运行时安装的,最重要的一点是,插件也是一个APP,后缀名为.apk。而这个主体应用专业点说法就叫做宿主APP,反正都是APP。

简单概括插件化开发:

通过宿主APP可以运行未安装的APP。

当然,只能运行“成为插件”的APP。

2.插件化开发的优缺点:

优点:

  • 模块解耦,应用程序扩展性强
  • 解除单个dex函数不能超过 65535的限制
  • 动态升级,下载更新节省流量
  • 高效开发(编译速度更快)

缺点:

  • 增加了主应用程序的逻辑难度
  • 技术有难度,目前一些成熟的框架都是闭源的

3.插件化框架—RePlugin介绍

3.1什么是RePlugin?

RePlugin是一套完整的、稳定的、适合全面使用的,占坑类插件化方案,由360手机卫士的RePlugin Team研发,也是业内首个提出”全面插件化“(全面特性、全面兼容、全面使用)的方案。

3.2RePlugin有什么用?

可以让你的项目很容易的成为插件化APP以及像开发单品一样来开发插件。

3.3官方文档

RePlugin —— 历经三年多考验,数亿设备使用的,稳定占坑类插件化方案

4.RePlugin的基本使用

这里分为两部分,宿舍APP的开发和插件APP的开发,然后每部分在一步一步的来讲。

4.1宿主APP

这里分为3步

  • 将RePlugin接入到您的主程序
  • 安装一个插件
  • 打开一个插件的Activity

4.1.1新建一个项目并接入RePlugin

新建一个项目
Android插件化开发—RePlugin插件化框架

接入Replugin
第 1 步:添加 RePlugin Host Gradle 依赖
在项目根目录的 build.gradle(注意:不是 app/build.gradle) 中添加 replugin-host-gradle 依赖:

buildscript {
    dependencies {
        classpath 'com.qihoo360.replugin:replugin-host-gradle:2.2.1'
        ...
    }
}

第 2 步:添加 RePlugin Host Library 依赖
在 app/build.gradle 中应用 replugin-host-gradle 插件,并添加 replugin-host-lib 依赖:

// 注意,这里要放在"android{}"这后面,因为要先读取applicationId
apply plugin: 'replugin-host-gradle'

/** * 配置项均为可选配置,默认无需添加 * 更多可选配置项参见replugin-host-gradle的RepluginConfig类 * 可更改配置项参见 自动生成RePluginHostConfig.java */
repluginHostConfig {
    /** * 是否使用 AppCompat 库 * 不需要个性化配置时,无需添加 */
    useAppCompat = true
    /** * 背景不透明的坑的数量 * 不需要个性化配置时,无需添加 */
    countNotTranslucentStandard = 6
    countNotTranslucentSingleTop = 2
    countNotTranslucentSingleTask = 3
    countNotTranslucentSingleInstance = 2
}

dependencies {
    compile 'com.qihoo360.replugin:replugin-host-lib:2.2.1'
    ...
}

务必注意

以下请务必注意:

  • 请一定要确保符合Gradle开发规范,也即“必须将包名写在applicatonId”,而非AndroidManifest.xml中(通常从Eclipse迁移过来的项目可能出现此问题)。如果不这么写,则有可能导致运行时出现“Failed to find provider info for com.ss.android.auto.loader.p.main”的问题。
  • 请将apply plugin: ‘replugin-host-gradle’放在 android{} 块之后,防止出现无法读取applicationId,导致生成的坑位出现异常
  • 如果您的应用需要支持AppComat,则除了在主程序中引入AppComat-v7包以外,还需要在宿主的build.gradle中添加下面的代码若不支持AppComat则请不要设置此项
repluginHostConfig {
    useAppCompat = true
}

开启useAppCompat后,我们会在编译期生成AppCompat专用坑位,这样插件若使用AppCompat的Theme时就能生效了。若不设置,则可能会出现“IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.”的异常。

  • 如果您的应用需要个性化配置坑位数量,则需要在宿主的build.gradle中添加下面的代码:
repluginHostConfig {
     /** * 背景不透明的坑的数量 */
    countNotTranslucentStandard = 6
    countNotTranslucentSingleTop = 2
    countNotTranslucentSingleTask = 3
    countNotTranslucentSingleInstance = 2
}

更多可选配置项参见replugin-host-gradle的RepluginConfig类

第 3 步:配置 Application 类
让工程的 Application 直接继承自 RePluginApplication。

如果您的工程已有Application类,则可以将基类切换到RePluginApplication即可。或者您也可以用“非继承式”接入。

public class MainApplication extends RePluginApplication {
}

既然声明了Application,自然还需要在AndroidManifest中配置这个Application。

    <application
        android:name=".MainApplication"
        ... />

备选:“非继承式”配置Application
若您的应用对Application类继承关系的修改有限制,或想自定义RePlugin加载过程(慎用!),则可以直接调用相关方法来使用RePlugin。

public class MainApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);

        RePlugin.App.attachBaseContext(this);
        ....
    }

    @Override
    public void onCreate() {
        super.onCreate();

        RePlugin.App.onCreate();
        ....
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();

        /* Not need to be called if your application's minSdkVersion > = 14 */
        RePlugin.App.onLowMemory();
        ....
    }

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);

        /* Not need to be called if your application's minSdkVersion > = 14 */
        RePlugin.App.onTrimMemory(level);
        ....
    }

    @Override
    public void onConfigurationChanged(Configuration config) {
        super.onConfigurationChanged(config);

        /* Not need to be called if your application's minSdkVersion > = 14 */
        RePlugin.App.onConfigurationChanged(config);
        ....
    }
}

针对“非继承式”的注意点

  • 所有方法必须在UI线程来“同步”调用。切勿放到工作线程,或者通过post方法来执行
  • 所有方法必须一一对应,例如 RePlugin.App.attachBaseContext 方法只在Application.attachBaseContext中调用
  • 请将RePlugin.App的调用方法,放在“仅次于super.xxx()”方法的后面

4.1.2安装插件

插件又分为内置插件和外置插件,不过,无论是内置,还是外置插件,还需理解:不是所有的APK都能作为 RePlugin 的插件并安装进来的。必须要严格按照RePlugin的《插件接入指南》中所述完成接入,其编译出的APK才能成为插件,且这个APK同时也可以被安装到设备中。

内置插件

内置插件是指可以“随着主程序发版”而下发的插件,通常这个插件会放到主程序的Assets目录下。
针对内置插件而言,开发者可无需调用安装方法,由RePlugin来“按需安装”。

外置插件

外置插件是指可通过“下载”、“放入SD卡”等方式来安装并运行的插件。

添加内置插件
添加一个内置插件是非常简单的,甚至可以“无需任何Java代码”。只需两步即可:

  • 将APK改名为:[插件名].jar
  • 放入主程序的assets/plugins目录

这样,当编译主程序时,我们的“动态编译方案”会自动在assets目录下生成一个名叫“plugins-builtin.json”文件,记录了其内置插件的主要信息,方便运行时直接获取。

必须改成“[插件名].jar”后,才能被RePlugin-Host-Gradle识别,进而成为“内置插件”。

[插件名]可以是“包名”,也可以是“插件别名”。

安装外置插件
要安装一个插件,只需使用 RePlugin.install 方法,传递一个“APK路径”即可。

RePlugin.install(“/sdcard/exam.apk”);

注意

  • 无论安装还是升级,都会将“源文件”“移动”(而非复制)到插件的安装路径(如app_p_a)上,这样可大幅度节省安装和升级时间,但显然的“源文件”也就会消失
    • 若想改变这个行为,您可以参考RePluginConfig中的 setMoveFileWhenInstalling() 方法
    • 升级插件和此等同,故不再赘述

4.1.3打开一个插件的Activity

//RePlugin.createIntent()中的参数是,插件名称,要打开的插件的Activity的全名称
RePlugin.startActivity(MainActivity.this, RePlugin.createIntent("pluginA", "com.fu.plugina.MainActivity"));

好,下面来看看该如何开发插件。

4.2插件APP

4.2.1新建一个项目并接入RePlugin

新建一个项目
Android插件化开发—RePlugin插件化框架

接入RePlugin
第 1 步:添加 RePlugin Plugin Gradle 依赖
在项目根目录的 build.gradle(注意:不是 app/build.gradle) 中添加 replugin-plugin-gradle 依赖:

buildscript {
    dependencies {
        classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.2.1'
        ...
    }
}

第 2 步:添加 RePlugin Plugin Library 依赖
在 app/build.gradle 中应用 replugin-plugin-gradle 插件,并添加 replugin-plugin-lib 依赖:

apply plugin: 'replugin-plugin-gradle'

dependencies {
    compile 'com.qihoo360.replugin:replugin-plugin-lib:2.2.1'
    ...
}

后面就可以向开发单品一样开发插件APP了。

4.2.2配置插件名

要声明插件别名,需要在插件的AndroidManifest.xml中声明以下Meta-data:

     <meta-data
            android:name="com.qihoo360.plugin.name"
            android:value="[你的插件别名]" />

5.演示

这里我就演示外置插件的安装和打开。
我在宿主APP的MainActivity中添加了两个按钮,一个按钮是安装插件,一个按钮是打开插件的MainActivity。
Android插件化开发—RePlugin插件化框架

安装插件需要文件的路径,这里我准备把插件放在缓存目录中,先把路径写上,然后先启动了RepluginDemo这个程序,这时插件app还没放到手机上,这时点击安装插件会说文件不存在。
宿主APP中的MainActivity.java

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private android.widget.Button btnInstallPlugin;
    private android.widget.Button btnOpenPlugin;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.btnInstallPlugin = (Button) findViewById(R.id.btn_install_plugin);
        this.btnOpenPlugin = (Button) findViewById(R.id.btn_open_plugin);

        btnInstallPlugin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                File file = new File(getCacheDir() + File.separator + "/pluginA.apk");
                if (file.exists()) {
                    PluginInfo info = RePlugin.install(file.getAbsolutePath());
                    Log.i(TAG, "installPluginInfo: " + info.toString());
                } else {
                    Toast.makeText(MainActivity.this, "文件不存在", Toast.LENGTH_SHORT).show();
                }
            }
        });

        btnOpenPlugin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //打开插件的Activity
                RePlugin.startActivity(MainActivity.this, RePlugin.createIntent("pluginA", "com.fu.plugina.MainActivity"));
            }
        });

    }
}

然后插件APP中我就在MainActivity中放了一个按钮,点击按钮弹出toast
插件APP的MainActivity.java

public class MainActivity extends AppCompatActivity {

    private android.widget.Button btntoast;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.btntoast = (Button) findViewById(R.id.btn_toast);
        btntoast.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "我是插件", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

编译打包插件APP,并把打包出来的APK改名为“pluginA.apk”(因为我们在代码中写的要安装名为pluginA.apk的插件)然后放到宿主APP的缓存目录下。
Android插件化开发—RePlugin插件化框架

好,这时我们点击APP中的安装插件。
安装完成后打印的信息。
Android插件化开发—RePlugin插件化框架

然后我们再点击打开插件
Android插件化开发—RePlugin插件化框架
成功打开插件。

参考文章:

RePlugin —— 历经三年多考验,数亿设备使用的,稳定占坑类插件化方案