插件化知識(shí)梳理(3) - Small 框架之宿主分身


相關(guān)閱讀

插件化知識(shí)梳理(1) - Small 框架之如何引入應(yīng)用插件
插件化知識(shí)梳理(2) - Small 框架之如何引入公共庫(kù)插件
插件化知識(shí)梳理(3) - Small 框架之宿主分身
插件化知識(shí)梳理(4) - Small 框架之如何實(shí)現(xiàn)插件更新
插件化知識(shí)梳理(5) - Small 框架之如何不將插件打包到宿主中
插件化知識(shí)梳理(6) - Small 源碼分析之 Hook 原理
插件化知識(shí)梳理(7) - 類(lèi)的動(dòng)態(tài)加載入門(mén)
插件化知識(shí)梳理(8) - 類(lèi)的動(dòng)態(tài)加載源碼分析
插件化知識(shí)梳理(9) - 資源的動(dòng)態(tài)加載示例及源碼分析
插件化知識(shí)梳理(10) - Service 插件化實(shí)現(xiàn)及原理


一、前言

插件化知識(shí)梳理(1) - Small 框架之如何引入應(yīng)用插件,插件化知識(shí)梳理(2) - Small 框架之如何引入公共庫(kù)插件 前兩篇文章中,我們介紹了如何通過(guò)Small框架來(lái)實(shí)現(xiàn)應(yīng)用插件及公共庫(kù)插件,今天,我再來(lái)介紹一個(gè)新的知識(shí)點(diǎn) - 宿主分身。

與應(yīng)用插件和公共庫(kù)插件不同,宿主分身會(huì)作為宿主的一部分,被編譯到宿主當(dāng)中,因此它不能被獨(dú)立更新,但是它提供了一些插件所不具備的功能:

  • 插件模塊可以自由訪問(wèn)其中的代碼和資源
  • 預(yù)留特定的資源、代碼和AndroidManifest.xml聲明,讓系統(tǒng)可以正常識(shí)別
  • 預(yù)留穩(wěn)定的資源、代碼、第三方庫(kù),減少插件體積

正如前面介紹的,應(yīng)用插件通過(guò)app.xxx,公共庫(kù)插件通過(guò)lib.xxx的命名方式,讓Small進(jìn)行識(shí)別。而宿主分身則需要以app+xxxx的方式來(lái)命名。

除了以上介紹的可選情況,有一些代碼是必須要放入到宿主分身中的:

  • AndroidManifest.xml的聲明
  • 所有權(quán)限,例如引入網(wǎng)絡(luò)權(quán)限、讀取存儲(chǔ)等
  • 包含了某些特殊權(quán)限的Activity,如process,configChanges
  • 任何的provider/receiver/service聲明
  • 資源
  • 轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
  • 通知欄圖標(biāo)、自定義視圖
  • 桌面快捷方式圖標(biāo)

二、具體示例

2.1 創(chuàng)建應(yīng)用插件模塊

首先,我們創(chuàng)建一個(gè)新的插件模塊,創(chuàng)建的方式和 插件化知識(shí)梳理(1) - Small 框架之如何引入應(yīng)用插件 中介紹的相同:


其中,DetailActivity是插件的入口,而以Stub開(kāi)頭的四個(gè)類(lèi)分別為Activity/Service/ContentProvider/BroadcastReceiver,我們需要在宿主分身模塊中對(duì)其進(jìn)行聲明。

public class StubActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stub);
    }
}
public class StubBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "StubBroadcastReceiver, onReceive", Toast.LENGTH_SHORT).show();
    }
}
public class StubContentProvider extends ContentProvider {

    public static final Uri CONTENT_URI = Uri.parse("content://com.demo.small.app.detail.stub.provider");

    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
        Toast.makeText(getContext(), "StubContentProvider, insert", Toast.LENGTH_SHORT).show();
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
        return 0;
    }
}
public class StubService extends Service {

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(getApplicationContext(), "StubService, onStartCommand", Toast.LENGTH_SHORT).show();
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

為了方便驗(yàn)證,我們?cè)诨卣{(diào)函數(shù)中彈出Toast。

public class DetailActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);
    }

    public void activity(View view) {
        Intent intent = new Intent(this, StubActivity.class);
        startActivity(intent);
    }

    public void service(View view) {
        Intent intent = new Intent(this, StubService.class);
        startService(intent);
    }

    public void contentProvider(View view) {
        ContentValues values = new ContentValues();
        getContentResolver().insert(StubContentProvider.CONTENT_URI, values);
    }

    public void broadcastReceiver(View view) {
        Intent intent = new Intent();
        intent.setAction("com.demo.small.action.stub.broadcast");
        sendBroadcast(intent);
    }
}

2.2 創(chuàng)建宿主分身模塊

創(chuàng)建宿主分身模塊時(shí),需要選擇Android Library,并且它的模塊名需要為app+xxx的結(jié)構(gòu),這里,我們命名為app+detail


對(duì)于宿主分身模塊,這里我們需要在AndroidManifest.xml文件中,添加必須要的聲明:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.demo.small.appdetail">

    <application
        android:allowBackup="true"
        android:label="@string/app_name">

        <!-- Stub Activity -->
        <activity android:name="com.demo.small.app.detail.StubActivity" android:theme="@style/Theme.AppCompat"/>

        <!-- Stub Service -->
        <service android:name="com.demo.small.app.detail.StubService"/>

        <!-- Stub ContentProvider -->
        <provider
            android:authorities="com.demo.small.app.detail.stub.provider"
            android:name="com.demo.small.app.detail.StubContentProvider"/>

        <!-- Stub BroadcastReceiver -->
        <receiver android:name="com.demo.small.app.detail.StubBroadcastReceiver">
            <intent-filter>
                <action android:name="com.demo.small.action.stub.broadcast"/>
            </intent-filter>
        </receiver>

    </application>

</manifest>

2.3 修改宿主模塊的 bundle.json 文件

對(duì)于宿主分身模塊,不需要添加聲明,我們所需要的只是對(duì)新增的應(yīng)用插件模塊app.detail添加聲明:

{
  "version": "1.0.0",
  "bundles": [
    {
      "uri": "lib.utils",
      "pkg": "com.demo.small.lib.utils"
    },
    {
      "uri": "lib.style",
      "pkg": "com.demo.small.lib.style"
    },
    {
      "uri": "main",
      "pkg": "com.demo.small.app.main"
    },
    {
      "uri": "detail",
      "pkg": "com.demo.small.app.detail"
    }
  ]
}

2.4 結(jié)果


通過(guò)分析最后生成的APK文件,可以發(fā)現(xiàn),只有應(yīng)用插件和公共庫(kù)插件模塊作為so被打包進(jìn)入了APK中,而宿主分身模塊并沒(méi)有生成so。

而我們?cè)谒拗鞣稚碇械穆暶鲃t被加入到了最后的AndroidManifest.xml文件中:


更多文章,歡迎訪問(wèn)我的 Android 知識(shí)梳理系列:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容