简介

App Bundle 是一种新的安卓编译打包方式,编译工具可以根据CPU架构类型、屏幕分辨率、语言等维度将一个传统的App 打包成一个App集合;用户下载App时,应用市场会根据终端三种维度的类型提供仅适配此终端的App子集,从而在提供相同业务功能的前提下,节省用户的网络流量与终端设备空间。

集成DynamicAbility SDK到Android项目中后,开发者可以与华为应用市场交互,利用AppBundle技术,对app中的某些模块实现动态的加载。

您将会学到什么

硬件要求

软件要求

创建一个Android Project

  1. 打开Android Studio, File>New>New Project
  2. 选择 Phone and Tablet > Empty Activity > Next
  3. 配置工程相关属性(工程名、Package name 、API Level 等等)
  4. 点击Finish 完成工程创建。
    在本例中,该module属于Android"Application"类型。换句话说,任何一个属于"Application"类型的module均可以作为基础模块Base App。
  1. 打开刚刚创建的Android Project中,以此选择File>New>New Module;
  2. 选择"Dynamic Feature Module",点击"Next";
  3. 选择"Base application module"为刚刚创建的基础模块Base App;然后配置Module name和Package name等信息;最后点击"Next";
  4. 配置Module Tile和Install-time inclusion;Module Tile表示该模块的标题,是用户可见的;
  5. 点击"Finish"完成创建Dynamic Feature Module。

检查配置项

  1. 检查Base App模块下的build.gradle文件。在该文件中,android闭包中存在如下配置:
    android { // 省略不相关配置... ... // 表示Base App关联了Dynamic Feature Module dynamicFeatures = [":demofeature"] }
  2. 检查Dynamic Feature Module下的build.gradle文件。在该文件中,dependencies闭包中存在如下配置:
    dependencies { // 省略不相关配置... ... // 表示该module依赖了Base App implementation project(':app') }
  3. 上述两处配置在创建Dynamic Feature Module的时候会自动完成,不需要开发者手动配置。

引入Dynamic Ability SDK

  1. 在项目中Base App的build.gradle文件中,加入如下依赖:
    dependencies { implementation 'com.huawei.hms:dynamicability:1.0.11.302' ... }
  2. 在项目中Base App配置Application,并override其中的attachBaseContext()方法,加入SDK的启动代码。
    public class MyApplication extends Application { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); // 启动Dynamic Ability SDK FeatureCompat.install(base); } }
  3. 在feature中的Activity增加如下配置。
    @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); // 启动Dynamic Ability SDK FeatureCompat.install(base); }

在Base App中加载Dynamic Feature Module

引入并启动Dynamic Ability SDK之后,便需要开发者根据业务需求,调用该SDK将Dynamic Feature Module加载起来,即实现按需加载的功能。

  1. 实例化 FeatureInstallManager ,统一管理加载的整个过程。
    FeatureInstallManager mFeatureInstallManager; mFeatureInstallManager = FeatureInstallManagerFactory.create(this);
  2. 构造请求FeatureInstallRequest,明确指定加载的相关信息。通过FeatureInstallRequest类可以构造一个加载的请求。在该请求中,可以指定一个或多个feature 的名称。例如:
    FeatureInstallRequest request = FeatureInstallRequest.newBuilder() // 添加dynamic feature 的名称 .addModule("SplitSampleFeature01") .build();
  3. Task注册监听器,监控请求状态。对于上面创建的task,可以通过注册监听器Listener的方式,监测Feature的请求状态,即成功或者失败。例如:
    task.addOnListener(new OnFeatureSuccessListener<Integer>() { @Override public void onSuccess(Integer integer) { Log.d(TAG, "load feature onSuccess.session id:" + integer); } }); task.addOnListener(new OnFeatureFailureListener<Integer>() { @Override public void onFailure(Exception exception) { if (exception instanceof FeatureInstallException) { int errorCode = ((FeatureInstallException) exception).getErrorCode(); Log.d(TAG, "load feature onFailure.errorCode:" + errorCode); } else { exception.printStackTrace(); } } });
  4. FeatureInstallManager对象注册监听器,监听加载的多种状态。FeatureInstallManager注册的监听器InstallStateListener中会返回一个FeatureInstallSessionState对象。该对象会根据实际的运行情况,包含当前不同的status。因此,在实际开发中,开发者需要注册一次listener,然后在listener中根据state的值进行判断,再做出合适的动作。这样就无需注册多个listener,减少冗余的代码。具体实现如下放代码所示:
    首先,创建InstallStateListener实例。
    private InstallStateListener mStateUpdateListener = new InstallStateListener() { @Override public void onStateUpdate(InstallState state) { Log.d(TAG, "install session state " + state); if (state.status() == FeatureInstallSessionStatus.REQUIRES_USER_CONFIRMATION) { try { mFeatureInstallManager.triggerUserConfirm(state, SampleEntry.this, 1); } catch (IntentSender.SendIntentException e) { e.printStackTrace(); } return; } if (state.status() == FeatureInstallSessionStatus.REQUIRES_PERSON_AGREEMENT) { try { mFeatureInstallManager.triggerUserConfirm(state, SampleEntry.this, 1); } catch (IntentSender.SendIntentException e) { e.printStackTrace(); } return; } if (state.status() == FeatureInstallSessionStatus.INSTALLED) { Log.i(TAG, "installed success ,can use new feature"); makeToast("installed success , can test new feature "); return; } if (state.status() == FeatureInstallSessionStatus.UNKNOWN) { Log.e(TAG, "installed in unknown status"); makeToast("installed in unknown status "); return; } if (state.status() == FeatureInstallSessionStatus.DOWNLOADING) { long process = state.bytesDownloaded() * 100 / state.totalBytesToDownload(); Log.d(TAG, "downloading percentage: " + process); makeToast("downloading percentage: " + process); return; } if (state.status() == FeatureInstallSessionStatus.FAILED) { Log.e(TAG, "installed failed, errorcode : " + state.errorCode()); makeToast("installed failed, errorcode : " + state.errorCode()); return; } } };
  5. 然后,对监听器进行注册和解注册。

    @Override protected void onResume() { super.onResume(); if (mFeatureInstallManager != null) { mFeatureInstallManager.registerInstallListener(installStateListener); } } @Override protected void onPause() { super.onPause(); if (mFeatureInstallManager != null) { mFeatureInstallManager.unregisterInstallListener(installStateListener); } }
  6. 启动Dynamic Feature Module。进过如上几个步骤成功安装动态模块后,便可以从Base App启动Dynamic Feature module。例如:
    startActivity(new Intent(this,Class.forName("com.huawei.android.demofeature.TestActivity")));

此步骤我们将编译出三种格式APK,分别为:

编译Bundles

点击 Build > Build Bunldes / APK(s) > Build Bundle(s)

会在 build>outputs>bundle>debug 目录生成 app-debug.aab 文件

定制化编译Bundles

默认情况下,Android studio 会自动根据 CPU 架构、屏幕分辨率、语言这三个维度将app 分拆;如果希望自由控制分拆维度,可以在app/build.gradle 文件中android {} 增加控制开关

bundle { language { enableSplit = false } density { enableSplit = true } abi { enableSplit = true } }

若将language的enableSplit 属性设置为false,那么编译时,Android Studio 会将所有的语言资源打入base.apk中(下一节我们会关注aab文件的格式)

通过上一步编译出的aab文件,并不能在本地进行安装测试,需要通过Bundletool转换为apk 集合,BundleTool 是Google 开源的一个命令行工具,可以通过下面按钮下载. aab 文件可以上传到应用市场(应用市场后台会自动调用BundleTool工具转换)。本节主要讨论使用BundleTool 本地转换aab文件。

下载BundleTool

将 aab 文件转为一组 APK

java -jar bundletool-all-0.10.2.jar build-apks --bundle=app-debug.aab --output=aab.apks

其中 bundle 表示aab 文件路径;–output 表示生成apk文件路径
修改apks 文件后缀名为zip,然后解压splits,获取各apk 信息如下:

安装bunlde

找到主包 以及适配自己手机的分辨率包、语言包、CPU架构包,进行安装测试;本例相关包如下: base-master.apk(主包)、base-xxxhdpi.apk(分辨率相关包)、base-zh.apk(语言包) ,没有SO文件(CPU架构相关包),安装到手机上验证功能

adb install-multiple .\outputs\bundle\debug\splits\base-master.apk .\outputs\bundle\debug\splits\base-xxxhdpi.apk .\outputs\bundle\debug\splits\base-zh.apk

转换aab为完整apk

由于部分版本(Android 5.0 以下) 不支持app Bundle功能,个别场景我们需要返回全量包;BundleTool工具提供了 将aab 转换为全量包的能力,使用方法如下:

java -jar bundletool-all-0.10.2.jar build-apks --bundle= app-debug.aab --output=aab-un.apks --mode=universal

修改后缀zip,解压后apk信息如下:

安装完整apk

adb install universal.apk

对接华为应用市场,可以点击本链接进行学习

恭喜你,你已经完成了App Bundle的第一个项目,主要学习了如下内容:

本次Codelab使用的DEMO原码

源码下载

已复制代码