Skip to content

Commit

Permalink
demo guide and odex verifer
Browse files Browse the repository at this point in the history
  • Loading branch information
关杰 committed Mar 23, 2017
1 parent 3b133ce commit daadde3
Show file tree
Hide file tree
Showing 19 changed files with 132 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ public KernalBundleArchive(Context context, File bundleDir, String wantedRevisio
//
// }
// },bundleFile,true);
boolean success = new KernalBundleRelease(revisionDir).release(bundleFile,true);
boolean success = new KernalBundleRelease(revisionDir,true).release(bundleFile,true);

if (!success||odexFile == null){
throw new IOException("process patch failed!");
Expand Down Expand Up @@ -452,7 +452,7 @@ private KernalBundleArchive(final File bundleDir, File file, int revNum,String c
// }
// },bundleFile,false);
// Looper.loop();
boolean success = new KernalBundleRelease(revisionDir).release(bundleFile,false);
boolean success = new KernalBundleRelease(revisionDir,false).release(bundleFile,false);
if (!success||odexFile == null){
throw new IOException("process mainDex failed!");
}
Expand Down Expand Up @@ -664,8 +664,8 @@ public static void copyInputStreamToFile(InputStream input, File file) throws IO
*/
public class KernalBundleRelease{
private BundleReleaser mBundlereleaser;
public KernalBundleRelease(File dir) {
mBundlereleaser = new BundleReleaser(dir);
public KernalBundleRelease(File dir,boolean hasReleasedBefore) {
mBundlereleaser = new BundleReleaser(dir,hasReleasedBefore);
}

public boolean release(final File bundleFile,final boolean start) throws IOException{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
Expand All @@ -243,17 +244,19 @@ public class BundleReleaser {
private Handler handler;
private ProcessCallBack processCallBack;
private File apkFile;
private boolean hasReleased;

public DexFile[] getDexFile() {
return dexFiles;
}

private DexFile[] dexFiles = null;

public BundleReleaser(final File reversionDir) {
public BundleReleaser(final File reversionDir,boolean hasReleased) {
if(Boolean.FALSE.booleanValue()){
String.valueOf(PreVerifier.class);
}
this.hasReleased = hasReleased;
this.reversionDir = reversionDir;
if (!(Looper.getMainLooper() == Looper.myLooper())) {
Looper.prepare();
Expand Down Expand Up @@ -426,7 +429,7 @@ public void run() {
String optimizedPath = optimizedPathFor(validDexes[j], dexOptDir());
try {
dexFiles[j] = DexFile.loadDex(validDexes[j].getPath(), optimizedPath, 0);
boolean result = verifyDexFile(dexFiles[j]);
boolean result = verifyDexFile(dexFiles[j],optimizedPath);
if (!result) {
handler.sendMessage(handler.obtainMessage(MSG_ID_RELEASE_FAILED));
}
Expand Down Expand Up @@ -459,11 +462,23 @@ public void run() {
handler.sendMessage(handler.obtainMessage(MSG_ID_DEX_OPT_DONE));
}

private boolean verifyDexFile(DexFile dexFile) throws IOException {
private boolean verifyDexFile(DexFile dexFile,String optimizedPath) throws IOException {
if (dexFile != null) {
if (checkDexValid(dexFile)) {
return true;
if (!checkDexValid(dexFile)) {
return false;
}
if(!hasReleased) {
try {
Class OdexVerifierClass = getClass().getClassLoader().loadClass("android.taobao.atlas.util.OdexVerifier");
Method isOdexValidMethod = OdexVerifierClass.getDeclaredMethod("isOdexValid", String.class);
isOdexValidMethod.setAccessible(true);
boolean result = (Boolean) isOdexValidMethod.invoke(OdexVerifierClass,optimizedPath);
if(!result){
return false;
}
}catch(Throwable e){}
}
return true;
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package android.taobao.atlas.util;

import com.taobao.android.runtime.AndroidRuntime;

import java.io.Serializable;


public class OdexVerifier implements Serializable{

public static boolean isOdexValid(String odexPath){
return AndroidRuntime.getInstance().isOdexValid(odexPath);
}
}
95 changes: 95 additions & 0 deletions atlas-demo/Atlas Demo快速接入.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
Atlas Demo已经具备了一个小型app的基本框架,本demo中主要展示了Atlas的主客户端、业务bundle、业务bundle独有library、中间件library、远程bundle、公共bundle、闪屏等特性。
#工程导入
Atlas Demo地址https://github.com/alibaba/atlas/tree/master/atlas-demo/AtlasDemo 。git等安装详情不表。
Atlas Demo基于gradle,使用Android Studio打开。Android开发环境搭建详情不表。
需要注意的是Atlas是基于gradle的最新版本3.3的,如果没有安装gradle 3.3的用户使用Android Studio第一次启动会卡住,这里建议在使用Android Studio启动之前可以先在终端使用`./gradlew build`编译工程,如果等待时间依旧过长,可以选择以下两种方式:
1、翻墙(推荐)。因为Atals Demo除了gradle还需要其他一下远程资源。
2、手动下载gradle-3.3-all.zip,拷贝到相应的grdle安装目录(mac默认为/Users/<用户名>/.gradle/wrapper/dists目录,Windows默认为C:\Users\计算机名.gradle\wrapper\dists目录)。
#工程框架
我们从几个方面结合起来共同来看Atlas Demo的工程框架,也会大家开发更为复杂的APP提供帮助。
##工程目录
<img src="./img/dictionary.png" width="30%" alt="工程目录.png"/>

app目录:主客户端代码
firstbundle目录:第一个业务bundle代码
secondbundle目录:第二个业务bundle代码
secondbundlelibrary目录:第二个业务bundle单独依赖的library代码
remotebundle目录:远程bundle代码(远程bundle在APK发布时不会编译进APK内,而是在客户端使用时下载后加载)
publicbundle目录:公共bundle代码(不会打入主dex)
middlewarelibrary目录:中间件library(会打入主dex)
splashscreen目录:闪屏代码
lottie目录:splahscreen的依赖代码
activitygroupcompat目录:实现展示bundle 内的Fragment,因为bundle只通过Component的方式可以被依赖安装
##./strutor.md
<img src="./img/structer.png" width="80%" alt="strutor.md.png"/>

Atlas Demo作者也在structor.md里面列出了demo的依赖关系,其中`mainDex指的是会被编译进主dex的依赖`;awbs指的是各个bundle,它们将以`so文件的形式放在APK的lib文件夹`下(这些个so文件其实就是各个业务bundle的独立APK,只不过这个APK可能要依赖容器里面的中间件代码才能运行,只是后缀改成了so而已),从awbs中也可以看出secondbundlelibrary只被secondbundle依赖。
##app/build.gradle
<img src="./img/buildgradle.png" width="80%" alt=" buildgradle.png"/>

我们再来看下主客户端的build.gradle代码,如上图所示,从中可以看到业务bundle都是使用Atlas自定义的编译命令`bundleCompile`来进行依赖,其余需要编译进主dex的依赖都是使用原生`compile`命令进行依赖,使用atlas功能的主客户端代码需要依赖atlasplugin、atlas_core和atlasupdate。
##APK解压后的文件目录(构建APK参见“主APK构建”章节)
<img src="./img/apk目录.png" width="80%" alt=" apk解压.png"/>

通过解压编译出来的APK我们也可以发现firstbundle,secondbundle,publicbundle是以so包的形式放在了lib/armeabi目录下面,注意这里并没有remotebundle的so文件,remotebundle的加载方式将在下文介绍。strutor.md中mainDex下的bundle都编译进了class.dex中。

#Activity跳转关系
Atlas Demo里面的Activity分布比较散,为了方便大家的快速入手,这里简单介绍下个Activity的跳转关系。
##WelcomeActivity
splashscreen bundle是会被编译进主dex里面的,从spllashscreen的manifest文件(splashscreen/src/main/AndroidManifest.xml)我们可以看到,WelcomeActivity是接收“android.intent.action.MAIN”和“android.intent.category.LAUNCHER”的Activity,WelcomeActivity是APP的入口Activity。
<img src="./img/manifest.png" width="80%" alt=" manifest.png"/>
##WelcomeActivity->MainActivity
在WelcomeActivity.java(splashscreen目录)中的onPostCreate通过startActivity跳转到APP的首页MainActivity(app目录)。
<img src="./img/welcomepostcreate.png" width="80%" alt=" WelcomeActivity.png"/>
##MainActivity->FirstBundleActivity
MainActivity其实是个壳子Activity,在其onCreate函数中我们可以看到它其实是默认加载了firstbundle的FirstBundleActivity,并设置了导航栏,关于导航栏的具体实现细节大家可以参考activitygroupcompat bundle。
<img src="./img/mainactivityoncreate.png" width="90%" alt=" MainActivity.png"/>
##MainActivity->FirstBundleActivity、MainActivity->SecondBundleActivity
通过导航栏我们可以实现在FirstBundleActivity和SecondBundleActivity之间进行跳转,FirstBundleActivity和SecondBundleActivity是属于不同的bundle,通过这种方式我们就实现了不同bundle的Activity在同一个APP中的展示。
<img src="./img/mainactivitynav.png" width="90%" alt=" MainActivity.png"/>

#场景构建
Atlas Demo里面集成了主APK构建、动态部署构建、远程bundle构建等功能,下面我们进行单独介绍。
##问题1:如何构建一个APK,发布到各个应用市场?
###主APK构建步骤:
1、Atlas Demo中将主客户端代码和所有bundle都放在了一个工程下面,这也符合gradle工程的标准框架格式。
2、各个业务bundle可以根据业务需求修改更新各自bundle的代码。然后再通过修改app/build.gradle中的version = getEnvValue("versionName", "1.0.0");来修改版本号,版本号默认为1.0.0。
3、然后再在app目录下面执行`../gradlew clean assembleDebug publish`命令从而进行APK打包,生成的APK文件目录是app/build/outputs/apk/app-debug.apk,上述命令也会将该目录下的ap文件发布到仓库以便于后续生成patch。
4、如果有手机连接在pc上可以直接使用adb install安装上述APK,在app目录下执行`adb install app/build/outputs/apk/app-debug.apk`

##问题2:如何在用户无感知的情况下,实现所有业务模块的独立动态更新?
###动态部署(patch)构建步骤:
1、各个业务bundle可以根据业务需求修改更新各自bundle的代码。
2、app工程目录下执行`../gradlew clean assembleDebug -DapVersion=apVersion -DversionName=newVersion`, 其中apVersion为之前打的完整APK的版本,newVersion为此次动态部署要生成的新的版本号。
这里举一个简单的例子,例如“主APK构建”步骤的APK版本是1.0.0,然后我简单更新firstbundle的string.xml里面的hello_blank_fragment属性为“this is fragment of firstBundle”,然后执行`../gradlew clean assembleDebug -DapVersion=1.0.0 -DversionName=1.0.1`
<img src="./img/_firstbundlepatch.png" width="100%" alt=" MainActivity.png"/>

3、检查build/output/tpatch-debug 目录下文件是否生成,将远程patch下载到你的设备上(主动下载和被动推送都可以,这里直接在PC上执行下面的命令(以下为mac下的命令,windows请修改文件分隔符):
` adb push build/outputs/tpatch-debug/update.json /sdcard/Android/data/com.taobao.demo/cache/update.json`
` adb push build/outputs/tpatch-debug/patch-*.tpatch /sdcard/Android/data/com.taobao.demo/cache`
4、打开Demo侧边栏,点击“动态部署模拟”,页面红色按钮执行动态部署,等待几秒钟之后APP会关闭此时说明动态部署已经成功,重启APP后就会发现自己改动的代码已经更新成功。
<img src="./img/app动态部署.png" width="40%" alt="动态部署.png"/> <img src="./img/appfirstbundlepatch.png" width="40%" alt="动态部署结果.png"/>

5、感兴趣的同学可以研究下update.json和tpatch文件
6、动态部署的主要优势:摆脱版本迭代周期限制,新增需求灵活发布;降低版本频繁发布给用户带来困扰;发现问题及时回滚;短时间内更高的覆盖率

##问题3:如果在APK发布时,我不想把某个bundle编译进APK,而是在客户端使用时下载后加载,该怎么办?
###远程bundle构建步骤:
1、添加远程bundle的依赖,参考 app/build.gradle下的 `bundleCompile project(':remotebundle')`
2、声明远程bundle列表,参考 app/build.gradle下的`atlas { tBuildConfig { outOfApkBundles = ['remotebundle'] }`
3、构建完整包,在app目录下执行`../gradlew clean assembleDebug publish`,远程bundle 路径:app/build/outputs/remote-bundles-debug。
4、将远程so下载到你的设备上(主动下载和被动推送都可以),这里直接在PC上执行`adb push app/build/outputs/remote-bundles-debug/libcom_taobao_remotebunle.so /sdcard/Android/data/com.taobao.demo/cache/libcom_taobao_remotebunle.so`
5、打开Demo侧边栏,点击“远程组件模拟”,点击“加载远程bundle”,加载成功后就会跳到remotebundle的页面。

<img src="./img/appremotebundle.png" width="40%" alt="动态部署.png"/>

#调试工具
##单bundle调试(供线下调试使用,当只更改了单个bundle的代码时,无需对整个APP进行动态部署,可以一键进行单bundle的部署调试)
1、在设备上安装APP,设备ADB连接电脑成功。
2、修改一个bundle工程的代码或者资源(设置生效的标识)。
例如,我们这里在firstbundle中的FirstBundleActivity的onCreate的中加一个Toast提示,如下
<img src="./img/toast.png" width="100%" alt="单bundle.png"/>
3、bundle工程的目录下执行`../gradlew clean assemblePatchDebug`,然后等应用重启或者应用关闭后点击重启")就可以看到代码生效。
根据上述改动,我们在firstbundle的目录下执行`../gradlew clean assemblePatchDebug`,然后就可以看到客户端重启,Toast提示生效。

<img src="./img/apptoast.png" width="40%" alt="单bundle.png"/>
Binary file added atlas-demo/img/_firstbundlepatch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/apk目录.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/appfirstbundlepatch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/appremotebundle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/apptoast.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/app动态部署.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/buildgradle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/dictionary.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/firstbundlepatch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/mainactivitynav.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/mainactivityoncreate.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/manifest.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/structer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/toast.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added atlas-demo/img/welcomepostcreate.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit daadde3

Please sign in to comment.