Android App Bundle 使用示例

1,059 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Step 1 新建工程

在 Android Studio 中新建一个通用的 Android 工程

image (1).png

Step 2 修改 app’s build.gradle文件

添加android.bundle代码块,可以启用对屏幕分辨率、cpu 架构配置 APK 的支持

// 'app' module should have the application plugin.
apply plugin: 'com.android.application'

android {
    defaultConfig {
        ...
        // Specify the version code, only once for each new version of your app, for your App Bundle
        // No need to have different version codes for different APKs generated.
        // All split APKs will share the same version code once installed via Google Play.
        versionCode 1
        versionName "1.0"
        ...
    }

    // Add this block and enable/disable the parameters as follows
    bundle {
        density {
            // Different APKs are generated for devices with different screen densities; true by default.
            enableSplit true
        }
        abi {
            // Different APKs are generated for devices with different CPU architectures; true by default.
            enableSplit true
        }
        language {
            // This is disabled so that the App Bundle does NOT split the APK for each language.
            // We're gonna use the same APK for all languages.
            enableSplit false
        }
    }
}

Step 3 新建 Feature Module

打开 File > New > New Module 窗口,选中 “Dynamic Feature” Module,如下图所示

feature1.png
  • 选中 app module 作为Base application module
  • Module name 命名为“feature1”;
  • 配置 feature1 module 的 Packge nameMinimum API Level
  • 点击 Next

In the Configure On-Demand Options section:

image (2).png
  • 设置 Module title 为 “Feature 1”;
  • 选择 Do not include module at install-time (on - demande only), 安装应用时不包含 featue module,后期用户手动请求时再安装 featue module;
  • 勾选 Fusing(Android L 之前版本不支持 On-demand 安装,所以需要初次安装时就包含 feature module);
  • 点击 Finish。 修改 feature1’s manifest.xml文件, 并且为 feature1 添加一个空 Activity
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:dist="http://schemas.android.com/apk/distribution"
    package="com.elijah.feature1" >

    <dist:module
        dist:instant="false"
        dist:title="@string/title_feature1" >
        <dist:delivery>
            <dist:on-demand />
        </dist:delivery>

        <dist:fusing dist:include="true" />
    </dist:module>

    <application>
        <activity
            android:name=".FeatureOneActivity"
            android:exported="false" >
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.elijah.appbundledemo.MainActivity" />
        </activity>
    </application>

</manifest>

确认 feature1 的 build.gradle文件有如下插件配置

plugins {
    id 'com.android.dynamic-feature'
}

相同的步骤创建feature2

Step 4 检查 app’s build.gradle文件

检查 android 模块下是否有 dynamicFeatures配置

plugins {
    id 'com.android.application'
}

android {
    ...
    // This specifies the dynamic features.
    dynamicFeatures = [':feature1', ':feature2']
}

Step 5 检查 feature1 和 feature2 的 build.gradle文件

检查是否依赖 app module

 plugins {
    id 'com.android.dynamic-feature'
}

android {
  ...
}

dependencies {
    ...
    // Add the app module as a dependency in the dynamic feature modules.
    implementation project(':app')
    ...
}

Step 6 编辑 app module

app’s build.gradle 添加 google play core 依赖

implementation 'com.google.android.play:core:1.10.3'

MainAcitivy 编辑

public class MainActivity extends AppCompatActivity {

    private SplitInstallManager splitInstallManager = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Instantiate an instance of SplitInstallManager for the dynamic feature module
        splitInstallManager =
                SplitInstallManagerFactory.create(getApplicationContext());

        Button buttonFeatureOne = findViewById(R.id.btn_feature_one);
        Button buttonFeatureTwo = findViewById(R.id.btn_feature_two);

        buttonFeatureOne.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loadFeatureOne();
            }
        });
        buttonFeatureTwo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loadFeatureTwo();
            }
        });
    }

    public void loadFeatureOne() {
        // Builds a request to install the feature1 module
        SplitInstallRequest request =
                SplitInstallRequest
                        .newBuilder()
                        // You can download multiple on demand modules per
                        // request by invoking the following method for each
                        // module you want to install.
                        .addModule("feature1")
                        .build();

        // Begin the installation of the feature1 module and handle success/failure
        splitInstallManager
                .startInstall(request)
                .addOnSuccessListener(new OnSuccessListener<Integer>() {
                    @Override
                    public void onSuccess(Integer integer) {
                        // Module download successful
                        Intent intent = new Intent().setClassName(getPackageName(), "com.bapspatil.feature1.FeatureOneActivity");
                        startActivity(intent);
                    }
                })
                .addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(Exception e) {
                        // Module download failed; handle the error here
                        Toast.makeText(getApplicationContext(), "Couldn't download feature1: " + e.getMessage(), Toast.LENGTH_LONG).show();
                    }
                });
    }

    public void loadFeatureTwo() {
        // Builds a request to install the feature1 module
        SplitInstallRequest request =
                SplitInstallRequest
                        .newBuilder()
                        // You can download multiple on demand modules per
                        // request by invoking the following method for each
                        // module you want to install.
                        .addModule("feature2")
                        .build();

        // Begin the installation of the feature1 module and handle success/failure
        splitInstallManager
                .startInstall(request)
                .addOnSuccessListener(new OnSuccessListener<Integer>() {
                    @Override
                    public void onSuccess(Integer integer) {
                        // Module download successful
                        Intent intent = new Intent().setClassName(getPackageName(), "com.bapspatil.feature2.FeatureTwoActivity");
                        startActivity(intent);
                    }
                })
                .addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(Exception e) {
                        // Module download failed; handle the error here
                        Toast.makeText(getApplicationContext(), "Couldn't download feature2: " + e.getMessage(), Toast.LENGTH_LONG).show();
                    }
                });
    }
}

Step 7 生成 App Bundle 文件

在 Android Studio 中点击 Build > Build Bundle(s)/APK(s) > Build Bundle(s)

或者输入命令行:

./gradlew :base:bundleDebug

即可在 project-name/app/build/outputs/bundle/目录下生成 bundle 文件

image (3).png

Step 8 部署 App Bundle 文件

Google Play

海外发布的应用可支持 Google Play 部署

  • 点击 Build > Generate Signed Bundle/APK, 生成签名 Bundle 包

  • 上传签名文件和 Bundle 包到 Google Play Console

  • 通过 Google Play 下载应用 BundleTool

  • 下载 bundletool,地址: bundletool release

  • 通过应用包生成 apk 集

java -jar bundletool-all-1.11.0.jar build-apks 
    --bundle=<your_app_bundle_path>
    --output=<output_apks_path>
  • 为已连接的设备生成 apk(根据已连接设备的分辨率、abi 和语言)
java -jar bundletool-all-1.11.0.jar build-apks
    --connected-device 
    --bundle=<your_app_bundle_path>
    --output=<output_apks_path>
  • 将 apk 部署到设备上(需要连接设备,根据设备信息自动提取对应资源并安装)
java -jar bundletool-all-1.11.0.jar install-apks 
    --apks=<output_apks_path>

因其生成的 apk 集的后缀为 apks,只能通过 bundletool 进行 apk 安装,无法通过 adb install 进行安装。

生成的full APKs image (4).png

连接特定机型配置生成的 APKs image (5).png

大小对比

full APKsFind X5 APKs减少体积
APKs 大小4100KB3728KB9%