Jetpack Startup elegantly completes library initialization and method interface simplification

1.What is the Startup component?

The Startup component was introduced by Google Jetpack. It provides a direct and efficient way to initialize multiple components when the application starts. Developers can rely on it to explicitly set the initialization order among multiple components, and Optimize application startup time. Before there was the Startup component, many third-party libraries often defined a ContentProvider to obtain the Context object in order to simplify the use cost of developers, and then automatically completed the initialization of the library. That is, when we used the library before, we always needed to use it in the Application. Call the XXXLib.init(Context) method of a certain library, mainly to pass our Context to the third-party library. Since ContentProvider will be called before Application's onCreate(), the Context can be obtained in advance by defining ContentProvider. However, there will be problems with this, because if each third-party library defines its own ContentProvider and performs time-consuming operations when obtaining the Context, this will cause the application to start slowly and cause a black screen for a long time. Therefore, Google launched the Startup component, which allows library developers and App developers to share a ContentProvider to complete their respective initialization logic, and supports setting the initialization sequence between components. In this way, the application can choose to initialize the libraries that need to be used immediately, and other libraries that do not need to be used immediately can be initialized later. This reduces application startup time.

2.What can the Startup component do?

2.1 The startup component can simplify the process for users to use the libraries we provide.

For example, we are very familiar with the memory leak detection tool LeakCarry. Before using the Startup component, you need to initialize it in the Application, as shown below:

Insert image description hereIn other words, users not only need to introduce dependencies, but also need to do initialization work. But after introducing the startup component, you can use LeakCanary to only introduce dependencies. The LeakCanary library will get the desired Context through the API provided by the startup component, and then perform memory leak detection. The following is a screenshot of the LeakCanary library using the startup component:
First, in the Manifest

Insert image description hereInitialization class:
Insert image description here Then we can see that the Context can be obtained and initialized in the LeakCanary library. So when we use it, we only need to introduce dependencies:

dependencies {
    
    
  // debugImplementation because LeakCanary should only run in debug builds.
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}

You can use the LeakCanary library to detect memory leaks.

2.2 Simplify the API interface provided by the library to users

Today's VR glasses are basically based on the Android system, and the rendering of 3D scenes relies on Unity and OpenGL. Part of the data rendered by Unity and OpenGL needs to be provided by the Android system, so the Android development interface is required to be provided to Unity. Or an OpenGL call. But many times the interface needs to be provided using a singleton and the Context needs to be passed, as shown below:

Insert image description hereIn this way, the caller needs to use the LoadAllAPIEngine.getInstance(context).getInfo() method to use the services we provide when calling, and also needs to call the LoadAllAPIEngine.getInstance(context).init() method to let us provide the interface. The library does some initialization actions. During the development process, I found that colleagues who used the interface often called the init() method multiple times, or forgot to call the release() method, resulting in some unknown problems. And if OpenGL calls our interface, it often needs to reflect to obtain the Android Context, and then pass it to the sdk library that provides the service every time the interface is called. These increase the burden on SDK users and make it difficult for us to check problems.

而利用Startup组件可以可以解决这个问题,使用了startup组件后,使用者只要引入我们的库依赖,就可以使用我们的提供的接口方法获取到想要的信息了,使用startup组件后,调用接口就可以变成。 LoadAllAPIEngine.getInstance().getInfo()了,并且不用调用LoadAllAPIEngine.getInstance(context).init()方法,直接使用接口即可。

3. How to use the Startup component

3.1 Introducing dependencies

implementation("androidx.startup:startup-runtime:1.1.1")

3.2 Create an initialized class that inherits Initializer

Then override the create() method and dependencies() method. Then in the create() method, you can get the Context of the APP that references our library.

class StartupInitializer : Initializer<StartupInitializer>{
    
    
    override fun create(context: Context): StartupInitializer = apply {
    
    
        val application = context.applicationContext as Application
        application.registerActivityLifecycleCallbacks(object 
        :ActivityLifecycleCallbacks{
    
    
            override fun onActivityCreated(activity: Activity, 
            savedInstanceState: Bundle?) {
    
    
                FMSdk.getInstance().init(activity)
                Log.d(TAG,"onActivityCreated")
            }

            override fun onActivityStarted(activity: Activity) {
    
    

            }

            override fun onActivityResumed(activity: Activity) {
    
    
                FMSdk.getInstance().resume()
                Log.d(TAG,"onActivityResumed")

            }

            override fun onActivityPaused(activity: Activity) {
    
    

            }

            override fun onActivityStopped(activity: Activity) {
    
    
                FMSdk.getInstance().pause()
                Log.d(TAG,"onActivityStopped")
            }

            override fun onActivitySaveInstanceState(activity: Activity, 
            outState: Bundle) {
    
    

            }

            override fun onActivityDestroyed(activity: Activity) {
    
    
                FMSdk.getInstance().release()
                Log.d(TAG,"onActivityDestroyed")
            }

        })
    }

    //获取在初始化自身之前需要先初始化的其他Initializer 列表 ,
    // 这里有助于我们管理库的依赖顺序,比如我们的库依赖其他的库A,并且需要在
    // A之后进行初始化,就可以在这里配置
    //如果不需要依赖于其它组件,则可以返回一个空列表
    override fun dependencies()  = emptyList<Class<out Initializer<*>>>()
}

3.3 Add configuration to AndroidManifest.xml of our library

After defining the initialization class, we need to declare a provider in our library project, configure the authorities to be the package name of the app that references our library + ".androidx-startup", and then define a <meta-data.../> node, Configure the initialization class reference we defined. The class reference I defined here is: com.nolovr.core.fmsdk.engine.StartupInitializer. Please refer to the code below for specific configuration.

<application>
    <provider
        android:name="androidx.startup.InitializationProvider"
        android:authorities="${
      
      applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <meta-data
            android:name="com.nolovr.core.fmsdk.engine.StartupInitializer"
            android:value="androidx.startup"
            />
    </provider>    
</application>

After the above steps are configured, developers can directly introduce dependencies when accessing our library, and then directly call the corresponding methods in our library without going to the Application to do any initialization operations.

4. Points to note when using Startup components

(1) When using Startup, we need to be careful not to perform time-consuming operations in the create() method of StartupInitializer, because the create method is called on the main thread. If time-consuming operations are performed, the initialization time will become longer and the application will start slowly. , the problem of black and white screen.
(2) The tools:node="remove" statement cannot be used in the main project that references the library. It requires itself to be removed when merging the AndroidManifest file, otherwise the automatic initialization function will not be available.例如不能在主项目中做如下配置:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${
      
      applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="com.nolovr.core.fmsdk.engine.StartupInitializer"
        android:value="androidx.startup"
        tools:node="remove" />
</provider>

or it could be:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${
      
      applicationId}.androidx-startup"
    tools:node="remove" />

Because this will cause our startup component to not take effect, of course, except if you want to manually initialize the library yourself.

Guess you like

Origin blog.csdn.net/zxj2589/article/details/134980692