概念
atlas:容器化框架,提供組件化開發(fā),熱更新。

如上圖所示,atlas主要分為以下幾個(gè)層級:
- 最底下的hack工具層: 包括了容器所需的所有系統(tǒng)層面的注入和hack的工具類初始化和校驗(yàn),容器啟動(dòng)時(shí)先校驗(yàn)設(shè)備是否支持容器運(yùn)行,不支持則采取降級并記錄原因;
- Bundle Framework 負(fù)責(zé)bundle的安裝 更新 操作以及管理整個(gè)bundle的生命周期;
- runtime層:主要包括清單管理、版本管理、以及系統(tǒng)代理三大塊,基于不同的觸發(fā)點(diǎn)按需執(zhí)行bundle的安裝和加載;runtime層同時(shí)提供了開發(fā)期快速debug支持和監(jiān)控兩個(gè)功能模塊。從Delegate層可以看到,最核心的兩個(gè)代理點(diǎn):一個(gè)是DelegateClassLoader:負(fù)責(zé)路由class加載到各個(gè)bundle內(nèi)部,第二個(gè)是DelegateResource:負(fù)責(zé)資源查找時(shí)能夠找到bundle內(nèi)的資源;這是bundle能夠真正運(yùn)行起來的根本;其余的代理點(diǎn)均是為了保證在必要的時(shí)機(jī)按需加載起來目標(biāo)bundle,讓其可以被DelegateClassloader和DelegateResource使用
- 對外接入層:AtlasBridgeApplication是atlas框架下apk的真正Application,在基于Atlas框架構(gòu)建的過程中會(huì)替換原有manifest中的application,所以Atlas沒入的接入并不存在任何初始化代碼,構(gòu)建腳本完成了接入的過程。AtlasBridgeApplication里面除了完成了Atlas的初始化功能,同時(shí)內(nèi)置了multidex的功能,這樣做的原因有兩個(gè):
很多大型的app不合理的初始化導(dǎo)致用multidex分包邏輯拆分的時(shí)候主dex的代碼就有可能方法數(shù)超過65536,?AtlasBridgeApplication與業(yè)務(wù)代碼完全解耦,所以拆分上面只要保證atlas框架在主dex,其他代碼無論怎么拆分都不會(huì)有問題;
如果不替換Application,那么atlas的初始化就會(huì)在application里面,由于基于Atlas的動(dòng)態(tài)部署實(shí)際上是類替換的機(jī)制,那么這種機(jī)制就會(huì)必然存在包括Application及其import的class等部分代碼在dalvik不支持部署的情況,這個(gè)在使用過程中造成一定成本,需要小心的使用以避免dalivk內(nèi)部class resolve機(jī)制導(dǎo)致部分class沒成功,替換以后該問題得到最好的解決,除atlas本身以外,所有業(yè)務(wù)代碼均可以動(dòng)態(tài)部署;
另外內(nèi)置的原生的multidex在dalvik上面性能并不好,atlas內(nèi)部對其進(jìn)行了優(yōu)化提高了在dalvik上面的體驗(yàn)。
除AtlasBridgeApplication之外,接入層對外提供了部分工具類,包括主動(dòng)install bundle,start bundle,以及獲取全局的application等各種功能。
集成
-
工程項(xiàng)目的build.gradle文件依賴插件:
image -
app目錄下的build.gradle使用插件,并且添加相關(guān)依賴
image
image
image 加載自啟動(dòng)的bundle
switchToActivity("home","com.taobao.firstbundle.FirstBundleActivity");
//通過類名來調(diào)用
public void switchToActivity(String key,String activityName){
Intent intent = new Intent();
intent.setClassName(getBaseContext(),activityName);
activityGroupDelegate.startChildActivity(framelayout,key,intent);
}

可以看到現(xiàn)在的fistBundle中彈出了一個(gè)toast,message 為“更新一下”,下面修改firstBundle中的代碼,然后熱更新一下
修改toast的message
@Override
protected void onResume() {
super.onResume();
Toast.makeText(this, "單模塊部署1111111", Toast.LENGTH_SHORT).show();
}
bundle熱更新步驟
1、 app的build.gradle的語句"version = getEnvValue("versionName", "1.0.0");"中修改想要生成的app的versionName(默認(rèn)為1.0.0)
app目錄下執(zhí)行../gradlew clean assembleDebug publish
(生成apk同時(shí)將跟apk同目錄的ap文件發(fā)布到倉庫)
2、 手機(jī)上安裝apk,同時(shí)進(jìn)到動(dòng)態(tài)部署界面(側(cè)邊欄里面劃開點(diǎn)擊進(jìn)入),且手機(jī)連接電腦adb(確保adb devices可見)
///////////////////////////////^^^^^^準(zhǔn)備工作^^^^^^^^^////////////////////////
3、 進(jìn)行一些想要的修改(暫時(shí)不支持manifest的修改,會(huì)在近期上線)
4、 app工程目錄下執(zhí)行../gradlew clean assembleDebug -DapVersion=apVersion -DversionName=newVersion,
其中apVersion為之前打的完整apk的版本,newVersion為此次動(dòng)態(tài)部署要生成的新的版本號(hào)
5、 檢查build/output/tpatch-debug 目錄下文件是否生成,然后執(zhí)行下面的命令(以下為mac下的命令,windows請修改文件分隔符)
adb push build/outputs/tpatch-debug/update.json /sdcard/Android/data/cx.com.atlasdemo11111/cache/update.json
adb push build/outputs/tpatch-debug/patch-*.tpatch /sdcard/Android/data/cx.com.atlasdemo11111/cache
6、 點(diǎn)擊動(dòng)態(tài)部署頁面紅色按鈕執(zhí)行動(dòng)態(tài)部署
更改firstBundle中的代碼,然后
在app目錄下執(zhí)行命令,app/build/outputs/ 會(huì)出現(xiàn)
//DapVersion基準(zhǔn)版本號(hào) DversionName更新版本號(hào)
../gradlew clean assembleDebug -DapVersion=1.0.0 -DversionName=1.0.1
命令跑完后就會(huì)出現(xiàn)下面的目錄結(jié)構(gòu)

執(zhí)行
adb push build/outputs/tpatch-debug/update.json /sdcard/Android/data/cx.com.atlasdemo11111/cache/update.json
adb push build/outputs/tpatch-debug/patch-*.tpatch /sdcard/Android/data/cx.com.atlasdemo11111/cache
然后點(diǎn)擊動(dòng)態(tài)部署按鈕,效果如下

可以看到熱更新已經(jīng)成功。
遠(yuǎn)程bundle使用
- 遠(yuǎn)程bundle必須在gradle中配置,如上圖
atlas {
atlasEnabled true
tBuildConfig {
autoStartBundles = ['com.taobao.firstbundle'] //自啟動(dòng)bundle配置
outOfApkBundles = ['remotebundle']//配置遠(yuǎn)程bundle
}
manifestOptions{
addAtlasProxyComponents true
}
patchConfigs {
debug {
createTPatch true
}
}
buildTypes {
debug {
if (apVersion) {
baseApDependency "com.taobao.android.atlasdemo:AP-debug:${apVersion}@ap"
patchConfig patchConfigs.debug
}
}
}
}
2.在Application中添加安裝遠(yuǎn)程bundle的回調(diào)
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)) {
//遠(yuǎn)程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, " 遠(yuǎn)程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, " 遠(yuǎn)程bundle 安裝失敗," + e.getMessage() , Toast.LENGTH_LONG).show();
e.printStackTrace();
}
activity.startActivities(new Intent[]{intent});
}
return intent;
}
});
3.把a(bǔ)pp/build/outputs/remote-bundles-debug/libcom_taobao_remotebundle.so 放到sd卡的cache目錄,執(zhí)行命令
adb push build/outputs/remote-bundles-debug/libcom_taobao_remotebunle.so /sdcard/Android/data/cx.com.atlasdemo11111/cache/libcom_taobao_remotebunle.so
點(diǎn)擊添加遠(yuǎn)程bundle看效果

可以看到已經(jīng)將遠(yuǎn)程bundle加載成功。
在自己集成的demo中也有很多的坑。跑命令失敗的情況。記錄一下
集成atlas時(shí)的坑
- app工程目錄下執(zhí)行../gradlew clean assembleDebug -DapVersion=apVersion -DversionName=newVersion
這個(gè)命令中的apVersion必須是存在的版本號(hào),比如在gradle中設(shè)置的1.0.0,不然會(huì)報(bào)找不到依賴
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "cx.com.atlasdemo11111"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName version //這里的versionName要和腳本中的一致,如果設(shè)置死,在熱修復(fù)merge的時(shí)候不會(huì)起作用
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
3.這個(gè)版本的單模塊調(diào)試模擬還不穩(wěn)定
atlas不得不說是良心之作,這個(gè)demo只是簡單地集成,以供參考。


