美文网首页
Android高级进阶之-插件化开发原理与实践

Android高级进阶之-插件化开发原理与实践

作者: carlwu_186 | 来源:发表于2018-04-06 23:27 被阅读0次

    写在前面,什么是插件化开发?

    所谓插件化开发就是将APP中的一些功能模块单独抽离出来,打包成可以单独运行的apk包(当然如果需要一些登录态或者运行参数环境时不可以单独运行,但是技术条件上是可以的),当APP程序需要运行这些模块的时候,就可以直接加载这些模块apk,然后运行。

    举个易懂的例子,支付宝内部集成了很多功能模块,其中就有类似淘票票这样的不可能在支付宝一个apk包就全部打包好,这样不仅安装包体积过大,也不利于功能模块的拔插。

    现在给出插件化开发的总体思路,然后我们逐个击破。

    首先宿主APP提供ProxyActivity,当宿主需要打开插件包中的Activity时,一律是启动的ProxyActivity,在启动ProxyActivity的intent中携带我们真正需要去打开的插件包中Activity的类全名。

            Intent intent = new Intent(context, ProxyActivity.class);
            intent.putExtra("className", className);//className 对应插件包中需要被打开的Activity的类全名
            return intent;
    

    ProxyActivity会被正常启动,但是这个ProxyActivity会在其onCreate回调中去反射出这个真实需要被开启的Activity对象,

                Class activityClass = getClassLoader().loadClass(className);
                Constructor constructor = activityClass.getConstructor(new Class[]{});
                Activity instance = constructor.newInstance(new Object[]{});
    

    现在我们拿到了instance对象,当然它不是系统new出来的,它的生命周期方法都需要我们去通知,那么接下来就好办了,我们重写ProxyActivity所有的生命周期方法,然后去手动调用instance相对应的生命周期方法。

    public class ProxyActivity extends Activity {
        private IPluginActivity mPluginActivity;//用来接收插件包所有Activity的接口对象
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            String className = getIntent().getStringExtra("className");
            try {
                Class activityClass = getClassLoader().loadClass(className);
                Constructor constructor = activityClass.getConstructor(new Class[]{});
                Object instance = constructor.newInstance(new Object[]{});
                mPluginActivity = (PluginBaseActivity) instance;
                mPluginActivity.onCreate(savedInstanceState);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            mPluginActivity.onStart();
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            mPluginActivity.onResume();
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            mPluginActivity.onPause();
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            mPluginActivity.onStop();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mPluginActivity.onDestroy();
        }
    }
    

    当然ProxyActivity中还有一些回调方法需要去通知mPluginActivity做出对应的调用,这里不一一例举。

    在Activity中有很重要的两个回调方法:

        @Override
        public ClassLoader getClassLoader() {
            return PluginManager.getInstance().getPluginBean().getDexClassLoader();
        }
    
        @Override
        public Resources getResources() {
            return PluginManager.getInstance().getPluginBean().getResources();
        }
    
    • getClassLoader返回的是ClassLoader 对象,Activity内部在使用反射new对象时,都会去使用这里返回的ClassLoader 来进行反射,所以我们ProxyActivity中需要提供的应该是当前加载插件包对应的ClassLoader 。
    • getResources返回的是Resources 对象,Activity内部在使用资源文件时,都会去使用这里返回的Resources 来获取资源,所以我们ProxyActivity中需要提供的应该是当前加载插件包对应的Resources 。

    相信讲到这里,你大概知道插件化开发打开插件包中某个Activity的原理了:我们宿主APP提供了一个ProxyActivity,我们打开插件包Activity其实都是打开的ProxyActivity,只不过ProxyActivity不做任何其他事情,只负责实例化插件包Activity,并且将ProxyActivity中的所有事件驱动通知给插件包Activity,也就是ProxyActivity调用了插件包Activity中的代码来实现插件包要实现的功能。

    这个时候我们再来看看如何去加载外部插件包apk,并且获得classLoader和resource:

                File pluginFile;//插件包文件
                PackageManager packageManager = context.getPackageManager();
                PackageInfo packageInfo = packageManager.getPackageArchiveInfo(pluginFile.getAbsolutePath(), PackageManager.GET_ACTIVITIES);
    
                File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);
                DexClassLoader dexClassLoader = new DexClassLoader(pluginFile.getAbsolutePath(), dexOutFile.getAbsolutePath()
                        , null, context.getClassLoader());
    
                AssetManager assetManager = AssetManager.class.newInstance();
                Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
                addAssetPath.invoke(assetManager, pluginFile.getAbsolutePath());
                Resources resources = new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
    
    

    上述代码中,PackageInfo 可以获取插件包manifest中所有注册了的组件信息,例如主Activity的全类名的获取为:

    packageInfo.activities[0].name
    

    OK,到了这里我们可以贴上来插件包中,所有Activity都需要继承的BaseActivity代码:

    public abstract class PluginBaseActivity extends AppCompatActivity implements IPluginActivity {
    
        protected Activity that;//宿主Activity
    
        @Override
        public void attach(ProxyActivity proxyActivity) {
            if (that != null)
                throw new RuntimeException("Plugin's activity already has been attached!");
            this.that = proxyActivity;
            attachBaseContext(proxyActivity);
        }
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            if (that == null) {
                super.onCreate(savedInstanceState);
            }
        }
    
        @Override
        public void onStart() {
            if (that == null) {
                super.onStart();
            }
        }
    
        @Override
        public void onResume() {
            if (that == null) {
                super.onResume();
            }
        }
    
        @Override
        public void onPause() {
            if (that == null) {
                super.onPause();
            }
        }
    
        @Override
        public void onStop() {
            if (that == null) {
                super.onStop();
            }
        }
    
        @Override
        public void onDestroy() {
            if (that == null) {
                super.onDestroy();
            }
        }
    
        @Override
        public void onSaveInstanceState(Bundle outState) {
            if (that == null) {
                super.onSaveInstanceState(outState);
            }
        }
    
        @Override
        public void onRestoreInstanceState(Bundle savedInstanceState) {
            if (that == null) {
                super.onRestoreInstanceState(savedInstanceState);
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (that == null) {
                return super.onTouchEvent(event);
            }
            return false;
        }
    
        @Override
        public void onBackPressed() {
            if (that == null) {
                super.onBackPressed();
            }
        }
    
        @Override
        public void setContentView(View view) {
            if (that == null) {
                super.setContentView(view);
            } else {
                that.setContentView(view);
            }
        }
    
        @Override
        public void setContentView(int layoutResID) {
            if (that == null) {
                super.setContentView(layoutResID);
            } else {
                that.setContentView(layoutResID);
            }
        }
    
        @Override
        public void startActivity(Intent intent) {
            if (that == null) {
                super.startActivity(intent);
            } else {//篡改intent打开页面为proxyActivity
                intent.putExtra("className", intent.getComponent().getClassName());
                intent.setClassName(intent.getComponent().getPackageName(), ProxyActivity.class.getName());
                that.startActivity(intent);
            }
        }
    
        @Override
        public ComponentName startService(Intent intent) {
            if (that == null) {
                return super.startService(intent);
            } else {
                intent.putExtra("className", intent.getComponent().getClassName());
                intent.setClassName(intent.getComponent().getPackageName(), ProxyService.class.getName());
                return that.startService(intent);
            }
        }
    
        @Override
        public View findViewById(int id) {
            if (that == null) {
                return super.findViewById(id);
            } else {
                return that.findViewById(id);
            }
        }
    
    
        @Override
        public Intent getIntent() {
            if (that == null) {
                return super.getIntent();
            } else {
                return that.getIntent();
            }
        }
    
    
        @Override
        public Window getWindow() {
            if (that == null) {
                return super.getWindow();
            } else {
                return that.getWindow();
            }
        }
    
        @Override
        public WindowManager getWindowManager() {
            if (that == null) {
                return super.getWindowManager();
            } else {
                return that.getWindowManager();
            }
        }
    }
    
    public interface IPluginActivity {
        void attach(ProxyActivity proxyActivity);
    
        void onCreate(@Nullable Bundle savedInstanceState);
    
        void onStart();
    
    
        void onResume();
    
        void onPause();
    
        void onStop();
    
        void onDestroy();
    
        void onSaveInstanceState(Bundle outState);
    
        void onRestoreInstanceState(Bundle savedInstanceState);
    
        boolean onTouchEvent(MotionEvent event);
    
        void onBackPressed();
    
        void setContentView(View view);
    
        void setContentView(int layoutResID);
    
        void startActivity(Intent intent);
    
        ComponentName startService(Intent intent);
    
        View findViewById(int id);
    
    
        Intent getIntent();
    
    
        Window getWindow();
    
        WindowManager getWindowManager();
    }
    

    IPluginActivity 除了声明一个attach()方法外,其它都是activity默认提供的方法。插件包的PluginBaseActivity 之所以对that做非空判断目的就是为了允许插件包apk可以单独安装并且运行。

    好了,写到这里献上源码。
    链接:https://share.weiyun.com/5ne9oLj

    相关文章

      网友评论

          本文标题:Android高级进阶之-插件化开发原理与实践

          本文链接:https://www.haomeiwen.com/subject/kbiocftx.html