In last article , I showed a content provider (it appears in the manifest file applications merger) is how third-party libraries and modules automatically loaded at the time the application starts.
In this article, I will introduce how to use AndroidX's App Startup library to further control when and how those libraries should be loaded. Maybe, I mean maybe, we will also find out how to shorten the startup time of the application by the way.
uses the application startup library to automatically initialize
The easiest way to use App Startup is to use its content provider to initialize other libraries in the background. You can specify how the application startup library should initialize other libraries, or you can remove the content providers of other libraries from the merged manifest file. Avoid using multiple content providers to perform startup tasks, but use resources to load the application startup library, and then load other content.
You can implement the above operations in the following three steps. First, add the application startup library as a dependency in the build.gradle file of your project, secondly create an Initializer for each library that needs to be initialized, and finally add it to the Manifest.xml file of your project Related Information.
Let me see it again in first article the WorkManager examples of use. In order to load the WorkManager through the application startup library, I first added the application startup library to the application's build.gradle file:
// 查看最新的版本号 https://developer.android.google.cn/jetpack/androidx/releases/startup
def startup_version = "1.0.0"
implementation “androidx.startup:startup-runtime:$startup_version”
Then, based on the Initializer interface provided by the application startup library, I created an Initializer:
class MyWorkManagerInitializer : Initializer<WorkManager> {
override fun create(context: Context): WorkManager {
val configuration = Configuration.Builder().build()
WorkManager.initialize(context, configuration)
return WorkManager.getInstance(context)
}
override fun dependencies(): List<Class<out Initializer<*>>> {
// 没有其他依赖库
return emptyList()
}
}
Each Initializer has two methods that need to be replicated: create()
and dependencies()
. dependencies()
is used to specify the initialization sequence of multiple dependent libraries. I don't need this feature in this example, because I only need to process WorkManager
. If you need to use multiple libraries in your application, check application startup manual regarding the use dependencies()
details.
For the create()
method, I imitated the WorkManager's content provider .
By the way, this method is actually very common when using the application startup library. The content provider of a library is responsible for the implementation of its initialization, so you can usually refer to the code in that class to implement it manually. Some libraries may be troublesome because they use hidden or internal APIs, but fortunately WorkManager is not, so I can do this, and hope that this method is also applicable to your situation.
Finally, I added two provider tags to the <application>
Manifest.xml
The first one looks like this:
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:exported="false"
tools:node="remove" />
WorkManagerInitializer
tag is important because it indicates that Android Studio needs to delete the automatically generated provider, which is generated after adding WorkManager
build.gradle
Without this special tag, the library will still be automatically initialized when the application starts, and then an error will be reported when the application startup library tries to initialize it because it has already been initialized.
Here is the second provider tag I added:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data android:name="com.example.startuplibtest.MyWorkManagerInitializer"
android:value="androidx.startup" />
</provider>
InitializationProvider
label by adding applications and libraries to start build.gradle
file automatically generated label is basically the same (You can verify this by manifest file to view the merger - For details, see first article ), but they have two very important The difference:
tools:node="merge"
This parameter is mainly used for the manifest merge operation that Android Studio is responsible for. It tells the tool to merge multiple instances of this tag in the final merged manifest file. In this example, it will <provider>
automatically generated by the library dependency to this version of the provider, so that there will only be this tag instance in the final merged manifest file.
The other line contains this meta-data:
<meta-data android:name="com.example.startuplibtest.MyWorkManagerInitializer"
android:value="androidx.startup" />
The metadata tag in this provider tells the application startup library how to find your Initializer code, which will be executed when the application starts to initialize the library. Please pay attention to the difference caused by this: If you do not use the application startup library, the relevant initialization will be performed automatically, because Android will create and execute the content provider in that library, and then automatically initialize the library itself. However, specifying your Initializer through the application startup library and removing WorkManager
in the merge manifest file is equivalent to telling Android to switch to the content provider of the application startup library to load the WorkManager
library. If you initialize multiple libraries in this way, you can use the separate content provider of the application startup library to effectively manage these requests, instead of causing each library to create its own content provider.
lazy...if you want
When optimizing application startup performance, we cannot change the code implementation that is beyond our control. So the idea here is not to speed up the initialization of our use of libraries, but to control when and how these libraries are initialized. In particular, we can decide whether any library needs to be initialized when the application starts (either use the default mechanism of the library to add the content provider to the merged manifest file, or use the content provider of the application startup library to centrally manage the initialization request), You still need to load them later.
For example, you may need a library that contains content provider initialization in a special process of your application, but this library does not need to be loaded immediately when the application starts, or in some cases it does not need it at all Is loaded. If this is the case, why spend time initializing a large library at application startup because it is only needed in a particular code path? Why not wait until the library is really needed before introducing related initialization overhead?
This is where the application startup library is smart. It can help you remove hidden content providers from the merged manifest file and during application startup. It can also help you delay or load these libraries more purposefully.
Use the application startup library to implement delayed initialization
Now we know how to use the application startup library to automatically load and initialize the library. Next, let's take a closer look at how to implement delayed initialization if you don't want to initialize at startup.
In fact, the above code is very close build.gradle
file, and you still need a special "remove" provider tag to remove the content provider automatically generated by each library. We only need to add a little more information to the manifest file to tell it to also remove the provider of the application startup library. In this way, no content provider initialization occurs when the application starts, and it is entirely up to you to decide when the relevant initialization should be triggered.
In order to achieve this goal, I replaced the previous InitializationProvider
with the following code. The code shown above tells the system how to locate the code in the content provider that automatically initializes your library. Because the initialization will be triggered manually later, this time I will skip that part and only leave the part where the content provider is automatically generated when the application starts.
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
After I made this change, there is no longer any content provider in the merged manifest file, so the application startup library and WorkManager will not be automatically initialized when the application starts.
In order to manually initialize these libraries, I added the following code elsewhere in the application to implement it:
val initializer = AppInitializer.getInstance(context)
initializer.initializeComponent(MyWorkManagerInitializer::class.java)
AppInitializer
is the class that connects all these parts provided by the application startup library. You need to use a context
object to create a AppInitializer
object, and then you can pass it a Initializer
reference that you created to initialize various libraries. In this example, I used MyWorkManagerInitializer
and it was done.
Time is everything
I did several tests (using the test application startup performance article) to compare several different methods of starting the application and initializing the library. I counted without any libraries, with WorkManager
(using the content provider automatically generated by default), using the application startup library to automatically initialize WorkManager
at startup, and using AppInitializer
delay initialization of WorkManager and application startup libraries.
It should be noted that, as we discussed in the article , all of these time calculations are based on the locked CPU frequency, so these durations are much longer than on a machine that does not lock the CPU frequency. They are only meaningful when compared with each other, and cannot represent the real situation. Here is what I found:
- Without WorkManager: 1244 ms
- With WorkManager and loaded via content provider: 1311 ms
- With WorkManager and loaded via App Startup: 1315 ms
- With WorkManager (lazy loading): 1268 ms
Finally, I counted the time AppInitalizer
manually initializing WorkManager
- Using
AppInitializer
initializationWorkManager
: MS 51 is
This data brings us some enlightenment. First of all, loading WorkManager
when the application starts will increase the startup time of my application by 67 milliseconds (1311–1244) on average. It should be noted that the conventional way of loading this library (using content provider) takes about the same time as the application startup library (1315 – 1244 = 71 ms). This is because the application startup library does not save us time in the case of a single library, we just transfer the logic to another code path to run. If you use the application startup library to load multiple libraries, we will get the corresponding optimization effect, but for the single library example here, using this method will not have any time-saving advantage.
At the same time delay initialization WorkManager
allows me to "save" about 51 milliseconds of time.
Is this difference obvious enough that you need to worry about it? The answer is always "it depends".
51 milliseconds accounted for less than 4% of the total time of 1.3 seconds, and for a real application, it is usually more complicated than my simple application, and this time-consuming percentage of the total startup time will be lower. In this case, this length of time may not be worth worrying about. But sometimes you may find that some libraries take too long to initialize. More likely, you may use several libraries with a content provider, and each of them will increase the startup time of your application a bit. If you can postpone most or all of the above work to a more appropriate point in time and remove it from the startup process, you may find that the startup speed of the application will be significantly improved.
Like all performance optimization projects, the most important thing you can do is to analyze the details, measure and decide:
- Check the merged manifest file of your project.
<provider>
tags can you see? - Can you use the application startup library to remove some or all of these content providers from the merged manifest file and observe how it affects the startup time? Can you achieve this without affecting runtime behavior? (It is worth noting: You need to make sure to initialize them before the application starts to rely on the functions of the related libraries.)
Finally, enjoy performance testing and optimization. I will continue to find more ways to analyze and optimize the performance of the application. If I find something valuable, I will post relevant content.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。