Android插件化技术之旅 1 开篇 - 实现启动插件与调用插件中的Activity和Service

前言

Android技术现在已很成熟了,组件化、插件化、热修复等等框架层出不穷,若是只停留在单纯的会用框架上,技术永远得不到成长,只有懂得其原理,可以婉婉道来,可以本身手动写出,技术才会获得成长,与其焦虑将来,不如把握如今。本篇将手写教你们写出插件化框架,插件化技术是Android高级工程师必备的技术之一,懂其思想,知其原理。本篇专题将由10篇文章来详细的讲解插件化技术,深耕一个技术领域,才能懂得如何更广阔的横向发展。git

本专题代码地址github

什么是插件化?

插件化技术的起源于免安装运行APK的想法,这个免安装的APK就是一个插件,支持插件化的app能够在运行时加载和运行插件,这样即可以将app中一些不经常使用的功能模块作成插件,一方面减少了安装包的大小,另外一方面能够实现app功能的动态扩展。bash

在过去几年有一款叫23code的应用,不知你们是否知道,这款应用大火在于,它能够直接下载demo并运行起来,这能够说是最先的插件化应用。app

如今并无什么新的插件化项目了,比较流行的框架有以下:框架

第一代:dynamic-load-apk最先使用ProxyActivity这种静态代理技术,由ProxyActivity去控制插件中PluginActivity的生命周期。该种方式缺点明显,插件中的activity必须继承PluginActivity,开发时要当心处理context。而DroidPlugin经过Hook系统服务的方式启动插件中的Activity,使得开发插件的过程和开发普通的app没有什么区别,可是因为hook过多系统服务,异常复杂且不够稳定。ide

第二代:为了同时达到插件开发的低侵入性(像开发普通app同样开发插件)和框架的稳定性,在实现原理上都是趋近于选择尽可能少的hook,并经过在manifest中预埋一些组件实现对四大组件的插件化。另外各个框架根据其设计思想都作了不一样程度的扩展,其中Small更是作成了一个跨平台,组件化的开发框架。函数

第三代:VirtualApp比较厉害,可以彻底模拟app的运行环境,可以实现app的免安装运行和双开技术。Atlas是阿里今年开源出来的一个结合组件化和热修复技术的一个app基础框架,其普遍的应用与阿里系的各个app,其号称是一个容器化框架。组件化

插件化的将来:去年比较火的ReactNative,尽管它不可能成为最终方案,可是移动应用Web化是一个必然的趋势,就好像曾经的桌面应用由C/S到B/S的转变。从Google今年推崇的Flutter,移动应用愈来愈趋向于Web化。ui

插件化与组件化的区别?

至于什么是组件化,你们能够看我写的关于组件化的专题this

组件化是将各个模块分红独立的组件,每一个组件都要打进apk包中。

而插件化各个组件均可以单独打成apk包,在须要时下载apk包,并加载运行。

插件化原理

1 设计接纳标准,符合这个标准的app都能接入进来,该标准跟生命周期相关。

2 设计好插件遵循这个标准,因为插件没有安装(上下文均不可用),须要给插件传递一个上下文。

3 用一个空壳Activity 放入插件内容。

4 反射获得Apk里面MainActivity的全类名。

5 加载

image.png

从上图能够看出,启动一个差价,核心代码就是ProxyActivity和PluginInterfaceActivity.下面咱们着重讲解核心思想的代码。

代码其实很简单,启动插件的过程:

  1. 首先须要一个空壳的ProxyActivity来启动插件的Activity。
  2. ProxyActivity根据插件apk包的信息,拿到插件中的ClassLoader和Resource,而后经过反射并建立MainActivity 转换为PluginInterfaceActivity,并调用其onCreate方法。
  3. 插件调用的setContentView被重写了,会去调用ProxyActivity的setContentView,因为ProxyActivity的getClassLoader和gerResource被重写是插件中的resource,因此ProxyActivity的setContentView可以访问插件中的资源,findViewById一样也是调用ProxyActivity的。
  4. ProxyActivity中的其余生命周期回调函数中调用相应PluginActivity的生命周期。

image.png

直接上代码

下面代码定义了插件Activity必需要实现的一个接口,也可说定义的一个标准,因为插件并无安装到手机上,没法拿到上下文,生命周期天然也没法调用,咱们须要将宿主的一个空壳ProxyActivity,将生命周期传递给插件。插件须要用到上下文的方法都须要重写。

public interface PluginInterfaceActivity {

    /**
     * 上下文的传递 经过上下文来启动Activity
     *
     * @param activity
     */
    void attach(Activity activity);

    //---------------------- 生命周期 传递到插件中 -------------------------//

    void onCreate(@Nullable Bundle savedInstanceState);

    void onStart();

    void onResume();

    void onPause();

    void onStop();

    void onRestart();

    void onDestroy();

    void onSaveInstanceState(Bundle outState);

    boolean onTouchEvent(MotionEvent event);

    void onBackPressed();
}
复制代码

重写必需要用到的方法,插件的Activity都须要继承BaseActivity

public class BaseActivity extends AppCompatActivity implements PluginInterfaceActivity {

    protected Activity mActivity;

    @Override
    public void attach(Activity proxyActivity) {
        this.mActivity = proxyActivity;
    }


    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {

    }

    @Override
    public void onStart() {

    }

    @Override
    public void onResume() {

    }

    @Override
    public void onPause() {

    }

    @Override
    public void onStop() {

    }

    @Override
    public void onRestart() {

    }

    @Override
    public void onDestroy() {

    }

    @Override
    public void onSaveInstanceState(Bundle outState) {

    }

    //--------------------------凡事用到上下文的地方都须要重写,须要重写的方法-----------------------------//

    /**
     * 这里咱们要使用宿主传递过来的上下文进行加载view
     *
     * @param view
     */
    @Override
    public void setContentView(View view) {
        if (mActivity != null) {
            mActivity.setContentView(view);
        } else {
            super.setContentView(view);
        }
    }

    @Override
    public void setContentView(int layoutResID) {
        if (mActivity != null) {
            mActivity.setContentView(layoutResID);
        } else {
            super.setContentView(layoutResID);
        }
    }

    @Override
    public <T extends View> T findViewById(int id) {
        if (mActivity != null) {
            return mActivity.findViewById(id);
        } else {
            return super.findViewById(id);
        }
    }

    @Override
    public Intent getIntent() {
        if (mActivity != null) {
            return mActivity.getIntent();
        }
        return super.getIntent();
    }

    @Override
    public ClassLoader getClassLoader() {
        if (mActivity != null) {
            return mActivity.getClassLoader();
        }
        return super.getClassLoader();
    }

    @Override
    public LayoutInflater getLayoutInflater() {
        if (mActivity != null) {
            return mActivity.getLayoutInflater();
        }
        return super.getLayoutInflater();
    }

    @Override
    public ApplicationInfo getApplicationInfo() {
        if (mActivity != null) {
            return mActivity.getApplicationInfo();
        }
        return super.getApplicationInfo();
    }

    @Override
    public Window getWindow() {
        return mActivity.getWindow();
    }


    @Override
    public WindowManager getWindowManager() {
        return mActivity.getWindowManager();
    }

    @Override
    public void startActivity(Intent intent) {
        if (mActivity != null) {
            //插件的launchMode 只可以是标准的
            Intent intent1 = new Intent();
            intent1.putExtra("className", intent.getComponent().getClassName());
            mActivity.startActivity(intent1);
        } else {
            super.startActivity(intent);
        }
    }

    @Override
    public ComponentName startService(Intent service) {
        if (mActivity != null) {
            Intent m = new Intent();
            m.putExtra("serviceName", service.getComponent().getClassName());
            return mActivity.startService(m);
        } else {
            return super.startService(service);
        }
    }
}
复制代码

下面来看宿主是如何启动插件的呢? 经过一个空壳的Activity ProxyActivity代理的方式最先是由dynamic-load-apk提出的,其思想很简单,在主工程中放一个ProxyActivy,启动插件中的Activity时会先启动ProxyActivity,在ProxyActivity中建立插件Activity,并同步生命周期。

public class ProxyActivity extends AppCompatActivity {

    //须要加载插件的全类名
    private String className;

    private PluginInterfaceActivity pluginInterfaceActivity;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        className = getIntent().getStringExtra("className");
//        Class.forName()
        //插件并无被安装到手机上的
        try {
            //className 表明activity的全类名
            Class activityClass = getClassLoader().loadClass(className);
            //调用构造函数
            Constructor constructors = activityClass.getConstructor(new Class[]{});
            //获得Activity对象
            Object newInstance = constructors.newInstance(new Object[]{});
            //最好不要反射 onCreate()
            //经过标准来
            pluginInterfaceActivity = (PluginInterfaceActivity) newInstance;
            //注入上下文
            pluginInterfaceActivity.attach(this);
            Bundle bundle = new Bundle();//将一些信息传递
            bundle.putString("test", "我是宿主给你传递数据");
            pluginInterfaceActivity.onCreate(bundle);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void startActivity(Intent intent) {
        String className = intent.getStringExtra("className");
        Intent intent1 = new Intent(this, ProxyActivity.class);
        intent1.putExtra("className", className);
        super.startActivity(intent1);
    }

    @Override
    public ComponentName startService(Intent service) {
        String serviceName = service.getStringExtra("serviceName");
        Intent intent = new Intent(this, ProxyService.class);
        intent.putExtra("serviceName", serviceName);
        return super.startService(intent);
    }

    //对外
    @Override
    public ClassLoader getClassLoader() {
        return PluginManager.getInstance().getClassLoader();
    }

    @Override
    public Resources getResources() {
        return PluginManager.getInstance().getResources();
    }

    @Override
    protected void onStart() {
        super.onStart();
        pluginInterfaceActivity.onStart();
    }

    @Override
    protected void onResume() {
        super.onResume();
        pluginInterfaceActivity.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        pluginInterfaceActivity.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
        pluginInterfaceActivity.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        pluginInterfaceActivity.onDestroy();
    }
}
复制代码

启动插件的代码以下:

public void loadPlugin(View view) {
        loadDownPlugin();
        Intent intent = new Intent(this, ProxyActivity.class);
        //获得全类名
        intent.putExtra("className", PluginManager.getInstance().getPackageInfo().activities[0].name);
        startActivity(intent);
    }
复制代码

其实,启动插件就是启动宿主的一个空壳的Activity,将这个空壳的Activity的上文和生命周期传递到插件的Activity。

这即是启动插件的核心代码:

//className 表明activity的全类名
            Class activityClass = getClassLoader().loadClass(className);
            //调用构造函数
            Constructor constructors = activityClass.getConstructor(new Class[]{});
            //获得Activity对象
            Object newInstance = constructors.newInstance(new Object[]{});
            //最好不要反射 onCreate()
            //经过标准来
            pluginInterfaceActivity = (PluginInterfaceActivity) newInstance;
            //注入上下文
            pluginInterfaceActivity.attach(this);
            Bundle bundle = new Bundle();//将一些信息传递
            bundle.putString("test", "我是宿主给你传递数据");
            pluginInterfaceActivity.onCreate(bundle);
复制代码

其实咱们就是启动了宿主中的一个空壳Activity,而后加载插件APK包中的资源,并将生命周期传递,那么下面咱们思考一个问题:

插件中的MainActivity调用插件中的OtherActivity,是如何调用的呢?startActivity需不须要重写?

答案是确定的,启用插件中的其余Activity,其实就是从新建立一个新的空壳的Activity。

//重写插件中的startActivity 将要启动的Activity的全类名传递给ProxyActivity
   @Override
    public void startActivity(Intent intent) {
        if (mActivity != null) {
            //插件的launchMode 只可以是标准的
            Intent intent1 = new Intent();
            intent1.putExtra("className", intent.getComponent().getClassName());
            mActivity.startActivity(intent1);
        } else {
            super.startActivity(intent);
        }
    }
    
    //重写ProxyActivity的startActivity,跳转一个新的ProxyActivity,并跳转Activity的全类名传递过去,至关于重走了一边上面的过程
    @Override
    public void startActivity(Intent intent) {
        String className = intent.getStringExtra("className");
        Intent intent1 = new Intent(this, ProxyActivity.class);
        intent1.putExtra("className", className);
        super.startActivity(intent1);
    }
复制代码

ProxyActivity代理方式主要注意两点:

  • ProxyActivity中须要重写getResouces,getAssets,getClassLoader方法返回插件的相应对象。生命周期函数以及和用户交互相关函数,如onResume,onStop,onBackPressedon,KeyUponWindow,FocusChanged等须要转发给插件。
  • PluginActivity中全部调用context的相关的方法,如setContentView,getLayoutInflater,getSystemService等都须要调用ProxyActivity的相应方法。

调用插件中的Service

经过上述的讲解,咱们知道了调用插件中的Activity,其实就是在宿主中建立一个空壳的Acitvity,而后加载插件中的资源,传递上下文。

那么调用插件中的Service呢?原理是同样的,原理是同样的仍是在宿主中建立一个空壳的Service ProxyService,ProxyService 将生命周期传递给插件中的Service

本身能够去实现一下,这里我只把核心代码给出

public class ProxyService extends Service {
    private String serviceName;

    private PluginInterfaceService pluginInterfaceService;

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

    private void init(Intent intent) {
        //开启某个服务的全类名
        serviceName = intent.getStringExtra("serviceName");
        //生成一个class
        DexClassLoader classLoader = PluginManager.getInstance().getClassLoader();
        try {
            Class<?> aClass = classLoader.loadClass(serviceName);
            Constructor<?> constructor = aClass.getConstructor(new Class[]{});
            //获取某个service对象
            Object newInstance = constructor.newInstance(new Object[]{});
            pluginInterfaceService = (PluginInterfaceService) newInstance;
            pluginInterfaceService.attach(this);
            Bundle bundle = new Bundle();
            pluginInterfaceService.onCreate();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (pluginInterfaceService == null) {
            init(intent);
        }
        return pluginInterfaceService.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        pluginInterfaceService.onStart(intent, startId);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        pluginInterfaceService.onConfigurationChanged(newConfig);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        pluginInterfaceService.onLowMemory();
    }

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
        pluginInterfaceService.onTrimMemory(level);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        pluginInterfaceService.onUnbind(intent);
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
        pluginInterfaceService.onRebind(intent);
    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        pluginInterfaceService.onTaskRemoved(rootIntent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        pluginInterfaceService.onDestroy();
    }
}

复制代码

须要注意的是:ProxyActivity和ProxyService必定要在宿主的 Manifest 中注册。

OK,这编文章的讲解是插件化最初的时候出现的设计原理,接下来会一步步深刻讲解。

相关文章
相关标签/搜索