1. Introduction
Recently, I have been working on the startup optimization of Android App. At present, I have achieved good results in two aspects of fast positioning time-consuming methods and merging multiple ContentProviders. This article focuses on merging multiple ContentProviders, especially multiple FileProviders. I noticed that currently there is no tutorial on merging multiple FileProviders online, so this is the first solution to merge multiple FileProviders on the web, which is sure to make you feel fresh and fresh. If you think it’s a good article, help share it with your Android colleagues and friends.
When it comes to merging multiple ContentProviders, App Startup comes to mind in the Jetpack component. This article uses the library to merge multiple contentProviders. The focus of this article is not on how to use App Startup. It’s how to merge multiple FileProviders.
The core outline of this paper is as follows:
- Problems encountered during project startup
- Outlining the ContentProvider
- This section describes how to use App Startup
- This section focuses on the principle of FileProvider
- Reflection hardcodes Uri maps
- Define openFile requests that relay contentProviders to distribute multiple FileProviders
- Use Aspectjx peg to hook the FileProvider’s getUriFromFile return value
Problem 2.
In the process of measuring the start-up time, it was found that the total initialization time of all contentProviders in the project was about 150ms. In order to determine whether this time is time-consuming, I wrote a Demo with only one ContentProvider and found that the time is around 20ms. Since there is a seven-fold difference, I think there is room for optimization in the initialization time of the ContentProvider in the project. Looking at the compiled AndroidManifest file, 14 contentProviders are declared. From the original design of App Startup, we know that it can slim down multiple contentProviders in a project and merge them into one ContentProvider.
Before we merge, we need to think about a question.
Can a ContentProvider written by ourselves be combined with a ContentProvider defined in a third party SDK? If not, what contentProviders can and cannot be merged?
Although ContentProvider is one of the four oldest components, more than 10 years old, it may be unfamiliar to some readers. So to answer this question, we need to take a quick look at the design purpose and implementation of ContentProvider.
3. The ContentProvider is analysed
ContentProvider is an older component of the Android system. It has been around since the beginning of the Android system. It has the following features
- You need to register in the AndroidManifest file
- The system automatically initializes the ContentProvider and calls the onCreate method
- Supports interprocess communication
- Add, delete, modify, and query operations are supported, generally, but not limited to database operations
- Support for calling custom methods
3.1 a simple Demo
Create a new ByteStationContentProvider code is as follows:
Register in the AndroidManifest:
We noticed that ByteStationContentProvider has the following methods:
- onCreate
- Insert, query, update, delete
- call
The conclusions are as follows:
It can be merged if the ContentProvider in the project simply overrides the onCreate method. If you override the add, delete, change, and call methods, they cannot be combined.
3.2 Abuse of ContentProvider
Since the Square team used ContentProovider to automatically initialize the SDK in Picasso’s image-loading library, SDK developers have learned this trick as well, minimizing SDK initialization for upper level developers. Just a few years ago, this was black tech, too. However, with the increase of the project scale, more and more similar SDKS are connected, resulting in the need to initialize more and more ContentProviders during startup, which slows down the startup speed of App.
Picasso automatically initializes the code using the ContentProvider as follows.
We note that Picasso’s insert and Query correlation methods default to empty implementations without any business logic. Then the ContentProvider can be merged.
3.3 Initialization time of the ContentProvider
ContentProvider. OnCreate method calls between Application. AttachBaseContext and Application between onCreate.
Code call diagram
3.4 Calculating the ContentProvider Time
From section 3.3, the App all the ContentProvider time-consuming in equivalent to the Application. The onCreate () start executing time minus Application. AttachBaseContext () the time of the end.
3.5 Viewing the Compiled AndroidManifest File
- Decompile view
- app/build/intermediates/merged_manifests/debug/AndroidManifest.xml
4. App Startup
App Startup Library is a simple and efficient application Startup initialization component based on ContentProvider implementation. Since this article focuses on FileProvider merging. So this chapter is just a brief introduction to its use.
Use App Startup in 3 steps:
- Add the dependent
- Implement the Initializer component
- AndroidManifest file to add a declaration
4.1 Adding a Dependency
4.2 Implementing Initializer Components
Implementing the Initializer interface requires overriding two methods:
- In the create() method, we can put the code that was originally initialized in the ContentProvider here.
- The dependencies() method indicates whether other Initializer components are currently initialized and if so, they are initialized first.
4.3 Add a declaration to the AndroidManifest file
It’s pretty simple to use. See the official documentation for more information. Developer.android.com/topic/libra…
4.4 Removing the ContentProvider from the third-party SDK
Suppose a Provider named ShareContentProvider is declared in the AndroidManifest file of a third-party SDK
To remove it, declare an identical Provider in the AndroidManifest of your app project and add tools:node=”remove”.
5. FileProvider analyses
5.1 Starting from invoking the Installation interface
- Android6.0 and previous versions install apk code as follows
- Android7.0+ install apK code as follows
- Custom FileProvider
2. Register the FileProvider in the AndroidManifest file
- Res/XML folder to create the toutiao.xml file
- Calling setup
We can see the difference between them lies in using uris. FromFile () and FIleProvider getUriFromFile () get the Uri of the file.
The values corresponding to the printed results are as follows:
way | value |
---|---|
Uri | file:///storage/emulated/0/toutiao/toutiao.apk |
FileProvider | content://com.toutiao.install/bytedance/toutiao.apk |
The file path obtained in Uri mode can be easily guessed, which may bring risks to third-party programs. However, the file path obtained by the FileProvider does not easily reveal the location of the file. The FileProvider mechanism was introduced for security reasons.
5.2 Store the FILE corresponding to the XML tag
The following figure shows the file paths corresponding to various tags
NAME | VALUE | PATH |
---|---|---|
TAG_ROOT_PATH | root-path | / |
TAG_FILES_PATH | files-path | /data/user/0/com.xxx/files |
TAG_CACHE_PATH | cache-path | /data/user/0/com.xxx/cache |
TAG_EXTERNAL | external-path | /storage/emulated/0 |
TAG_EXTERNAL_FILES | external-files-path | /storage/emulated/0/Android/data/com.xxx/files |
TAG_EXTERNAL_CACHE | external-cache-path | /storage/emulated/0/Android/data/com.xxx/cache |
5.3 MAPPING table of URIs and paths in XML
- The FileProvider sCache
FileProvider has a static variable called sCache. Key stores the FileProvider’s authority, and value stores the PathStrategy content in the XML corresponding to FileProvider.
- The SimplePathStrategy mRoots
MRoots is also a hashMap, where the key corresponds to the NAME node in XML. Value corresponds to the combination of tag and path.
<files-path name="apk" path="."/>
Copy the code
key: apk
value: /data/user/0/com.xxx/files
The corresponding relationship between sCache and mRoots is shown as follows:
5.4 FileProvider parses XML
- After the FileProvider is automatically installed, attachInfo is invoked
- Call parsePathStrategy to parse the XML
- The getPathStrategy method puts the parsed PathStrategy into the sCache
6. FileProvider merger
To merge FileProvider, we need to remove the FileProvider defined by the third party SDK from the AndroidManifest file. But in doing so we face two problems.
- The Uri and file path mapping in XML cannot be written to the sCache of the FileProvider
- File sharing between processes is done by finding the ContentProvider corresponding to the authority in the Uri. Calling its openFile method will cause file sharing to fail if the ContentProvider is not declared
Solutions are as follows:
- By reflection, the mapping of the XML is hardcoded into the sCache of the FileProvider
- Define a transit ContentProvider and declare it in the AndroidManifest file to take over all FileProvider openFile methods
- Use Aspect peg to convert the authority hook of all the URIs returned by FileProvider getUriForFile() into the authority of the ContentProvider
6.1 Reflection hardcoded write mapping
- The mapping is written to the sCache by reflection
- Hardcode the Authority information and XML information of the FileProvider that should be registered in the AndroidManifest to sCache
6.2 Defining the transit ContentProvider
Authorities are com. Peter. Dispatch. It’s mainly a transit effect
6.3 the Aspect hook authority
6.3.1 Root directory build.gradle Add AspectJx
6.3.2 App /build.gradle applies Aspect plug-ins
6.3.3 hook FileProvider.getUriFromFile
Will the content: / / com. Toutiao. Install/bytedance/toutiao apk
Converted to
content://com.peter.dispatch/com.toutiao.install/bytedance/toutiao.apk
6.3.4 Overwriting the openFile for forwarding CP
7. To summarize
This article focuses on how to combine multiple FileProviders to optimize App startup speed. In the next article I’ll show you how to quickly locate slow methods that affect Application cold startup speed. Please follow, share, like and leave a comment.