插件化知识梳理(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 框架之如何引入应用插件 中介绍的相同:

插件化知识梳理(3) - Small 框架之宿主分身_第1张图片

其中, 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文件中,添加必须要的声明:



    

        
        

        
        

        
        

        
        
            
                
            
        

    


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
插件化知识梳理(3) - Small 框架之宿主分身_第2张图片

而我们在宿主分身中的声明则被加入到了最后的 AndroidManifest.xml文件中:
插件化知识梳理(3) - Small 框架之宿主分身_第3张图片


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

  • Android 知识梳理目录:http://www.jianshu.com/p/fd82d18994ce
  • 个人主页:http://lizejun.cn
  • 个人知识总结目录:http://lizejun.cn/categories/

你可能感兴趣的:(插件化知识梳理(3) - Small 框架之宿主分身)