Android 使用Google Core动态功能交付(Feature Delivery)

介绍

动态功能交付(Feature Delivery),是谷歌Google Play Core 库为谷歌商店发布的应用所提供的功能之一。

使用Play Core 库还可以提供以下这些功能:

  • 下载其他语言资源
  • 管理功能模块分发
  • 管理资源包分发
  • 触发应用内更新
  • 请求应用内评价

本文的重点是说明功能模块分发与资源包分发的使用。

Android App Bundle

Android App Bundle 是一种发布格式(.aab),这种格式和传统的apk类似,都会包含应用的所有经过编译的代码和资源。

不同之处在于将这种格式不能直接安装,而开发者需要将包上传至Google Play之后,Google Play会使用这个 App Bundle 包针对每种设备配置生成并提供经过优化的 APK,即 APK 的生成及签名交由了 Google Play 来完成。

如果要使用动态功能交付或资产交付,则必须要使用App Bundle方式打包。

如上图所示,传统的apk打包是将这些组成部分打包成一个压缩包,即apk包。而app bundle打包的aab文件也是包含以上所有内容,但是在将这个aab格式的包上传至Google Play后,用户进入商店选择下载应用时,下载的包将不是包含所有内容的包,而是只包含该用户手机所对应的相应类型资源的包。

如该用户的手机是使用英语、xhdpi、arm-v7架构cpu的设备,那么该用户下载到的包将只包含(如果你启用了这些类型的拆分)这些类型对应的语言、资源图、lib库文件。显而易见,这种方式将有助于缩小用户下载的包大小与减小应用所占用的空间。

而将现有的项目修改为app bundle打包的方法也很简单:

  1. 在项目gradle中引入google core库

    // In your app’s build.gradle file:
    ...
    dependencies {
        // This dependency is downloaded from the Google’s Maven repository.
        // So, make sure you also include that repository in your project's build.gradle file.
        implementation 'com.google.android.play:core:1.9.1'
    
        // For Kotlin users also import the Kotlin extensions library for Play Core:
        implementation 'com.google.android.play:core-ktx:1.8.1'
        ...
    }
    
  2. 在项目的app模块的build.gradle文件中添加以下内容:

android { // 在gradle的android结点下
    
    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 true
        }
    }
}
  1. 在打包时选择app bundle打包即可

这样打出来的包就是aab格式的包,发版时将这个aab文件传到谷歌后台即可。如果需要测试这个aab包的功能,则需要使用谷歌的bundletool工具来安装aab包到测试机上。

资产交付(Asset Delivery)

通常,对于用户不会立刻使用到的一些比较占用空间的assets资源,如大量图片资源、机器学习模型参数包、较大的视频资源等,可以使用动态资产交付。

具体的使用方式为,将这些资源单独拆分在一个Module的assets文件夹下,并将该Module设置为动态分发的模块。

动态资产交付主要有三种交付模式,分别为:

  • install-time:安装时分发,应用安装时就会下载,安装后可立即使用,资产包的大小会计入应用详情页的下载大小
  • fast-follow:快速跟进式分发,应用安装后会立即开始下载,安装后不一定保证可以使用,资产包大小暂时还不会计入详情页大小
  • on-demand:按需分发,资产包不会主动下载,需要代码在特定时机向用户征求开始下载,确保下载后才可使用,资产包大小不会计入详情页大小

这三种交付模式可以在资源Module的AndroidManifest.xml中设置。

代码中,对于动态分发的资源的访问,install-time与另外两种模式不同,对于install-time的模块,与常规资源一样使用AssertManager访问,官方代码示例为:

import android.content.res.AssetManager;
...
Context context = createPackageContext("com.example.app", 0);
AssetManager assetManager = context.getAssets();
InputStream is = assetManager.open("asset-name");

而对于另外两种模式,要访问动态分发模块的资源,则要复杂许多。

首先需要AssetPackManager#getPackLocation()方法获取 Asset Pack 的根文件夹。如果返回值存在,则说明动态模块可用,如果为null,则需要代码申请下载并安装该模块才可用。流程如下图,由于不是本文重点,故详细代码不列出来了。具体可以查看官方文档

动态功能交付(Feature Delivery)

介绍

动态功能交付与资产交付类似,同样是需要单独拆分出一个模块,而且这个模块与常规的功能模块存在很大的不同。

我们通常添加一个模块,是希望这个模块中的代码能被多个项目复用,所以app模块与这个feature模块的引用关系为:app引用feature模块。

这种引用关系决定了app模块中可以随意使用feature模块中的代码和资源,而feature模块无法访问app中的资源和代码。

在动态功能交付中,这种引用关系需要反过来,feature模块引用app模块。所以feature中可以随意访问app中的代码和资源,而反过来则不行。

接下来,我们演示一下将一部分代码和资源从原有的项目中拆分出来。

在我负责的项目中,由于opencv的so文件占用的空间太大,产品不满意,所以决定将openCV对应的so文件和相应的activity页面与功能代码拆分出来配置成动态功能交付。

使用方式

首先,先新建一个Module,可以直接在AS中选择创建动态功能交付的Module,然后根据提示填好Module的Name和Title。

创建完成之后,确认以下配置:

app的gradle文件中,以动态模块的形式引入了刚刚创建的模块

android { // android结点下
    
    dynamicFeatures = [':opencvfeature']
}

创建模块的gradle文件中,引入了app模块

dependencies {
    implementation project(":app")
}

新创建的模块AndroidManifest.xml文件中,已经添加了以下内容:

    <dist:module dist:title="@string/module_name">
        <dist:delivery>
            <dist:on-demand />
        </dist:delivery>
        <dist:fusing dist:include="true" />
    </dist:module>

确认以上内容后,一个动态功能交付的模块就创建完成了。接下来要做的工作就是将原app模块中需要拆分的代码迁移到新创建的这个模块中,并消除app模块对这部分代码的引用(即能成功编译)。这部分工作和每个人的项目相关,有些甚至需要重构功能代码,所以就不展开讲了。

在这个过程中可能会发现一个问题,那就是app模块无法引用动态模块中的代码和资源后,那动态模块我要如何使用呢?无法引用其中的类,也就无法调用其中的功能方法,那动态分发的意义在哪里?

这个问题的答案也很简答:使用反射。

对于调用动态模块中的代码,使用反射的方式去调用对应类中的方法。对于跳转动态模块中的activity,使用Intent指定类名和包名即可,示例如下。

Intent intent = new Intent();
intent.setClassName(getPackageName(), "com.xxx.xxx.XXXActivity");
startActivityForResult(intent, requestCode);

修改代码完成后,工程目录大致如下,其中opencvfeature就是我拆分出的动态功能模块。

完成了以上工作,还有一个重要的工作没有完成,即使用该模块的功能前,判断模块是否安装以及申请下载安装该模块,毕竟我们的功能模块设置的是on-demand(按需交付)模式。

private SplitInstallManager splitInstallManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
	// ...
    splitInstallManager = SplitInstallManagerFactory.create(this); // 创建manager
}

@Override
public void onResume() {
	// ...
    splitInstallManager.registerListener(splitInstallStateUpdatedListener);
}

 @Override
public void onPause() {
    // ...
    splitInstallManager.unregisterListener(splitInstallStateUpdatedListener);
}

// 使用动态模块功能
public void tryUseDynamicFeature() {
    String moduleName = FeatureManager.getInstance().getModuleName();
    if (splitInstallManager.getInstalledModules().contains(moduleName)) {
        // 已下载对应的模块
        // TODO 使用反射代码
        
    } else { // 没有下载模块,需要申请下载
        SplitInstallRequest request = SplitInstallRequest.newBuilder().addModule(moduleName).build();
        splitInstallManager.startInstall(request);

        boolean isGoogleCanUse = PhoneUtil.isGooglePlayServiceAvailable(this);
        if (isGoogleCanUse) {
            T.show("Starting install for " + moduleName);
        } else {
            T.show("The Google service appears to be unavailable and the installation cannot begin");
        }
    }
}

// 动态模块的状态监听
private final SplitInstallStateUpdatedListener splitInstallStateUpdatedListener = state -> {
        switch (state.status()) {
            case SplitInstallSessionStatus.DOWNLOADING: {
                // 正在下载
            }
            break;
            case SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION: {
                // 需要用户确认
            }
            break;
            case SplitInstallSessionStatus.INSTALLED: {
                // 安装模块成功
            }
            break;
            case SplitInstallSessionStatus.INSTALLING: {
                // 正在安装
            }
            break;
            case SplitInstallSessionStatus.FAILED: {
                // 下载模块失败
            }
            break;
            default:
                break;
        }
    };
动态交付的优势
  • 减少必要apk的体积,使得在流量分发中更具优势
  • 使用谷歌官方服务器分发,稳定性更有保障,不需要消耗宝贵的服务器资源
  • 下载过程由google自身处理,支持断点续传等
注意事项
  • 动态功能交付和资产交付不同,只有两种模式,install-time和on-demand,没有提供fast-follow
  • 测试动态功能交付需要使用Google Play提供的测试服功能,将包当作测试包传上商店,将测试的账号加入测试账号列表,即可测试
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值