I recently started using the App Startup library on AndroidX. With the library in version 1.0, I felt it was time to take a closer look at why, when, and how to use it.
The first thing I noticed was its name, App Launch, which suggests that the library might be more versatile than it sounds. This library doesn’t involve normal startup (at least not yet). It is intended to reduce the impact of content Provider initialization on application startup speed.
Like me, you’ve probably never thought about how third-party libraries are initialized. Perhaps it’s because all this processing is done at the bottom. Specifically, you add a line of code to the build.gradle file to make a development library a dependency on your project (of course you need to call the library API in your project, otherwise why would you add it?). .
However, many libraries do not simply encapsulate a bunch of methods to be called; they often need to be initialized first, which is often a time-consuming process. To make matters worse, there are pitfalls, as these libraries are often loaded and initialized at application startup due to the internal use of content Providers.
Open your heart – Content Provider
Content Providers are Android’s way of sharing data between different applications. For example, contacts on mobile phones are shared via content Providers, which also allows other applications to access the user’s contact data (assuming, of course, that the user gives the application access to the contact data). You can also grant other applications access to data created by your application. Perhaps your application manages a database of donut ratings that other applications may need to use frequently for such important information.
Content Providers are automatically created and started as soon as an application declares content Provider enabled either way.
An important, but perhaps less obvious, problem with using Content Providers is that after an application declares the Content Provider enabled, it is automatically created and run. Moreover, it should be noted that an application is not only started by the user, but also can access the application’s services through the system, or the job scheduler triggers a circular job of the application, etc. All of this triggers the resource overhead of the Content Provider and generates the corresponding computation jobs. When accessing the Content Provider, the system needs the application to be ready. Therefore, the system automatically starts the Content Provider when the application is started.
These details are invisible to developers who simply call these libraries because the implementation is hidden in the automatically generated code. You need to look at the merged manifest file to understand how this happened.
Merge the Manifest
Most of my interaction with Android app listings takes place in the project’s self-generated manifest.xml file, which I edit to add activities, services, and permissions. But the MANIFEST file is not the one that is ultimately submitted to the system, it simply provides information about your application that is “merged” into the final MANIFEST file. The merged file contains your manifest.xml, along with other information selected by the compilation tool, including the Manifest file for the library your application uses. It is this merged manifest file that tells us what is going on with the library’s Content provider.
Let’s look at a specific example. Not all libraries use Content Providers (although it’s quite common), so we’ll use a library that includes content Providers — WorkManager. To use WorkManager in my project, I added the following dependencies to my application’s build.gradle file:
/ / to view the latest version number https://developer.android.google.cn/jetpack/androidx/releases/work def work_version = "2.5.0" implementation "androidx.work:work-runtime-ktx:$work_version"Copy the code
After I synchronized and built the application, I measured the startup time (more on that later) to compare the difference in startup time before and after adding this dependency. I noticed that the application started up 70ms longer after adding the dependency, and this was without calling any of the WorkManager’s functions, I just added the dependency.
I found the cause of the startup delay in the merged manifest file, which you can see when viewing the manifest.xml file, To view the Merged Manifest file, click on the Merged Manifest TAB at the bottom left of the Android Studio edit window.
The tabs at the bottom of the edit window control whether you see your application’s MANIFEST file or the final merged manifest file
In the merged manifest file, I noticed that declaring the WorkManager dependency added a lot of additional information, including the following provider code block:
This provider exists in the manifest file merged after adding WorkManager dependencies
I was curious where the provider came from, so I clicked on the first line and the editor jumped directly to the WorkManager manifest file, which contains the following code:
<application>
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:directBootAware="false"
android:exported="false"
android:multiprocess="true"
tools:targetApi="n" />
<! -... and a bunch of other stuff ... -->
</application>
Copy the code
The merged MANIFEST file works by combining the manifest files of all the modules that make up the application, including, of course, the Manifest files of the WorkManager library I just added. Because the content provider it contains is now part of the merged Manifest file, the system automatically creates and runs the Content provider when the application starts.
Now I know how I load the library and run the associated Content Provider. But what is the effect?
Calculate startup time
I recently published an article, Testing App startup Performance, which detailed how to measure app startup time. I used the same method to measure application startup time before and after adding WorkManager dependencies, and found that WorkManager increased application startup time by an average of 67ms.
Please note that, as I mentioned in my boot test article, I locked the CPU clock rate on my Pixel 2, so the app boot time might be shorter on other users’ devices and longer on others using low-end devices. Another thing to note (which I also mentioned in that article) is that I probably don’t need to lock the clock rate because the system will usually run at the highest frequency when the application starts. However, it is always a good idea to lock the clock frequency during performance testing, because this way we can achieve consistent results. Also, locking the clock frequency usually results in longer running times (due to lower frequencies), which also helps reduce noise data due to too short running times.
It is also important to note that this startup time is not entirely content Provider generated. Content Providers do take some time to create, but they take about 1-2ms instead of 67ms as I saw. This is the total time it took for the library to be loaded and initialized, plus the time it took to create and run the Content Provider to initialize the code base.
So it looks like just adding this library to my project caused a startup delay of nearly 70 milliseconds. In a real application, I might use multiple libraries, many of which have their own Content providers to run when the application starts up, which can cause even more serious startup delays.
So, what can we do to mitigate the impact of this problem? Stay tuned for our next article, where I’ll delve into how AndroidX’s App Startup library can be used for lazy loading.