1. 程式人生 > >Android RePlugin 使用及原始碼分析(1)

Android RePlugin 使用及原始碼分析(1)

1. RePlugin 概述

RePlugin是一套完整的、穩定的、適合全面使用的,佔坑類外掛化方案。
具體來說有如下特點:
完整的:讓外掛執行起來“像單品那樣”,支援大部分特性
穩定的:如此靈活完整的情況下,其框架崩潰率僅為業內很低的“萬分之一”
適合全面使用的:其目的是讓應用內的“所有功能皆為外掛”
佔坑類:以穩定為前提的Manifest佔坑思路
外掛化方案:基於Android原生API和語言來開發,充分利用原生特性

作為一款成熟的外掛化框架,官方列舉了RePlugin如下優勢:

極其靈活:主程式無需升級(無需在Manifest中預埋元件),即可支援新增的四大元件,甚至全新的外掛
**非常穩定:**Hook點僅有一處(ClassLoader),無任何Binder Hook!如此可做到其崩潰率僅為“萬分之一”,並完美相容市面上近乎所有的Android ROM
特性豐富:

支援近乎所有在“單品”開發時的特性。包括靜態Receiver、Task-Affinity坑位、自定義Theme、程序坑位、AppCompat、DataBinding等
易於整合:無論外掛還是主程式,只需“數行”就能完成接入
管理成熟:擁有成熟穩定的“外掛管理方案”,支援外掛安裝、升級、解除安裝、版本管理,甚至包括程序通訊、協議版本、安全校驗等
數億支撐:有360手機衛士龐大的數億使用者做支撐,三年多的殘酷驗證,確保App用到的方案是最穩定、最適合使用的

RePlugin 專案一共4個庫,其中有兩個是library庫,兩個是gradle外掛工程,工程截圖如下:
這裡寫圖片描述

1. replugin-host-gradle


host工程使用的Gradle外掛,主要功能是宿主程式打包的過程中動態的修改AndroidManifest.xml的資訊,動態的生成佔位各種Activity、provider和service的宣告。

2. replugin-host-library
宿主工程的library,RePlugin 的核心功能都在這個庫中,RePlugin 框架的初始化,外掛的安裝,載入以及啟動都與這個庫有很大關係。

3. replugin-plugin-gradle
外掛工程的Gradle的外掛,替換外掛工程中的Activity的繼承全部替換成Replugin庫中定義的XXXActivity。動態的將外掛apk中呼叫LocalBroadcastManager的地方修改為Replugin中的PluginLocalBroadcastManager呼叫,動態修改ContentResolver和ContentProviderClient的呼叫修改成Replugin呼叫,動態的修改外掛工程中所有呼叫Resource.getIdentifier方法的地方,將第三引數修改為外掛工程的包名 。

4. replugin-plugin-library
外掛工程的library,通過反射的方式來使用宿主程式中介面和功能。

2. 如何整合 RePlugin

RePlugin 對外掛的管理有兩種方式:內建外掛和外接外掛
外接外掛是指可通過“下載”、“放入SD卡”等方式來安裝並執行的外掛。
內建外掛是指可以“隨著主程式發版”而下發的外掛,通常這個外掛會放到主程式的Assets目錄下。
大多數情況使用外掛都是外接的方式,所以接下來我們就以外接的方式來梳理RePlugin的整合方式。

要將 RePlugin 整合到我們自己的專案中,需要分別在宿主工程和外掛工程分別整合RePlguin框架:

宿主整合 RePlguin:
  • 1 . 新增 RePlugin Host Gradle 依賴
    在工程專案根目錄的 build.gradle(注意:不是 app/build.gradle) 中新增 replugin-host-gradle 依賴:
buildscript {
    dependencies {
        classpath 'com.qihoo360.replugin:replugin-host-gradle:2.2.4'
        ...
    }
}
  • 2 . 新增 RePlugin Host Library 依賴
    在 app/build.gradle 中應用 replugin-host-gradle 外掛,並新增 replugin-host-lib 依賴:
apply plugin: 'replugin-host-gradle'
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    ...
    implementation 'com.qihoo360.replugin:replugin-host-lib:2.2.4'
}

注意:請將apply plugin: ‘replugin-host-gradle’放在 android{} 塊之後,防止出現無法讀取applicationId,導致生成的坑位出現異常

  • 3 . 配置 Application 類
    這裡有兩種做法,最簡單的方式就是讓宿主工程的 Application 直接繼承自 RePluginApplication。
    如果工程本身沒有Application,那就直接繼承RePluginApplication,如下:
public class App extends RePluginApplication {
    ...
}

然後在AndroidManifest中配置這個App

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

當宿主功能已經定義了Application,那此時就直接找到它的基類的,該基類必然是繼承自Application,此時我們只需要讓該基類繼承RePluginApplication即可。

上面的方法無論怎麼都是採用繼承的方式來做的,如果實在不想繼承,還可以在已有的Application中的相應生命週期函式中呼叫RePlugin的方法:

RePlugin.App.attachBaseContext(this);
RePlugin.App.onCreate();
RePlugin.App.onLowMemory();
RePlugin.App.onTrimMemory(level);
RePlugin.App.onConfigurationChanged(config);
外掛整合 RePlguin:
  • 1 . 新增 RePlugin Plugin Gradle 依賴
    在工程專案根目錄的 build.gradle(注意:不是 app/build.gradle) 中新增 replugin-plugin-gradle 依賴:
buildscript {
    dependencies {
        classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.2.4'
        ...
    }
}
  • 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.4'
    ...
}

經過以上操作,宿主和外掛的RePlguin框架就算整合完成了,接下來就是如何去在專案中使用了。

3. 使用 RePlugin

對於外掛App經過上面的整合步驟後就可以直接打包在宿主工程中使用了,但怎麼使用呢? 拿外接整合來說,我們只需要將打包好的外掛APK放置在手機特定目錄下,這裡我們選擇放在SD卡的根目錄下,然後在host宿主工程中呼叫RePlugin 的API去安裝該外掛APK就行,安裝成功後就可以直接啟動外掛了,具體做法如下:
1. 首先打包外掛APK,pluginApp.apk, 將該APK放置在SD卡根目錄下;
2. 在宿主工程中安裝外掛APK pluginApp.apk, 完整的安裝過程程式碼如下:

private void installPlugin() {
    //判斷外掛是否已經安裝,外掛的名字為外掛的包名com.example.pluginapp
    PluginInfo info = MP.getPlugin("com.example.pluginapp", true);
    if (info == null) {
        //外掛apk 位於sd卡根目錄下
        String path = Environment.getExternalStorageDirectory().getPath() + "/pluginApp.apk";
        //判斷外掛apk檔案是否存在
        File pluginApk = new File(path);
        if (pluginApk.exists()) {
            PluginInfo pluginInfo = RePlugin.install(path);
            if (pluginInfo == null) {
                Toast.makeText(this, "外掛安裝失敗", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "外掛安裝成功", Toast.LENGTH_SHORT).show();
            }
        } else {
            Toast.makeText(this, "sd卡路徑下無外掛apk", Toast.LENGTH_SHORT).show();
        }
    } else {
        Toast.makeText(this, "外掛已經安裝", Toast.LENGTH_SHORT).show();
    }
}

3 .如果安裝成功,接下來就可以直接啟動外掛了,程式碼如下:

private void setupPlugin() {
    Intent intent = RePlugin.createIntent("com.example.pluginapp", "com.example.pluginapp.MainActivity");
    RePlugin.startActivity(MainActivity.this, intent);
}  

其實建立Intent的操作可以不使用RePlugin提供的介面,用Android 原生的方式也是可以的。