插件化-占位式插件化

插件化-占位式插件化

Android中的插件化,即将业务解耦封装为一个单独的插件,根据不同的业务需求,进行不同的组合,动态进行替换,并可对插件进行管理、更新,后期对插件也可进行版本管理等操作。

插件化主要概念

  • 宿主:即需要提供运行环境的apk,也就是我们安装在手机上的应用程序。
  • 插件:即独立的功能封装成的小apk,无法直接运行,需要依赖于宿主环境,否则无法运行。用于动态更新内容。
  • 标准:提供给插件使用的我们定义的标准库,用于宿主和插件之间的环境交互。

宿主

宿主中应提供ProxyActivity类,为帮插件中的Activity代理/占位,给插件中提供宿主的环境。其中加载插件中的apk,需要使用到ClassLoader类加载器和Resources资源管理。即在ProxyActivity代理类中重写getClassLoadergetResources方法。

getClassLoader()和getResources()

fun loadPlugin() {
        try {
            val file = File(Environment.getExternalStorageDirectory().toString() + File.separator + DEX_APK_NAME)
            if (!file.exists()) {
                Log.d(TAG, "插件包 不存在...");
                return;
            }

            val pluginPath = file.absolutePath

            //dexClassLoader需要一个缓存地址
            val fileDir = mContext.getDir("myClassLoaderDir",Context.MODE_PRIVATE)
            /**
             * 加载插件中的class
             */
            mDexClassLoader = DexClassLoader(pluginPath, fileDir.absolutePath, null, mContext.classLoader)


            val mAssetManager = AssetManager::class.java.newInstance()

            // 我们要执行此方法,为了把插件包的路径 添加进去
            // public final int addAssetPath(String path) 通过反射获取
            val method = mAssetManager.javaClass.getMethod("addAssetPath", String.javaClass)
            method.invoke(mAssetManager, pluginPath)

            val r = mContext.resources
            /**
             * 加载插件中的资源layout等
             */
            mResources = Resources(mAssetManager, r.displayMetrics, r.configuration)

        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

即我们从服务器下载的apk进行获取,通过DexClassLoader类和Resources类的到ClassLoader和resources,给予宿主环境使用。

标准库

定义Activity、Service、Receiver提供接口标准
示例:

public interface ActivityInterface {

    /**
     * 把宿主(app)的环境  给  插件
     * @param appActivity
     */
    void insertAppContext(Activity appActivity);

    // 生命周期方法
    void onCreate(Bundle savedInstanceState);

    void onStart();

    void onResume();

    void onDestroy();

}
public interface ServiceInterface {
    /**
     * 把宿主(app)的环境  给  插件
     */
    void insertAppContext(Service appService);

    public IBinder onBind(Intent intent);

    public int onStartCommand(Intent intent, int flags, int startId);

    public void onCreate();

    public void onDestroy();
}
public interface ReceiverInterface {

    public void onReceive(Context context, Intent intent);
}

Activity

宿主环境跳转到插件的Activity

val file =
            File(Environment.getExternalStorageDirectory().toString() + File.separator + PluginManager.DEX_APK_NAME)
        val path: String = file.absolutePath

        // 获取插件包 里面的 Activity
        val packageManager = packageManager
        val packageInfo =
            packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES)
        val activityInfo = packageInfo.activities[0]

        // 占位  代理Activity
        val intent = Intent(this, ProxyActivity::class.java)
        intent.putExtra("className", activityInfo.name)
        startActivity(intent)

即获取插件中的定义跳转的Activity,通过PackageManager获取全类名,使用我们的代理类ProxyActivity进行跳转。

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 真正的加载 插件里面的 Activity,获取到需要跳转的全类名
        String className = getIntent().getStringExtra("className");

        try {
            //通过类加载器,加载我们要跳转的类
            Class mPluginActivityClass = getClassLoader().loadClass(className);
            // 实例化 插件包里面的 Activity
            Constructor constructor = mPluginActivityClass.getConstructor(new Class[]{});
            Object mPluginActivity = constructor.newInstance(new Object[]{});
            //获取在插件类中实现的Activity标准ActivityInterface
            ActivityInterface activityInterface = (ActivityInterface) mPluginActivity;

            // 给插件库注入宿主环境
            activityInterface.insertAppContext(this);
            
            //传值
            Bundle bundle = new Bundle();
            bundle.putString("appName", "我是宿主传递过来的信息");

            // 执行插件里面的onCreate方法
            activityInterface.onCreate(bundle);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

其中在我们的插件类中集成BaseActivity实现标准ActivityInterface

public class BaseActivity extends Activity implements ActivityInterface {

    public Activity appActivity; // 宿主的环境

    @Override
    public void insertAppContext(Activity appActivity) {
        this.appActivity = appActivity;
    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onCreate(Bundle savedInstanceState) {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onStart() {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onResume() {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onDestroy() {

    }

    public void setContentView(int resId) {
        appActivity.setContentView(resId);
    }

    public View findViewById(int layoutId) {
        return appActivity.findViewById(layoutId);
    }

    @Override
    public void startActivity(Intent intent) {

        Intent intentNew = new Intent();
        intentNew.putExtra("className", intent.getComponent().getClassName());
        appActivity.startActivity(intentNew);
    }

    @Override
    public ComponentName startService(Intent service) {

        Intent intent = new Intent();
        intent.putExtra("className", service.getComponent().getClassName());
        return appActivity.startService(intent);
    }

    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        return appActivity.registerReceiver(receiver, filter);
    }

    @Override
    public void sendBroadcast(Intent intent) {
        appActivity.sendBroadcast(intent);
    }
}

**注意:在此类中重写需要的方法,使用宿主环境进行调用。**否则会由于在插件中没有可运行的环境而导致崩溃。

插件中的Activity之间的跳转

1、通过调用我们重写的startActivity(Intent intent),其中会使用宿主环境的appActivity.startActivity(intentNew)调用到宿主的startActivity方法。
2、在宿主环境的ProxyActivity中就会调用

@Override
    public void startActivity(Intent intent) {
        String className = intent.getStringExtra("className");

        Intent intentNew = new Intent(this, ProxyActivity.class);
        intentNew.putExtra("className", className);

        // 要给TestActivity 进栈
        super.startActivity(intentNew);
    }

重新调用占位式的ActivityProxyActivity.class,执行onCreate方法,获取全类名通过类加载器再进行跳转操作。

Service

启动Service服务。与Activity类似,即startService(Intent(appActivity, TestService::class.java)),在BaseActivity中重写

@Override
    public ComponentName startService(Intent service) {

        Intent intent = new Intent();
        intent.putExtra("className", service.getComponent().getClassName());
        return appActivity.startService(intent);
    }

调用宿主环境的

@Override
    public ComponentName startService(Intent service) {

        String className = service.getStringExtra("className");

        Intent intentNew = new Intent(this, ProxyService.class);
        intentNew.putExtra("className", className);

        return super.startService(intentNew);
    }

使用代理类ProxyService.class,重写需要的方法,

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        String className = intent.getStringExtra("className");

        try {
            //通过全类名,加载插件中的服务类
            Class mPluginServiceClass = PluginManager.Companion.getInstance(this).getClassLoader().loadClass(className);
            Object mTestServiceInterface = mPluginServiceClass.newInstance();
            //获取服务标准ServiceInterface
            ServiceInterface serviceInterface = (ServiceInterface) mTestServiceInterface;
            //启动插件中的Service的onStartCommand
            serviceInterface.onStartCommand(intent, flags, startId);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return super.onStartCommand(intent, flags, startId);
    }

注意:插件中的Service要实现ServiceInterface

BroadcastReceiver

动态广播

同样ProxyActivity重写registerReceiver(MyReceiver(),intentFilter)sendBroadcast(intent),调用到宿主环境中。
宿主环境:

@Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {

        String pluginMyReceiverClassName = receiver.getClass().getName();

        return super.registerReceiver(new ProxyReceiver(pluginMyReceiverClassName), filter);
    }

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

宿主环境中的代理Receiver:

public class ProxyReceiver extends BroadcastReceiver {

    private String mPluginReceiverClassName;

    public ProxyReceiver(String mPluginReceiverClassName) {
        //完整类名
        this.mPluginReceiverClassName = mPluginReceiverClassName;
    }

    @Override
    public void onReceive(Context context, Intent intent) {

        try {
            Class receiver = PluginManager.Companion.getInstance(context).getClassLoader().loadClass(mPluginReceiverClassName);
            Object serviceData = receiver.newInstance();

            ReceiverInterface receiverInterface = (ReceiverInterface) serviceData;

            receiverInterface.onReceive(context, intent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

静态广播

静态广播需要在AndroidManifest.xml中注册,所以只能通过解析apk,拿到apk中注册的静态广播信息进行注册处理。
分析:

  1. apk应用在data/app下,通过PackManagerService的File systemAppDir = new File(Environment.getRootDirectory(), “app”);得到其中的数据
  2. 调用scanDirLI->scanPackageChildLI方法扫描其中的数据,返回PackageParser.Package,即返回此APK文件解析的完整软件包,其中我们可以看到public final ArrayList receivers = new ArrayList(0);所有的静态广播集合
  3. 遍历receivers,得到PackageParser类下的Activity类,其中参数ActivityInfo的父类中包含public String name;即:Public name of this item. From the “android:name” attribute.拿到
  4. 通过反射得到PackageParser类下的generateActivityInfo方法获取到ActivityInfo,得到receiverClassName静态注册全类名,最后调用mContext.registerReceiver(broadcastReceiver, intentFilter)进行注册

在宿主环境中拿到静态广播信息,代码如下:

/**
     * 解析APK,得到静态注册的广播的清单文件,并拿到
     * 使用反射
     */
    fun parserApkAction() {
        try {

            val file = File(Environment.getExternalStorageDirectory().toString() + File.separator + DEX_APK_NAME)
            if (!file.exists()) {
                Log.d(TAG, "插件包 不存在...");
                return;
            }

            val pluginPath = file.absolutePath

            //通过 PackageParse中的parsePackage(File packageFile, int flags)用来解析apk包
            val packageParseClass = Class.forName("android.content.pm.PackageParser")
            //得到类的实体
            val packageParseAny = packageParseClass.newInstance()

            //拿到parsePackage方法
            val packageParseMethod = packageParseClass.getMethod("parsePackage", File::class.java, Int::class.java)
            //执行此方法拿到Package
            val mPackage = packageParseMethod.invoke(packageParseAny, file, PackageManager.GET_ACTIVITIES)

            //Package中的receivers为注册的广播的集合,需要拿到这个集合
            val receiversField = mPackage.javaClass.getDeclaredField("receivers")
            //得到他的实例
            val receivers = receiversField.get(mPackage)

            val arrayList = receivers as ArrayList<*>

            // 此Activity 不是组件的Activity,是PackageParser里面的内部类
            for (activity: Any in arrayList) { // mActivity --> <receiver android:name=".StaticReceiver"> 即在ActivityInfo中

                // 获取 <intent-filter>    intents== 很多 Intent-Filter
                // 通过反射拿到 intents
                val mComponentClass =
                    Class.forName("android.content.pm.PackageParser\$Component")
                val intentsField: Field = mComponentClass.getDeclaredField("intents")
                val intents: ArrayList<IntentFilter> = intentsField[activity] as ArrayList<IntentFilter>

                //需要获取PackageUserState
                val packageUserState = Class.forName("android.content.pm.PackageUserState")

                //获取userId
                val mUserHandle =
                    Class.forName("android.os.UserHandle")
                val userId = mUserHandle.getMethod("getCallingUserId").invoke(null) as Int

                //获取ActivityInfo拿到他的name就是.StaticReceiver具体类名
                //ActivityInfo generateActivityInfo(Activity a, int flags,
                //            PackageUserState state, int userId)
                val generateActivityInfoMethod = packageParseClass.getDeclaredMethod("generateActivityInfo",activity.javaClass, Int::class.java, packageUserState, Int::class.java)
                generateActivityInfoMethod.isAccessible = true
                val activityInfo = generateActivityInfoMethod.invoke(null,activity, 0, packageUserState.newInstance(), userId) as ActivityInfo
                val receiverClassName = activityInfo.name

                val mStaticReceiverClass = getClassLoader().loadClass(receiverClassName)

                val broadcastReceiver = mStaticReceiverClass.newInstance() as BroadcastReceiver

                for (intentFilter: IntentFilter in intents) {
                    // 注册广播
                    mContext.registerReceiver(broadcastReceiver, intentFilter)
                }
            }

        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值