Android外掛化框架—— Atlas

語言: CN / TW / HK

概述

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:[email protected]') { transitive = true } compile 'com.taobao.android:atlasupdate:[email protected]' compile 'com.alibaba:fastjson:[email protected]' ​ //專案依賴 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() { @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技術交流可以前往下方連結:

傳送直達↓↓↓ docs.qq.com/doc/DUkNRVF…

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,重啟