动态库中的全局变量
This is the first article in a three part article series on how you can leverage GloballyDynamic to accomplish tasks such as:
这是由三部分组成的系列文章中的第一篇,该系列文章介绍如何利用GloballyDynamic完成以下任务:
- Enabling dynamic delivery for environments where it would be otherwise unavailable (e.g. Firebase App Distribution). 在原本无法使用的环境中启用动态交付(例如,Firebase App Distribution)。
- Testing various outlying dynamic delivery scenarios during development. 在开发过程中测试各种外部动态交付方案。
Making life easier when working with multiple dynamic delivery platforms (e.g. Play Feature Delivery or Dynamic Ability) for the same project.
在同一项目中使用多个动态交付平台(例如Play Feature Delivery或Dynamic Ability )时,使工作变得更轻松。
系列概述: (Series outline:)
Part 1: GloballyDynamic: Dynamic delivery during development (this article)
第1部分 :GloballyDynamic:开发过程中的动态交付( 本文 )
Part 2: GloballyDynamic: Dynamic delivery with Firebase App Distribution
Part 3: GloballyDynamic: Multi-platform dynamic delivery with a unified client API
Dynamic Delivery is Google’s app serving model, it leverages App Bundles and bundletool to dissect monolithic APKs into multiple smaller ones that together produce a wholesome experience.
Dynamic Delivery是Google的应用程序服务模型,它利用App Bundles和bundletool将整体式APK分解为多个较小的APK,共同产生有益的体验。
It brings with it benefits such as saving device storage space, lowering network consumption and the ability to package discrete pieces of functionality from your app to be delivered on-demand through dynamic feature modules.
它带来的好处包括节省设备存储空间,降低网络消耗以及打包应用程序中离散功能以通过动态功能模块按需交付的功能。
While the model is great, adopting it can introduce some inconveniences:
虽然该模型很棒,但是采用该模型可能会带来一些不便:
App bundles are not supported by all app stores / distribution platforms (e.g. Amazon App Store, Samsung Galaxy Store, Firebase App Distribution): if you distribute through any of these you are not able to reap the benefits of dynamic delivery, and you ultimately have to deliver an app with behaviour different to the one distributed on Google Play Store (provided you use dynamic feature modules)
所有应用程序商店/发行平台(例如,Amazon App Store,Samsung Galaxy Store,Firebase应用程序发行)均不支持应用程序捆绑:如果通过其中任何一种进行分发,您将无法获得动态交付的好处,最终您将获得提供的行为与Google Play商店中发布的行为不同的应用程序(前提是您使用动态功能模块)
Different integrations for different app stores: the app stores that do support it natively all provide different client API:s (e.g. Google Play Store through Play Feature Delivery and Huawei App Gallery through Dynamic Ability), you therefore have to provide a separate client side integration for each of the platforms you distribute through.
不同应用商店的集成方式不同 :本地支持该应用的应用商店都提供不同的客户端API :(例如,通过Play Feature Delivery提供的 Google Play商店和通过Dynamic Ability提供的Huawei App Gallery),因此您必须提供单独的客户端集成针对您通过其分发的每个平台。
Unavailable for devices without a dynamic delivery compatible app store installed: emulators w/o Play Store and other devices without a compatible app store (e.g. custom devices or Amazon Fire devices) are also excluded from using dynamic delivery.
对于未安装动态交付兼容应用程序商店的设备不可用:没有 Play Store的模拟器和其他没有兼容应用程序商店的设备(例如,自定义设备或Amazon Fire设备)也无法使用动态交付。
Enter GloballyDynamic: a set of tools geared towards solving these problems.
输入GloballyDynamic:一组旨在解决这些问题的工具。
The core features provided by GloballyDynamic are the following:
GloballyDynamic提供的核心功能如下:
- Infrastructure necessary to make dynamic delivery universally available, regardless of underlying distribution platform. 无论基础分发平台如何,都可以普遍获得动态交付所需的基础结构。
- Unified Android client API: write once, run with any underlying dynamic delivery platform. 统一的Android客户端API:只需编写一次,即可在任何基础动态交付平台上运行。
- Tools to provide a streamlined developer experience for dynamic delivery. 为动态交付提供简化的开发人员体验的工具。
The remainder of this article will shed light on how to get up and running with a development setup that enables dynamic delivery during development.
本文的其余部分将阐明如何使用开发设置来启动和运行该开发设置,以便在开发过程中实现动态交付。
The developer tools provided by GloballyDynamic allow for testing various scenarios you can encounter while making split install requests, some of these include:
GloballyDynamic提供的开发人员工具可用于测试您提出分割安装请求时可能遇到的各种情况,其中包括:
- Slow download speeds 下载速度慢
- Broken connection to the server from which to make split install requests 断开与服务器的连接,从中发出分割安装请求
- Cancellation of split install requests 取消拆分安装请求
- Multiple split install sessions 多个拆分安装会话
What makes this possible is interplay between the following components:
以下组件之间的相互作用使之成为可能:
GloballyDynamic Server: an http server that receives and stores app bundles from which split APK:s are subsequently generated and delivered to clients.
GloballyDynamic Server :这是一个http服务器,用于接收和存储应用程序捆绑包,然后从中生成拆分的APK:并分发给客户端。
GloballyDynamic Studio Plugin: an Android Studio plugin that embeds a GloballyDynamic server.
GloballyDynamic Studio插件 :嵌入了GloballyDynamic服务器的Android Studio插件。
GloballyDynamic Gradle Plugin: a Gradle plugin that intercepts app bundles produced by the Android Gradle Plugin (e.g. through
./gradlew bundle
) and uploads them to a GloballyDynamic Server.GloballyDynamic Gradle插件 :一个Gradle插件,可拦截由Android Gradle插件生成的应用程序包(例如,通过
./gradlew bundle
)并将其上传到GloballyDynamic Server。GloballyDynamic Android Library: an Android library that downloads and installs split APK:s from a GloballyDynamic Server.
GloballyDynamic Android库 :一个Android库,可从GloballyDynamic服务器下载并安装拆分的APK:s。
The way in which these components cooperate is illustrated below:
这些组件的协作方式如下所示:
Follow the steps provided below to configure your project for a development setup.
请按照下面提供的步骤为开发设置配置项目。
1)安装Android Studio插件 (1) Install the Android Studio plugin)
The plugin embeds a GloballyDynamic server which will be used to receive and store app bundles, these bundles will subsequently be used in order to generate and serve split APKs (such as dynamic feature APKs) to clients making split install requests.
该插件嵌入了一个GloballyDynamic服务器,该服务器将用于接收和存储应用程序捆绑包,随后将使用这些捆绑包来生成拆分APK(例如动态功能APK)并将其提供给提出拆分安装请求的客户端。
Find the plugin here. Or search for “GloballyDynamic” in the plugin browser in Android Studio:
在此处找到插件。 或在Android Studio的插件浏览器中搜索“ GloballyDynamic”:
2)配置您的应用程序以使用Android Studio嵌入式服务器 (2) Configure your app to use the Android Studio embedded server)
We now have to make sure that app bundles produced by AGP gets intercepted and uploaded to the Android Studio embedded server, as well as instructing our app to route split install requests to the server.
现在,我们必须确保AGP生成的应用程序捆绑包被拦截并上传到Android Studio嵌入式服务器,并指示我们的应用程序将拆分安装请求路由到服务器。
The way in which this is done is through the Gradle plugin, it exposes a globallyDynamicServers {}
DSL for this purpose — basically you configure a collection of GloballyDynamic servers in the form of a NamedDomainObjectContainer<GloballyDynamicServer>
, the GloballyDynamicServer
object exposes the following configurable properties:
完成此操作的方法是通过Gradle插件 ,为此目的,它公开了globallyDynamicServers {}
DSL —基本上,您以NamedDomainObjectContainer<GloballyDynamicServer>
的形式配置了GloballyDynamic服务器的集合, GloballyDynamicServer
对象公开了以下可配置的属性:
serverUrl
: the url to the serverserverUrl
:服务器的URLusername
: the username to the serverusername
:服务器的用户名password
: the password to the serverpassword
:服务器的密码throttleDownloadBy
: the amount of time (in ms) to throttle split APK downloads by, defaults to 0throttleDownloadBy
:节流拆分APK下载所用的时间(以毫秒为单位),默认为0applyToBuildVariants
: what build variants the server should be used forapplyToBuildVariants
:服务器应用于哪些构建变体uploadAutomatically
: whether or not to upload bundles produced by AGP automatically, defaults to trueuploadAutomatically
:是否自动上传AGP产生的捆绑包,默认为true
The username/password credentials are used for authorising bundle uploads to the server.
用户名/密码凭证用于授权将捆绑包上传到服务器。
To use the Android Studio embedded server we apply the following configuration:
要使用Android Studio嵌入式服务器,我们应用以下配置:
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'com.jeppeman.globallydynamic.gradle:plugin:1.0.0'
}
}
apply plugin: 'com.android.application'
apply plugin: 'com.jeppeman.globallydynamic'
android {
// DSL from the gradle plugin in which you configure your GloballyDynamic servers
globallyDynamicServers {
// Note that we are not specifying a server url here,
// when this is the case the Android Studio embedded
// server will be used
studioEmbedded {
// Optional: throttle split APK downloads by
// the amount of milliseconds given
throttleDownloadBy 5000
// The build variants that this server should be applied to.
// In this case we apply it to the debug variant
applyToBuildVariants 'debug'
}
}
dynamicFeatures = [':my_dynamic_feature']
}
dependencies {
// With this artifact split install requests will be delegated to a self hosted
// GloballyDynamic server, in this case an Android Studio embedded server
debugImplementation 'com.jeppeman.globallydynamic.android:selfhosted:1.0.0'
}
For kotlin script (build.gradle.kts
) the DSL usage is a slight bit different:
对于kotlin脚本( build.gradle.kts
),DSL的使用略有不同:
import com.jeppeman.globallydynamic.gradle.extensions.globallyDynamicServers
android {
globallyDynamicServers {
create("studioEmbedded") {
throttleDownloadBy = 5000
applyToBuildVariants('debug')
}
}
}
Some of the properties get packaged as metadata with the application that is being built for, such as
serverUrl
andthrottleDownloadBy
—this metadata is then used by the Android library in order to communicate with the server.某些属性与正在为其构建的应用程序一起打包为元数据,例如
serverUrl
和throttleDownloadBy
—然后,Android库会使用此元数据来与服务器进行通信。
With that, all infrastructure is in place and we can proceed with integrating the Android library.
这样,所有基础结构都已就绪,我们可以继续集成Android库。
3)集成Android库 (3) Integrate the Android library)
The library provides the following core features:
该库提供以下核心功能:
- A dynamic delivery client implementation that downloads and installs split APKs from GloballyDynamic servers (in this case the Android Studio embedded one) 一种动态交付客户端实施,可从GloballyDynamic服务器(在本例中为Android Studio嵌入式服务器)下载并安装拆分的APK。
A unified API in the form of a wrapper around different dynamic delivery client APIs, such as Play Core, Dynamic Ability and the internal implementation provided by the library.
围绕不同动态交付客户端API的包装形式的统一API,例如Play Core , Dynamic Ability和库提供的内部实现。
It comes in different flavours, one for each underlying dynamic delivery platform, all of which expose an identical API while delegating operations to their respective underlying platforms.
它具有不同的风格,每个基本动态交付平台都需要一种,所有这些都公开了相同的API,同时将操作委派给了各自的基本平台。
In part 3 of this series we’ll go through how to configure a project for a multi-dynamic-delivery-platform setup (w/ Google Play, Huawei App Gallery etc); for now though, we are focusing on a development setup and thereby only need to use the com.jeppeman.globallydynamic.android:selfhosted
flavour.
在本系列的第3部分中 ,我们将介绍如何为多动态交付平台设置(带有Google Play,Huawei App Gallery等)配置项目。 目前,我们专注于开发设置,因此仅需要使用com.jeppeman.globallydynamic.android:selfhosted
风味。
The API itself mirrors that of Play Core, it thereby exposes versions of SplitCompat
and SplitInstallManager
, called GlobalSplitCompat
and GlobalSplitInstallManager
respectively — these are the cornerstones of the API.
该API本身是Play Core的镜像,因此它公开了SplitCompat
和SplitInstallManager
版本,分别称为GlobalSplitCompat
和GlobalSplitInstallManager
这些是API的基石。
启用GlobalSplitCompat (Enable GlobalSplitCompat)
Just as with Play Core, we need to enable access to dynamically downloaded code and resources; this is achieved by enabling GlobalSplitCompat
, it can be enabled either by using GlobalSplitCompatApplication
, or manually calling GlobalSplitCompat.install(context)
, as illustrated below:
与Play Core一样,我们需要启用对动态下载的代码和资源的访问; 这可以通过启用GlobalSplitCompat
来实现,可以通过使用GlobalSplitCompatApplication
或手动调用GlobalSplitCompat.install(context)
来启用,如下所示:
class MyApplication : Application() {
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
GlobalSplitCompat.install(this)
}
}
You should also enable it for dynamically downloaded activities as follows:
您还应该为动态下载的活动启用它,如下所示:
class DynamicActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(newBase)
GlobalSplitCompat.installActivity(this)
}
}
下载并安装动态功能 (Download and install dynamic features)
The flow of installing features is also almost identical to that of Play Core, we interact with the GlobalSplitInstallManager
class, an instance of which is created as follows:
安装功能的流程也几乎与Play Core相同,我们与GlobalSplitInstallManager
类进行交互,该类的实例创建如下:
val manager = GlobalSplitInstallManagerFactory.create(context)
It exposes the usual suspects, such as startInstall(...)
, for installing, registerListener(...)
for monitoring install requests, cancelInstall(...)
for canceling requests, etc.
它暴露了通常的嫌疑人,如startInstall(...)
安装, registerListener(...)
用于监视安装请求, cancelInstall(...)
取消请求,等等。
Performing an install request and monitoring the progress of it is illustrated in the gist below:
下面的要点说明了执行安装请求并监视其进度:
val globalSplitInstallManager = GlobalSplitInstallManagerFactory.create(context)
val listener = GlobalSplitInstallUpdatedListener { state ->
when (state.status()) {
GlobalSplitInstallSessionStatus.DOWNLOADING -> updateProgressBar()
...
}
}
val request = GlobalSplitInstallRequest.newBuilder()
.addModule("my_dynamic_feature")
.addLanguage(Locale.ENGLISH)
.build()
globalSplitInstallManager.registerListener(listener)
globalSplitInstallManager.startInstall(request)
.addOnSuccessListener { sessionId -> ... }
.addOnFailureListener { exception -> ... }
The addOnSuccessListener
also behaves in the same fire-and-forget manner as Play Core, i.e. the callback is not invoked when a feature has been downloaded and fully installed; but rather when a request has been accepted and scheduled to start.
addOnSuccessListener
行为也与Play Core的addOnSuccessListener
,即在下载并完全安装功能后不调用回调。 而是当请求已被接受并计划开始时。
For more details and documentation on the Android library, refer to here.
有关Android库的更多详细信息和文档,请参阅此处 。
4)构建并运行应用程序 (4) Build and run the application)
All we need to do now is build a bundle and install the base APK from it, this can be done either through ./gradlew installDebug
, or running the app from Android Studio with the “APK from app bundle” option selected (Run > Edit configurations
) and unchecking modules you want to test on-demand delivery for.
现在我们需要做的就是构建一个捆绑包并从中安装基本APK,这可以通过./gradlew installDebug
来完成,也可以通过./gradlew installDebug
“ APK from app bundle”( Run > Edit configurations
从Android Studio运行该应用来完成Run > Edit configurations
),然后取消选中要测试其按需交付的模块。
Once the application has been installed, you should see something like the following in the “GloballyDynamic Log” tool window in Android Studio:
安装该应用程序后,您应该在Android Studio的“ GloballyDynamic Log”工具窗口中看到类似以下内容:
All done, split APKs can now be downloaded and installed from the app.
现在,可以从该应用下载并安装所有已完成的拆分APK。
The following video showcases this development setup in action:
以下视频展示了实际的开发设置:
The project used in the video can be found here.
视频中使用的项目可以在这里找到。
附录:测试不同的安装方案 (Addendum: Testing different install scenarios)
As mentioned in the beginning of the article, we can test different outlying install scenarios during development, such as slow download speeds, broken server connection, canceling installs etc. Details on how go about it are follows below.
如本文开头所述,我们可以在开发过程中测试不同的外围安装方案,例如下载速度慢,服务器连接断开,取消安装等。有关操作的详细信息如下。
下载速度慢 (Slow download speeds)
As illustrated in the above example, to throttle download speeds we configure the throttleDownloadBy
property from the Gradle plugin — the Android library sends this information as a parameter when making split install requests, the server then distributes the delivery of split APKs equally in an interval over the time provided.
如上面的示例所示,为了限制下载速度,我们从Gradle插件中配置了throttleDownloadBy
属性-Android库在发出拆分安装请求时将此信息作为参数发送,然后服务器在一段时间内平均分配拆分APK的分发量提供的时间。
服务器连接断开 (Broken server connection)
It can sometimes happen that a client loses connection to the server while downloading split APKs during an install request — this scenario can be tested easily with the help of the Android Studio plugin. The plugin adds a “GloballyDynamic” menu item where the server can be toggled on/off, as illustrated in the image below:
有时可能会发生客户端在安装请求期间下载拆分的APK时失去与服务器的连接的情况-可以借助Android Studio插件轻松测试此情况。 该插件添加了一个“ GloballyDynamic”菜单项,可以在其中打开/关闭服务器,如下图所示:
Simply toggle the server off while a download is in progress, and the installation process should emit a state with a status of GlobalSplitInstallSessionStatus.FAILED
and an error code of GlobalSplitInstallErrorCode.NETWORK_ERROR
.
只需在下载进行中关闭服务器,安装过程就会发出状态为GlobalSplitInstallSessionStatus.FAILED
和错误代码为GlobalSplitInstallErrorCode.NETWORK_ERROR
。
取消安装请求 (Cancel install requests)
With the throttling of download speeds, we can give the user enough time to react to the system notification that is displayed during split install requests, through this notification requests can be canceled, as illustrated in the video below:
通过限制下载速度,我们可以给用户足够的时间来响应在拆分安装请求期间显示的系统通知,可以取消此通知请求,如以下视频所示:
When pressing Cancel, a state with a status of GlobalSplitInstallSessionStatus.CANCELED
will be emitted from the ongoing installation.
当按“取消”时,将从正在进行的安装中发出状态为GlobalSplitInstallSessionStatus.CANCELED
的状态。
多个安装请求 (Multiple install requests)
Throttling also allows for having multiple install requests active simultaneously, to test this, simply make multiple calls to GlobalSplitInstallManager#startInstall(GlobalSplitInstallRequest)
.
节流还允许同时激活多个安装请求以进行测试,只需对GlobalSplitInstallManager#startInstall(GlobalSplitInstallRequest)
进行多次调用即可。
In the next part of the series we’ll discover how we can leverage GloballyDynamic to enable dynamic delivery for Firebase App Distribution through a similar process.
在本系列的下一部分中,我们将发现如何利用GloballyDynamic通过类似的过程为Firebase App Distribution启用动态交付。
有用的链接 (Useful links)
翻译自: https://proandroiddev.com/globallydynamic-dynamic-delivery-during-development-f28093ed184f
动态库中的全局变量