架构之占位式插件化框架 --组件通信

占位式,也叫插装式。运行的APP,也称之为宿主。

Activity通信

通过宿主来加载Plugin Activity

1. 环境准备

项目分为3个基础模块,分别是 宿主module(可以启动),插件module(最终会打包成单独apk文件),标准module(Android Library)。宿主和插件分别依赖标准

  1. 新建 插件module: plugin_package,该module只是为打包成apk文件。
  2. 新建 标准module:standard,该module是为了维护宿主和插件。是一个activity library
  3. 添加依赖关系,宿主和插件分别依赖标准stander

标准制定(组件的管理):

  1. 制定标准
    /**
     * standard 模块
     * 制定组件Activity的标准
     */
    public interface ActivityInterface {
    
        /**
         * 把宿主的环境给插件
         * @param appActivity
         */
        void insertAppActivity(Activity appActivity);
    
        void onCreate(Bundle savedInstanceState);
    
        void onResume();
    
        void onPause();
    
        void onStop();
    
        void onDestroy();
    
    }
    
  2. 插件的组件实现该标准
    /**
     * 插件 module 中的 Activity组件基类
     */
    public class BaseActivity extends Activity implements ActivityInterface {
    
        protected Activity appActivity; //宿主的 Activity环境
    
        @Override
        public void insertAppActivity(Activity appActivity) {
            this.appActivity = appActivity;
        }
    
        @SuppressLint("MissingSuperCall")
        @Override
        public void onCreate(Bundle savedInstanceState) {
    
        }
    
        @SuppressLint("MissingSuperCall")
        @Override
        public void onResume() {
    
        }
    
        @SuppressLint("MissingSuperCall")
        @Override
        public void onPause() {
    
        }
    
        @SuppressLint("MissingSuperCall")
        @Override
        public void onStop() {
    
        }
    
        @SuppressLint("MissingSuperCall")
        @Override
        public void onDestroy() {
    
        }
    }
    
    

插件的特点:插件没有组件环境,可以将宿主的组件环境给插件,可以通过标准传递给插件
加载插件:①加载插件中的Activity组件,②加载插件中的资源文件

2. 加载

在宿主APP,通过用户触发加载插件apk文件;而APK文件通过服务器下发,保存在本地存储。
加载包含两部分:①加载插件的Class ②加载插件的layout


    /**
     * 加载插件:
     * 1. 加载activity
     * 2.加载layout
     */

    public void loadPlugin() {

        try {
            // 1. 加载plugin class
            File file = getPluginPath();
            if (!file.exists()) {
                Log.e(TAG, "loadPlugin: 插件不存在 ");
                return;
            }

            String pluginPath = file.getAbsolutePath();
            // dexClassLoader需要一个缓存目录
            // getDir 生成路径: /data/data/当前应用包名/pDir。
            String optimizedDirectory = mContext.getDir("pDir", Context.MODE_PRIVATE).getAbsolutePath();
            /**
             * 加载class
             * @param String dexPath,  插件路径
             * @param String optimizedDirectory,  加载插件apk 需要一个缓存目录
             * @param String librarySearchPath, 底层c/c++ 库的目录
             * @param ClassLoader parent ClassLoader
             */
            mDexClassLoader = new DexClassLoader(pluginPath, optimizedDirectory, null, mContext.getClassLoader());

            // 2. 加载plugin layout
            //
            //
            // public final class AssetManager 不能直接new  需要通过反射调用
            AssetManager assetManager = AssetManager.class.newInstance();
            // 执行 android.content.res.AssetManager#addAssetPath ,将插件包的路径添加进去,加载resource  可以加载zip apk文件
            /**
             * @param parameterTypes 不是真正的类型,而是类类型
             */
            Method addAssetPathMethod = assetManager.getClass().getMethod("addAssetPath", String.class);//
            /**
             * @param obj 执行的方法所对应的类对象
             * @param args 执行方法的参数
             */
            addAssetPathMethod.invoke(assetManager,pluginPath);


            // 至此,assetManager就可以加载资源文件
            //
            //

            /**
             *  @param assets  资源管理类
             *  @param metrics 资源的配置信息  对应不同的资源分辨率
             *  @param config
             */
            Resources res = mContext.getResources(); // 宿主资源配置信息
            mResources = new Resources(assetManager, res.getDisplayMetrics(), res.getConfiguration());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3. 启动插件

Activity启动采用任务栈的启动,由于插件Activity没有安装是不能启动的,采用代理Activity启动插件Activity

宿主Activity中,触发启动插件。

    // 启动插件里面的Activity
    public void startPluginActivity(View view) {
        PackageManager packageManager = getPackageManager();
        String pluginPath = PluginManager.getPluginPath().getAbsolutePath();
        PackageInfo packageInfo = packageManager.getPackageArchiveInfo(pluginPath, PackageManager.GET_ACTIVITIES);
//        for (int i = 0; i < packageInfo.activities.length; i++) {
//            Log.e(TAG, "packageInfo.activities:" + packageInfo.activities[i]);
//        }
        // 拿到第一个activity
        ActivityInfo activityInfo = packageInfo.activities[0];
        Intent intent = new Intent(this, ProxyActivity.class);
        intent.putExtra("className", activityInfo.name);
        startActivity(intent);
    }

在代理Activity中做真正的启动,首先要将宿主的环境注入到插件

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String className = getIntent().getStringExtra("className");

        //真正的加载 插件里面的Activity

        // 动态获取className , 不能写死(包名+类名)
        // String className = null;
        try {
            // 拿到插件包中的第一个Activity
            Class pluginClass = getClassLoader().loadClass(className);

            // 实例化插件包中的Activity
            Constructor constructor = pluginClass.getConstructor(new Class[]{});
            Object pluginActivity = constructor.newInstance(new Object[]{});

            // 强转为ActivityInterface
            activityInterface = (ActivityInterface) pluginActivity;

            // 将宿主的环境注入给插件
            activityInterface.insertAppActivity(this);

            // 执行插件onCreate 方法
            // 可以从宿主 携带参数 给 插件
            Bundle bundle = new Bundle();
            bundle.putString("fromAppInfo", "我是来自宿主的一条信息");
            // 间接调用pluginActivity的onCreate方法
            activityInterface.onCreate(bundle);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

插件Activity 的onCreate方法:

	// com.purang.plugin_package.PluginMainActivity#onCreate
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 在父类BaseActivity 通过 宿主环境 appActivity 来加载 Resource
        setContentView(R.layout.activity_main_plugin);

        Log.e(TAG, "onCreate: ");
        // toast 不能通过this,只能通过宿主传递过来的appActivity 环境
        Toast.makeText(appActivity, "我是插件", Toast.LENGTH_SHORT).show();

        // 子类复写了父类的onCreate方法, 继续调用onCreate
        String info = savedInstanceState.getString("fromAppInfo");
        Log.e(TAG, "子类收到宿主传递的信息: " + info);
    }

4. 插件Activity的生命周期

在 宿主的ProxyActivity中,通过activityInterface.onCreate(bundle);启动pluginActivity。因为插件的Activity实现了这一标准。所以,插件的Activity的生命周期也可以通过这种方式来加载:

/**
 * 代理/占位Activity  用来启动插件 Activity
 */
public class ProxyActivity extends Activity {

    private ActivityInterface activityInterface;
    @Override
    protected void onStart() {
        super.onStart();
        if (activityInterface != null)
            activityInterface.onStart();
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
	 	// ... 省略代码
		 // 强转为ActivityInterface
		 activityInterface = (ActivityInterface) pluginActivity;   
		 activityInterface.onCreate(bundle);
      	// ... 省略代码
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (activityInterface != null)
            activityInterface.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (activityInterface != null)
            activityInterface.onPause();
    }
}

插件PluginMainActivity代码:

public class PluginMainActivity extends BaseActivity {

    private static final String TAG = PluginMainActivity.class.getSimpleName();

    @Override
    public void onStart() {
        super.onStart();
        Log.e(TAG, "onStart: ");
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 在父类BaseActivity 通过 宿主环境 appActivity 来加载 Resource
        setContentView(R.layout.activity_main_plugin);
        Log.e(TAG, "onCreate: ");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.e(TAG, "onResume:");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.e(TAG, "onPause:");
    }
}

日志输出:

2020-02-22 22:00:05.065 7863-7863/com.purang.plugin E/PluginMainActivity: onCreate: 
2020-02-22 22:00:05.072 7863-7863/com.purang.plugin E/PluginMainActivity: 子类收到宿主传递的信息: 我是来自宿主的一条信息
2020-02-22 22:00:05.074 7863-7863/com.purang.plugin E/PluginMainActivity: onStart: 
2020-02-22 22:00:05.075 7863-7863/com.purang.plugin E/PluginMainActivity: onResume:
2020-02-22 22:00:27.941 7863-7863/com.purang.plugin E/PluginMainActivity: onPause:

可以看出,先调用了onCreate来加载Activity,然后才是onStart onResume

插件内部 Activity 加载

在插件内部实现跳转到新的Activity实现分析:
由于是在插件内部没有上下文环境,所以所有涉及到上下文环境的操作,都必须借助宿主 ProxyActivity注入给插件的上下文 appActivity来实现。如:findViewById startActivity等等。Activity跳转桥接到ProxyActivity,在ProxyActivity内部实现自己跳转自己

查看Activity task stack

    Running activities (most recent first):
      TaskRecord{459ab29 #11615 A=com.purang.plugin U=0 StackId=1967 sz=3}
        Run #2: ActivityRecord{4528a48 u0 com.purang.plugin/.ProxyActivity t11615}
        Run #1: ActivityRecord{40d3487 u0 com.purang.plugin/.ProxyActivity t11615}
        Run #0: ActivityRecord{43aa009 u0 com.purang.plugin/.MainActivity t11615}

改变ProxyActivity launchMode,尝试采用Activity的四种启动模式,查看任务栈及生命周期方法。具体分析查看

Service通信

service实现思路与activity思路一致,具体查看代码。

动态广播的使用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值