Android--›360全面插件化RePlugin框架交互通信使用概述

官網的wiki文檔, 把RePlugin的接入, 插件的使用, 組件的調用介紹的很清楚.

但是關於宿主插件的交互,介紹的比較少.

本文主要介紹關於Binder交互方式的使用.

點擊直接跳轉Binder相關


官方開原地址RePlugin

1.宿主接入指南

https://github.com/Qihoo360/RePlugin/wiki/主程序接入指南

1.1 在項目根目錄的 build.gradle

https://github.com/Qihoo360/RePlugin/wiki/主程序接入指南#第-1-步添加-replugin-host-gradle-依賴

(注意:不是 app/build.gradle) 中添加 replugin-host-gradle 依賴:

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

1.2 在 app/build.gradle 中

https://github.com/Qihoo360/RePlugin/wiki/主程序接入指南#第-2-步添加-replugin-host-library-依賴

應用 replugin-host-gradle 插件,並添加 replugin-host-lib 依賴:

android {
    // ATTENTION!!! Must CONFIG this to accord with Gradle's standard, and avoid some error
    defaultConfig {
        applicationId "com.qihoo360.replugin.sample.host"
        ...
    }
    ...
}

// ATTENTION!!! Must be PLACED AFTER "android{}" to read the 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.4'
    ...
}

1.3 讓工程的 Application 直接繼承自 RePluginApplication。

https://github.com/Qihoo360/RePlugin/wiki/主程序接入指南#第-3-步配置-application-類

如果您的工程已有Application類,則可以將基類切換到RePluginApplication即可。或者您也可以用“非繼承式”接入。

public class MainApplication extends RePluginApplication {
}

既然聲明瞭Application,自然還需要在AndroidManifest中配置這個Application。

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

2.插件接入指南

https://github.com/Qihoo360/RePlugin/wiki/插件接入指南

2.1 在項目根目錄的 build.gradle

https://github.com/Qihoo360/RePlugin/wiki/插件接入指南#第-1-步添加-replugin-plugin-gradle-依賴

(注意:不是 app/build.gradle) 中添加 replugin-plugin-gradle 依賴:

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

2.2 在 app/build.gradle 中

https://github.com/Qihoo360/RePlugin/wiki/插件接入指南#第-2-步添加-replugin-plugin-library-依賴

應用 replugin-plugin-gradle 插件,並添加 replugin-plugin-lib 依賴:

apply plugin: 'replugin-plugin-gradle'

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

3.內置插件

https://github.com/Qihoo360/RePlugin/wiki/插件的管理#內置插件

添加內置插件

  • 將APK改名爲:插件名.jar
  • 放入主程序的assets/plugins目錄
  • 如果更改了assets/plugins目錄下的插件, 需要卸載重裝宿主才能生效

3.1.使用插件中的組件

https://github.com/Qihoo360/RePlugin/wiki/插件的組件#如何使用組件

例如您要打開一個Activity,則可以這麼玩:

Intent intent = new Intent(v.getContext(), ThemeDialogActivity.class);
context.startActivity(intent);

打開服務呢?當然,如法炮製:

Intent intent = new Intent(v.getContext(), PluginDemoService1.class);
intent.setAction("action1");
context.startService(intent);

使用Content-Provider也是如此:

Uri uri = Uri.parse("content://com.qihoo360.replugin.sample.demo1.provider2/test");

ContentValues cv = new ContentValues();
cv.put("address", "beijing");

Uri urii = context.getContentResolver().insert(uri, cv);

當然了,還有大名鼎鼎的BroadcastReceiver:

Intent intent = new Intent();
intent.setAction("com.qihoo360.repluginapp.replugin.receiver.ACTION1");
intent.putExtra("name", "jerry");
context.sendBroadcast(intent);

和在APP中保持一致.

3.2.使用插件外的組件

https://github.com/Qihoo360/RePlugin/wiki/插件的組件#插件外組件

// 方法1(最“單品”)
Intent intent = new Intent();
intent.setComponent(new ComponentName("demo2", 
    "com.qihoo360.replugin.sample.demo2.databinding.DataBindingActivity"));
context.startActivity(intent);

// 方法2(快速創建Intent)
Intent intent = RePlugin.createIntent("demo2", 
    "com.qihoo360.replugin.sample.demo2.databinding.DataBindingActivity");
context.startActivity(intent);

// 方法3(一行搞定)
RePlugin.startActivity(v.getContext(), new Intent(), "demo2", 
    "com.qihoo360.replugin.sample.demo2.databinding.DataBindingActivity");
    
// 方法4 (action)
Intent intent = new Intent(
    "com.qihoo360.replugin.sample.demo2.action.theme_fullscreen_2");
RePlugin.startActivity(v.getContext(), intent, "demo2", null);

3.3.插件獲取主程序Context

https://github.com/Qihoo360/RePlugin/wiki/插件的組件#插件獲取主程序context

要獲取主程序的Context,需要調用 RePlugin.getHostContext() 方法即可。例如:

Context hostContext = RePlugin.getHostContext();
...

當然,獲取其它內容(如ClassLoader等)也如法炮製,可直接調用RePlugin類中的相應方法即可。

3.4.主程序調用插件組件

https://github.com/Qihoo360/RePlugin/wiki/插件的組件#主程序調用插件組件

打開插件的Activity
要打開一個插件的Activity,您需要調用 RePlugin.startActivity() 方法。例如:

RePlugin.startActivity(MainActivity.this, RePlugin.createIntent("demo1", 
    "com.qihoo360.replugin.sample.demo1.MainActivity"));

獲取插件的Context

要獲取插件的Context,可以調用 RePlugin.fetchContext() 方法。例如:

Context examContext = RePlugin.fetchContext("exam");
...

當然,獲取其它內容(如ClassLoader等)也如法炮製,可直接調用RePlugin類中的相應方法即可。

啓動、綁定插件的Service

可以使用我們的 PluginServiceClient 類中的相應方法來操作。例如,若您想“綁定”一個服務,則可以:

PluginServiceClient.bindService(RePlugin.createIntent(
    "exam", "AbcService"), mServiceConn);

其它方法都在 PluginServiceClient 裏,和系統參數完全一致,這裏不贅述。

請參見 JavaDoc 文檔瞭解更多。

使用插件的Content-Provider

同樣的,使用 PluginProviderClient 類中的方法即可操作Provider,具體做法如下:

PluginProviderClient.query(xxx);

4.Binder使用

4.1.1 插件中聲明實現aidl類

plugin-demo2工程

聲明aidl com.qihoo360.replugin.sample.demo2.IDemo2.aidl

package com.qihoo360.replugin.sample.demo2;

interface IDemo2 {
    void hello(String str);
}

實現 com.qihoo360.replugin.sample.demo2.Demo2Impl

public class Demo2Impl extends IDemo2.Stub {
    @Override
    public void hello(String str) throws RemoteException {
        Toast.makeText(RePlugin.getPluginContext(), str, Toast.LENGTH_SHORT).show();
    }
}

註冊Binder

RePlugin.registerPluginBinder("demo2test", new Demo2Impl());

4.1.2 宿主/插件中使用aidl類

需要在工程中,聲明相同的aidl文件 com.qihoo360.replugin.sample.demo2.IDemo2.aidl

package com.qihoo360.replugin.sample.demo2;

interface IDemo2 {
    void hello(String str);
}

然後,在需要使用的插件中調用:

IBinder b = RePlugin.fetchBinder("demo2", "demo2test");
if (b == null) {
    return;
}
IDemo2 demo2 = IDemo2.Stub.asInterface(b);
try {
    demo2.hello("helloooooooooooo");
} catch (RemoteException e) {
    e.printStackTrace();
}

4.2.1 宿主中聲明實現aidl

宿主中聲明aidl,並實現

//聲明 IMyAidlInterface.aidl
package com.qihoo360.replugin.sample.host;

// Declare any non-default types here with import statements

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    String test(String str);
}

//實現 
public class IMyAidlInterfaceImpl extends IMyAidlInterface.Stub {

    @Override
    public String test(String str) throws RemoteException {
        Toast.makeText(RePluginInternal.getAppContext(), str, Toast.LENGTH_SHORT).show();
        return str;
    }
}

註冊Binder

在宿主中註冊Binder需要使用registerHostBinder.

在插件中註冊Binder需要使用registerPluginBinder.

RePlugin.registerHostBinder(new IHostBinderFetcher() {
    @Override
    public IBinder query(String module) {
        if (TextUtils.equals(module, "IMyAidlInterfaceImpl")) {
            return new IMyAidlInterfaceImpl();
        }
        return null;
    }
});

4.2.2 插件中使用aidl

插件中獲取宿主的Binder, 並使用

IBinder binder = RePlugin.fetchBinder("main", "IMyAidlInterfaceImpl");
if (binder != null) {
    IMyAidlInterface aidlInterface = IMyAidlInterface.Stub.asInterface(binder);
    try {
        appendText("aidl返回:" + aidlInterface.test("by plugin"));
    } catch (RemoteException e) {
        e.printStackTrace();
    }
} else {
    appendText("binder is null");
}

需要注意的就是, fetchBinder方法的第一個參數必須是main,其他和插件中使用Binder一致.

4.3.1 Global Binder使用

宿主和插件, 方法通用.

aidl的聲明和實現, 與前2種方法一致.

區別在於註冊/獲取 Binder的方式

註冊Binder

RePlugin.registerGlobalBinder("global", new GlobalInterfaceImpl());

獲取Binder

IBinder global = RePlugin.getGlobalBinder("global");
if (global != null) {
    GlobalInterface globalInterface = GlobalInterface.Stub.asInterface(global);
    try {
        appendText("global aidl返回:" + globalInterface.testGlobal("by plugin"));
    } catch (RemoteException e) {
        e.printStackTrace();
    }
} else {
    appendText("global binder is null");
}

5.代碼互通

5.1 宿主使用插件中的類

com.qihoo360.replugin.sample.host.PluginFragmentActivity

 /**
     * 注意:
     *
     * 如果一個插件是內置插件,那麼這個插件的名字就是文件的前綴,比如:demo1.jar插件的名字就是demo1(host-gradle插件自動生成),可以執行諸如RePlugin.fetchClassLoader("demo1")的操作;
     * 如果一個插件是外置插件,通過RePlugin.install("/sdcard/demo1.apk")安裝的,則必須動態獲取這個插件的名字來使用:
     * PluginInfo pluginInfo = RePlugin.install("/sdcard/demo1.apk");
     * RePlugin.preload(pluginInfo);//耗時
     * String name = pluginInfo != null ? pluginInfo.getName() : null;
     * ClassLoader classLoader = RePlugin.fetchClassLoader(name);
    */

    boolean isBuiltIn = true;
    String pluginName = isBuiltIn ? "demo1" : "com.qihoo360.replugin.sample.demo1";

    //註冊相關Fragment的類
    //註冊一個全局Hook用於攔截系統對XX類的尋找定向到Demo1中的XX類主要是用於在xml中可以直接使用插件中的類
    RePlugin.registerHookingClass("com.qihoo360.replugin.sample.demo1.fragment.DemoFragment",
            RePlugin.createComponentName(pluginName, "com.qihoo360.replugin.sample.demo1.fragment.DemoFragment"), null);
    setContentView(R.layout.activity_plugin_fragment);

    //代碼使用插件Fragment
    ClassLoader d1ClassLoader = RePlugin.fetchClassLoader(pluginName);//獲取插件的ClassLoader
    try {
        Fragment fragment = d1ClassLoader.loadClass("com.qihoo360.replugin.sample.demo1.fragment.DemoCodeFragment").asSubclass(Fragment.class).newInstance();//使用插件的Classloader獲取指定Fragment實例
        getSupportFragmentManager().beginTransaction().add(R.id.container2, fragment).commit();//添加Fragment到UI
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

需要添加依賴

provided files('libs/fragment.jar')//這個jar就是從Support-fragment中提取出來的並非特製包目的是爲了騙過編譯期

com.qihoo360.replugin.sample.demo1.MainActivity

5.2 直接調用代碼

// 此爲RePlugin的另一種做法,可直接調用宿主的Utils
// 雖然不是很推薦(版本控制問題,見FAQ),但畢竟需求較大,且我們是“相對安全的共享代碼”方案,故可以使用
final String curTime = TimeUtils.getNowString();
if (!TextUtils.isEmpty(curTime)) {
    Toast.makeText(v.getContext(), "current time: " + TimeUtils.getNowString(), Toast.LENGTH_SHORT).show();
    // 打印其ClassLoader
    Log.d("MainActivity", "Use Host Method: cl=" + TimeUtils.class.getClassLoader());
} else {
    Toast.makeText(v.getContext(), "Failed to obtain current time(from host)", Toast.LENGTH_SHORT).show();
}

需要添加依賴:

provided files('libs/common-utils-lib-1.0.0.jar')//這個jar就是從Host的utils中編譯生成的,其目的是爲了騙過編譯期

5.3 通過反射調用 推薦方式

// 這是RePlugin的推薦玩法:反射調用Demo2,這樣"天然的"做好了"版本控制"
// 避免出現我們當年2013年的各種問題
ClassLoader cl = RePlugin.fetchClassLoader("demo2");
if (cl == null) {
    Toast.makeText(v.getContext(), "Not install Demo2", Toast.LENGTH_SHORT).show();
    return;
}

try {
    Class clz = cl.loadClass("com.qihoo360.replugin.sample.demo2.MainApp");
    Method m = clz.getDeclaredMethod("helloFromDemo1", Context.class, String.class);
    m.invoke(null, v.getContext(), "Demo1");
} catch (Exception e) {
    // 有可能Demo2根本沒有這個類,也有可能沒有相應方法(通常出現在"插件版本升級"的情況)
    Toast.makeText(v.getContext(), "", Toast.LENGTH_SHORT).show();
    e.printStackTrace();
}

插件信息

https://github.com/Qihoo360/RePlugin/wiki/插件的信息

別名

https://github.com/Qihoo360/RePlugin/wiki/插件的信息#插件別名

協議版本號

https://github.com/Qihoo360/RePlugin/wiki/插件的信息#插件協議版本號

框架版本號

https://github.com/Qihoo360/RePlugin/wiki/插件的信息#插件框架版本號

<meta-data
    android:name="com.qihoo360.plugin.name"
    android:value="[你的插件別名]" />
<meta-data
    android:name="com.qihoo360.plugin.version.low"
    android:value="[你的插件協議版本號]" />
<meta-data
    android:name="com.qihoo360.plugin.version.high"
    android:value="[你的插件協議版本號]" />
<meta-data
    android:name="com.qihoo360.framework.ver"
    android:value="[你的框架版本號]" />

插件信息的獲取

插件信息的類是 PluginInfo,無論是宿主還是插件均可使用。而要獲取插件信息還是非常簡單的:

  • 調用 RePlugin.getPlugin()方法,可獲取任意插件的信息,也可以藉此判斷插件是否安裝(若爲null則表示沒有安裝)
  • 調用 RePlugin.getPluginInfoList()方法可獲取所有已安裝(包括內置插件)的信息
  • 調用 RePlugin.install()方法,其返回值是“安裝成功後”的PluginInfo。若返回Null則表示“安裝失敗”

相關源碼


羣內有各(pian)種(ni)各(jin)樣(qun)的大佬,等你來撩.

聯繫作者

點此快速加羣

請使用QQ掃碼加羣, 小夥伴們都在等着你哦!

關注我的公衆號, 每天都能一起玩耍哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章