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


相关阅读

插件化知识梳理(1) - Small 框架之如何引入应用插件
插件化知识梳理(2) - Small 框架之如何引入公共库插件
插件化知识梳理(3) - Small 框架之宿主分身
插件化知识梳理(4) - Small 框架之如何实现插件更新
插件化知识梳理(5) - Small 框架之如何不将插件打包到宿主中
插件化知识梳理(6) - Small 源码分析之 Hook 原理
插件化知识梳理(7) - 类的动态加载入门
插件化知识梳理(8) - 类的动态加载源码分析
插件化知识梳理(9) - 资源的动态加载示例及源码分析
插件化知识梳理(10) - Service 插件化实现及原理


一、前言

插件化知识梳理(1) - Small 框架之如何引入应用插件插件化知识梳理(2) - Small 框架之如何引入公共库插件 前两篇文章中,我们介绍了如何通过Small框架来实现应用插件及公共库插件,今天,我再来介绍一个新的知识点 - 宿主分身。

与应用插件和公共库插件不同,宿主分身会作为宿主的一部分,被编译到宿主当中,因此它不能被独立更新,但是它提供了一些插件所不具备的功能:

  • 插件模块可以自由访问其中的代码和资源
  • 预留特定的资源、代码和AndroidManifest.xml声明,让系统可以正常识别
  • 预留稳定的资源、代码、第三方库,减少插件体积

正如前面介绍的,应用插件通过app.xxx,公共库插件通过lib.xxx的命名方式,让Small进行识别。而宿主分身则需要以app+xxxx的方式来命名。

除了以上介绍的可选情况,有一些代码是必须要放入到宿主分身中的:

  • AndroidManifest.xml的声明
  • 所有权限,例如引入网络权限、读取存储等
  • 包含了某些特殊权限的Activity,如processconfigChanges
  • 任何的provider/receiver/service声明
  • 资源
  • 转场动画
  • 通知栏图标、自定义视图
  • 桌面快捷方式图标

二、具体示例

2.1 创建应用插件模块

首先,我们创建一个新的插件模块,创建的方式和 插件化知识梳理(1) - Small 框架之如何引入应用插件 中介绍的相同:


其中,DetailActivity是插件的入口,而以Stub开头的四个类分别为Activity/Service/ContentProvider/BroadcastReceiver,我们需要在宿主分身模块中对其进行声明。

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;
    }
}

为了方便验证,我们在回调函数中弹出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 创建宿主分身模块

创建宿主分身模块时,需要选择Android Library,并且它的模块名需要为app+xxx的结构,这里,我们命名为app+detail


对于宿主分身模块,这里我们需要在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 文件

对于宿主分身模块,不需要添加声明,我们所需要的只是对新增的应用插件模块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 结果


通过分析最后生成的APK文件,可以发现,只有应用插件和公共库插件模块作为so被打包进入了APK中,而宿主分身模块并没有生成so

而我们在宿主分身中的声明则被加入到了最后的AndroidManifest.xml文件中:


更多文章,欢迎访问我的 Android 知识梳理系列:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容