Android插件化框架—— Atlas

作者: 谁动了我的代码 | 来源:发表于2022-11-23 20:36 被阅读0次

    概述

    Atlas 是伴随着手机淘宝不断发展而衍生出来的一个运行于 Android 系统上的插件化框架,也可以叫动态组件化框架,主要提供了解耦化、组件化、动态性的支持。是目前比较成熟的方案,功能强大,但相对的,使用和集成的难度也比较大。

    Atlas是Hadoop的数据治理和元数据框架。 Atlas是一组可扩展和可扩展的核心基础治理服务,使企业能够有效,高效地满足Hadoop中的合规性要求,并允许与整个企业数据生态系统集成。 Apache Atlas为组织提供了开放的元数据管理和治理功能,以建立其数据资产的目录,对这些资产进行分类和治理,并为数据科学家,分析师和数据治理团队提供围绕这些数据资产的协作功能。

    Atlas 的优点:

    • 稳定,成熟,功能强悍
    • 维护团队比较负责,技术实力值得信赖
    • 能承担大体量应用的插件化改造,例如手机淘宝这样的巨型应用
    • 能够实现单 bundle 的快速调试(速度类似于 freeline 增量编译)
    • 具有远程插件和动态部署功能,可以实现热修复和线上版本发布功能

    Atlas 的缺点:

    • 集成较为复杂。
    • 文档很是简略。
    • 本管理较为复杂。
    • 官方的动态部署方法,需要根据版本来下方补丁包。
    • 插件必须要以 library 的形式,如果需要单独打包,需要自己配置 gradle 文件,并且每个 bundle 都得进行 atlas 配置,没有和 atlas 完全分离。
    • 插件跳转必须通过 activity ,如果是旧项目迁移,可能有一定的改造成本。

    图示

    • Atlas支持各种Hadoop和非Hadoop元数据类型
    • 提供了丰富的REST API进行集成
    • 对数据血缘的追溯达到了字段级别,这种技术还没有其实类似框架可以实现
    • 对权限也有很好的控制

    架构原理

    Atlas包括以下组件:

    • 采用Hbase存储元数据
    • 采用Solr实现索引
    • Ingest/Export 采集导出组件 Type System类型系统 Graph Engine图形引擎 共同构成Atlas的核心机制
    • 所有功能通过API向用户提供,也可以通过Kafka消息系统进行集成 Atlas支持各种源获取元数据:Hive,Sqoop,Storm。
    • 还有优秀的UI支持

    效果图

    Atlas项目实践

    前面我们介绍了Atlas的一些原理性的东西,总的来说,Atlas就是利用远程Bundle的下发方式,为了减少apk的安装体积,Atlas项目使用bundle的加载方式。当用户安装没有Bundle的apk文件时,就从网上下载这个bundle的so,然后加载打开,下载逻辑使用app原生代码编写,加载用按需加载的策略。

    Atlas接入

    首先新建一个项目,然后新建几个module,如下图。

    修改配置

    1,把gradle改为3.3

    2,然后我们需要为Atlas添加一些配置,引用Atlas插件及依赖仓库,修改工程gradle文件。

    //不需要再依赖classpath "com.android.tools.build:gradle"
             classpath "com.taobao.android:atlasplugin:2.3.3.beta2"
    

    3,修改app的build.gradle脚本,需要注意包名的对应关系。

    // 需要放最上面初始化
    group = "mmc.atlastest"
    version = getEnvValue("versionName", "1.0.0");
    def apVersion = getEnvValue("apVersion", "");
    
    apply plugin: 'com.android.application'
    apply plugin: 'com.taobao.atlas'
    
    android {
       compileSdkVersion 25
       buildToolsVersion "25.0.2"
       defaultConfig {
           applicationId "mmc.atlastest"
           minSdkVersion 15
           targetSdkVersion 25
           versionCode 1
           versionName version
           testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
       }
       buildTypes {
           release {
               minifyEnabled false
               proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
           }
       }
    }
    
    dependencies {
       compile fileTree(include: ['*.jar'], dir: 'libs')
       androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
           exclude group: 'com.android.support', module: 'support-annotations'
       })
       compile 'com.android.support:appcompat-v7:25.3.1'
       testCompile 'junit:junit:4.12'
    
       //atlas的依赖
       compile('com.taobao.android:atlas_core:5.0.7@aar') {
           transitive = true
       }
       compile 'com.taobao.android:atlasupdate:1.1.4.7@aar'
       compile 'com.alibaba:fastjson:1.1.45.android@jar'
    
       //项目依赖
       compile project(':librarybundle')
       compile project(':localbundle')
       compile project(':remotebundle')
    }
    
    //加入以下配置
    atlas {
       atlasEnabled true
       tBuildConfig {
          // autoStartBundles = ['com.android.homebundle'] //自启动bundle配置
           outOfApkBundles = ['remotebundle'] //远程module,列表来的,可填多个
           preLaunch = 'mmc.atlastest.AtlasLaunch' //AppApplication启动之前调用,这个类下面放出代码
       }
       patchConfigs {
           debug {
               createTPatch true
           }
       }
       buildTypes {
           debug {
               if (apVersion) {
                   // 打差异补丁 gradlew assembleDebug -DapVersion=1.1.0 -DversionName=1.1.1
                   // 对应着本地maven仓库地址 .m2/repository/mmc/atlastest/AP-debug/1.1.4/AP-debug-1.1.4.ap
                   baseApDependency "mmc.atlastest:AP-debug:${apVersion}@ap"
                   patchConfig patchConfigs.debug
               }
           }
       }
    }
    
    String getEnvValue(key, defValue) {
       def val = System.getProperty(key);
       if (null != val) {
           return val;
       }
       val = System.getenv(key);
       if (null != val) {
           return val;
       }
       return defValue;
    }
    
    apply plugin: 'maven'
    apply plugin: 'maven-publish'
    
    publishing {
       // 指定仓库位置
       repositories {
           mavenLocal()
       }
       publications {
           // 默认本地仓库地址  用户目录/.m2/repository/
           maven(MavenPublication) {
               //读取ap目录上传maven
               artifact "${project.buildDir}/outputs/apk/${project.name}-debug.ap"
               //生成本地maven目录
               groupId group
               artifactId "AP-debug"
    

    4,修改远程bundle和本地bundle的build.gradle脚本

    apply plugin: 'com.android.library'
    apply plugin: 'com.taobao.atlas'
    
    atlas {
       bundleConfig{
           awbBundle true
       }
       buildTypes {
           debug {
               baseApFile project.rootProject.file('app/build/outputs/apk/app-debug.ap')
           }
       }
    }
    //只添加上面的配置就行了,下面的是默认生成的
    
    android {
       compileSdkVersion 25
       buildToolsVersion "25.0.2"
    
       defaultConfig {
           minSdkVersion 15
           targetSdkVersion 25
           versionCode 1
           versionName "1.0"
    
           testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    
       }
       buildTypes {
           release {
               minifyEnabled false
               proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
           }
       }
    }
    
    dependencies {
       compile fileTree(include: ['*.jar'], dir: 'libs')
       androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
           exclude group: 'com.android.support', module: 'support-annotations'
       })
       compile 'com.android.support:appcompat-v7:25.3.1'
       testCompile 'junit:junit:4.12'
    
       //依赖lib中间bundle
       compile project(':librarybundle')
    }
    

    5,在宿主app的application中添加如下代码。

    public class DemoApplication extends Application
    
       @Override
       public void onCreate() {
           super.onCreate();
    
           Atlas.getInstance().setClassNotFoundInterceptorCallback(new ClassNotFoundInterceptorCallback() {
               @Override
               public Intent returnIntent(Intent intent) {
                   final String className = intent.getComponent().getClassName();
                   final String bundleName = AtlasBundleInfoManager.instance().getBundleForComponet(className);
        
                   if (!TextUtils.isEmpty(bundleName) && !AtlasBundleInfoManager.instance().isInternalBundle(bundleName)) {
        
                       //远程bundle
                       Activity activity = ActivityTaskMgr.getInstance().peekTopActivity();
                       File remoteBundleFile = new File(activity.getExternalCacheDir(),"lib" + bundleName.replace(".","_") + ".so");
        
                       String path = "";
                       if (remoteBundleFile.exists()){
                           path = remoteBundleFile.getAbsolutePath();
                       }else {
                           Toast.makeText(activity, " 远程bundle不存在,请确定 : " + remoteBundleFile.getAbsolutePath() , Toast.LENGTH_LONG).show();
                           return intent;
                       }
    
    
                       PackageInfo info = activity.getPackageManager().getPackageArchiveInfo(path, 0);
                       try {
                           Atlas.getInstance().installBundle(info.packageName, new File(path));
                       } catch (BundleException e) {
                           Toast.makeText(activity, " 远程bundle 安装失败," + e.getMessage() , Toast.LENGTH_LONG).show();
                           e.printStackTrace();
                       }
        
                       activity.startActivities(new Intent[]{intent});
        
                   }
        
                   return
    

    6、在app新建一个类AtlasLaunch,继承AtlasPreLauncher。

    public class AtlasLaunch implements AtlasPreLauncher
       @Override
       public void initBeforeAtlas(Context context) {
    
       }
    }
    

    项目结构 然后写app的基本功能,示例如下图。

    下面是宿主中具体的跳转逻辑实现。

    public class MainActivity extends BaseActivity
    
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
    
       }
    
       //打开远程bundle
       public void remote(View view){
           Intent intent = new Intent();
           intent.setClassName(view.getContext(), "mmc.remotebundle.RemoteActivity");
           startActivity(intent);
       }
    
       //打开本地bundle
       public void local(View view){
           Intent intent = new Intent();
           intent.setClassName(view.getContext(), "mmc.localbundle.LocalActivity");
           startActivity(intent);
       }
    
       //更新补丁
       public void update(View view){
           new AsyncTask<Void, Void, Void>() {
               @Override
               protected Void doInBackground(Void... voids) {
                  update();
                   return null;
               }
    
               @Override
               protected void onPostExecute(Void aVoid) {
                   Toast.makeText(MainActivity.this, "更新完成,请重启", Toast.LENGTH_LONG).show();
               }
           }.execute();
       }
    
       private void update(){
           File updateInfo = new File(getExternalCacheDir(), "update.json");
           if (!updateInfo.exists()) {
               showToast("更新信息不存在,请先 执行 buildTpatch.sh");
               return;
           }
    
           String jsonStr = new String(FileUtils.readFile(updateInfo));
           UpdateInfo info = JSON.parseObject(jsonStr, UpdateInfo.class);
        
           File patchFile = new File(getExternalCacheDir(), "patch-" + info.updateVersion + "@" + info.baseVersion + ".tpatch");
           try {
               AtlasUpdater.update(info, patchFile);
           } catch (Throwable e) {
               e.printStackTrace();
               showToast("更新失败, "
    

    安装运行项目,就可以看到如下图所示的效果。

    此时还有以下工具需要完成:

    1,这个时候点击远程bundle会弹出说没有so文件,因为还没打so包呢

    2、点击本地bundle,是可以跳转到那个本地bundle页面

    3、点击更新补丁,会提示更新信息不存在

    打远程bundle的so文件 下面要打包出远程bundle的so文件,补丁的差异包和更新说明。

    1,打so文件,每个远程都会生成一个so文件的。打开AS的Terminal,输入:gradlew clean assembleDebug publish,然后回车,如下图:

    正常的话,会提示下面的正确信息。

    成功的话,app的build文件夹里,会生成这个so文件,这个就是远程bundle的so文件,把这个文件放到手机内存卡Android/data/mmc.atlastest/cache 文件夹里面,然后再打开app,点击“远程Bundle”,这个时候就能跳转过去了。


    更新补丁,打差异包和更新说明 接着第一步,然后修改版本号,对本地Bundle进行文字修改,对app主项目也可以修改。

    修改后,在Terminal里面输入:gradlew clean assembleDebug -DapVersinotallow=1.0.0 -Dversinotallow=1.0.1

    回车,成功后会生成补丁差异包和更新说明,如下图:

    把红色圈中的两个文件,放到手机内存卡Android/data/mmc.atlastest/cache 文件夹里面,然后点击“更新补丁”,过一会,提示更新成功后,就退出杀死app,再打开就是后面修改的内容了。

    以上就是Android插件化中的Atlas原理以及实战浅析,如果想更深入学习Android插件化或者进阶核心技术,可以参考《Android核心技术手册》文档,里面技术归纳了几十个知识点。更加前面方便学习,点击查看获取方法。

    Atlas使用步骤总结

    1、配置好,安装1.0.0的app

    2、用命令“gradlew clean assembleDebug publish”打AP,得到远程Bundle的so文件

    3、修改版本号,修改版本内容

    4、用命令“gradlew clean assembleDebug -DapVersion=1.0.0 -DversionName=1.0.1”打差异包补丁和更新说明

    5、把上面得到的三个文件放到app的cache目录里面

    6、运行更新方法,杀死app,重启

    相关文章

      网友评论

        本文标题:Android插件化框架—— Atlas

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