zoukankan      html  css  js  c++  java
  • Adroid动态加载Apk-插件化技术框架(动态代理方案)

    技术:Android + java +动态加载+插件化
     

    概述

    为什么要使用插件化?在开发中,一个项目只会越做越大。初始版本可能是单一功能,后续可能加上各种风马牛不相及的功能。所以我认为插件化可以使得业务分离的更彻底,一人负责哪几个模块,问题也能快速定位。但是也会带来问题:插件和插件之间的交互的复杂性更高、底层支持库因为多个插件需要使用相同的代码可能会变得很大。所以插件化看似解耦了程序员的职责,实际上对于代码质量的要求更高。   实现插件化,最快的方法就是找一个第三方框架。但是要想真正理解,需要真正自己写一个.下面本文就带大家写一个动态加载插件化的框架

    详细

    Android动态加载Apk-插件化技术(动态代理方案)

    一.概述

    为什么要使用插件化?在开发中,一个项目只会越做越大。初始版本可能是单一功能,后续可能加上各种风马牛不相及的功能。所以我认为插件化可以使得业务分离的更彻底,一人负责哪几个模块,问题也能快速定位。但是也会带来问题:插件和插件之间的交互的复杂性更高、底层支持库因为多个插件需要使用相同的代码可能会变得很大。所以插件化看似解耦了程序员的职责,实际上对于代码质量的要求更高。

      要想实现插件化,最快的方法就是找一个第三方框架接入。但是要想真正理解,需要真正自己写一个.下面本文就带大家写一个动态加载插件化的框架



    二. 什么是插件化

    1. 主App(宿主App)加载插件apk的实现

    2. 每个业务组件模块形成一个独立的Apk, 然后通过主App动态加载部署业务组件模块Apk的一种方案

    三.效果演示图&应用场景

    1.效果演示图:

    1551677279143090192.png 1551677395686035818.png

    2.实际开发中,比如微信和支付宝的如下页面就是典型的插件化应用场景

    1551677447098003903.jpg 1551677489063007128.jpg

    三.插件化的优点好处

    1. 业务组件解耦,能够实现业务组件模块的热插拔

    2. 更改产品迭代模式,可分为主App和次Apk(动态加载业务组件模块)

    3. 改善产品更新过程,可以在不影响用户的情况下实现业务组件模块更新以及重要Bug修复

    4. 减轻主App的内存和CPU占用,提高应用的性能.

    四.插件化的思想

    动态加载Apk的主要思想是:主App是被系统(PMS)安装,被系统(AMS)调用,整个过程都是由系统提供的,而插件Apk并非一个真正的Apk,只是一个打包成Apk的一个组件模块,因为它并非被系统安装调用.简言之,需要讲插件Apk看成一个”非Apk”文件,只是一个结构比较复杂的压缩打包成Apk格式的文件.调用插件即用某种特殊技术手段打开文件并执行其相关代码.

    五.插件化的步骤-分析主App

    1.主APp打包完成解压后,会有dex,images,xml,asset等类型文件

    2.Dex靠PathClassLoader加载运行

    3.图片以及xml等资源依靠Resources&AssetManager加载管理

    六.插件化的实现流程

    1551676499854069490.png

    六. 插件化的代码实现步骤

    1.创建DexClassLoader加载插件化Apk相关代码,核心代码如下:

    /**'
     * 创建DexClassLoader
     */
    private DexClassLoader createDexClassLoader(String apkPath) {
        File file = mContext.getDir("dex",Context.MODE_PRIVATE);
        return new DexClassLoader(apkPath,file.getAbsolutePath(),null,mContext.getClassLoader());
    }

    2.创建Resources&AssetManager来加载插件化Apk的资源

    /**
     * 获取到插件中的Resource
     */
    private Resources createResources(AssetManager am) {
        Resources resources = mContext.getResources();
        return new Resources(am,resources.getDisplayMetrics(),resources.getConfiguration());
    }
    
    /**
     * 获取插件的AssetManager
     */
    private AssetManager createAssetManager(String apkPath) {
        try {
            AssetManager am = AssetManager.class.newInstance();
            Method method = AssetManager.class.getDeclaredMethod("addAssetPath",String.class);
            method.invoke(am,apkPath);
            return am;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    3.管理插件Apk里的组件(如Activity)的生命周期

    package com.czm.pluginlib;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    
    /**
     * Created by caizhiming on 2018/3/3.
     */
    
    public interface IPlugin {
    
        int FROM_INTERNAL = 0;//内部跳转
        int FROM_EXTERNAL = 1;//外部跳转
    
        void attach(Activity activity);
    
        void onCreate(Bundle bundle);
        void onStart();
        void onRestart();
        void onActivityResult(int requestCode, int resultCode, Intent data);
        void onResume();
        void onPause();
        void onStop();
        void onDestroy();
    }

    4.通过代理模式实现对插件Apk里面组件的管理

    @Override
    protected void onCreate( Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mClassName = getIntent().getStringExtra("className");
        mPluginApk = XCPluginManager.getInstance().getPluginApk();
    
        launchPluginActivity();
    }
    
    private void launchPluginActivity() {
        if(mPluginApk == null){
            throw new RuntimeException("请先加载插件Apk");
        }
        try {
            //clazz 就是Activity的实例对象,但是该对象没有生命周期,没有上下文环境
            Class<?> clazz = mPluginApk.mDexClassLoader.loadClass(mClassName);
            Object object = clazz.newInstance();
            if(object instanceof IPlugin) {
                mIPlugin = (IPlugin) object;
                mIPlugin.attach(this);
                Bundle bundle = new Bundle();
                bundle.putInt("FROM",IPlugin.FROM_EXTERNAL);
                mIPlugin.onCreate(bundle);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    @Override
    public Resources getResources() {
        if(mPluginApk != null) {
            return mPluginApk.mResources;
        } else {
            return super.getResources();
        }
    }
    
    @Override
    public AssetManager getAssets() {
        if(mPluginApk != null) {
            return mPluginApk.mAssetManager;
        }else {
            return super.getAssets();
        }
    }
    
    @Override
    public ClassLoader getClassLoader() {
        if(mPluginApk != null) {
            return mPluginApk.mDexClassLoader;
        }else {
            return super.getClassLoader();
        }
    }

    以上就是实现插件化的主要过程步骤,具体细节优化读者可以自己扩展优化补充.

    七.项目代码目录结构图

    apkplugin.png

    注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

  • 相关阅读:
    【乱侃】How do they look them ?
    【softeware】Messy code,some bug of Youdao notebook in EN win7
    【随谈】designing the login page of our project
    【web】Ad in security code, making good use of resource
    SQL数据库内存设置篇
    关系数据库的查询优化策略
    利用SQL未公开的存储过程实现分页
    sql语句总结
    sql中使用cmd命令注销登录用户
    SQLServer 分页存储过程
  • 原文地址:https://www.cnblogs.com/demodashi/p/10503465.html
Copyright © 2011-2022 走看看