For more articles, visit blog.mxnzp.com

1, the preface

For those of you who have done SDK, it is common to let the caller pass in as few Context parameters as possible when defining SDK methods. For one thing, the method is cumbersome and the Context passed to you may leak memory if it is not used properly. Second, you might never guess where the caller is going to call your method in some strange place, and he might go through a lot of trouble to give you the Context. So we typically provide a global Init method that the caller will initialize in the onCreate method of the Application, passing us the Context, SDK internal storage.

1.1 Problems?

You’ll find that for larger projects, there’s a lot of init methods under the onCreate method of Application, and it’s really hard to watch. So what are the optimizations for this situation?

1.2 Historical Optimization Scheme?

We’re going to pre-initialize the SDK by registering the ContentProvider, defining a custom ContentProvider and having it inherit from the ContentProvider, and then we’re going to initialize our SDK in the onCreate() method and get the Context, There is no need to let the user self-initialization, to achieve a quiet initialization operation. Doesn’t that look sexy? However, there are many SDKS that use this approach, such as Facebook’s library, Firebase’s library, as well as WorkManager, Lifecycles, and so on.

Is 1.3 as good as it gets?

Using the ContentProvider approach solves the problem of having to explicitly initialize the SDK, but it also introduces other problems:

  • ContentProvider is a heavy component

    ContentProvider is one of the four components of Android. After registering ContentProvider, the startup speed of the entire APP will be slowed down and the performance will be affected. Especially when every SDK adopts this method, the performance loss will be doubled. Although some SDKS initialize just to get a Context.

  • Initialize the SDK upon application startup

    Those of you who have been subject to the privacy protocols of various app markets should know that if your SDK has access to users’ privacy information, you must ask users to agree to privacy permission before using it, otherwise the review will be rejected, so it is not reasonable to use ContentProvider. We need to leave it up to the user to decide when to initialize the SDK.

So, today’s star is Jetpack’s Startup component.

2. What is Startup? What can you do?

2.1 What is Startup?

Let’s start with the official website:

The App Startup library provides a straightforward, performant way to initialize components at application startup. Both library developers and app developers can use App Startup to streamline startup sequences and explicitly set the order of initialization.

Instead of defining separate content providers for each component you need to initialize, App Startup allows you to define component initializers that share a single content provider. This can significantly improve app startup time.

Let’s have some more Youdao translation.

The App Startup library provides a simple, efficient way to initialize components at application Startup time. Both library developers and application developers can use App Startup to simplify the Startup sequence and explicitly set the initialization order.

Instead of defining separate content providers for each component that needs to be initialized, App Startup allows you to define component initializers that share a single content provider. This can significantly improve application startup time.

Finally, a bit of plain English:

It is the management center that manages the initialization of all applications and SDK uniformly by opening a ContentProvider, including the immediate initialization of components, delayed initialization, setting the initialization sequence, etc., which can not only make SDK implicit initialization, but also reduce the initialization of multiple ContentProviders, and improve the overall performance.

2.2 What can Startup do?

  • It implicitly initializes the SDK, gives you the Context

    There is also an internal ContentProvider, but no matter how many SDKS depend on it, it will only initialize one ContentProvider, which solves the performance problem of initializing multiple ContentProviders.

  • It provides lazy initialization

    It provides lazy initialization, which allows you to manually initialize from any location when you do not need to start the initialization immediately.

  • Single initialization is guaranteed

    It internally records the initialization status of the SDK, preventing the problem of repeated initialization through multiple calls.

  • Set the initialization sequence

    When each SDK is initialized, it can specify other SDKS that it depends on and set the initialization sequence, so as to initialize the dependent SDK first and then initialize itself

3, how to use Startup?

3.1 Importing Dependencies

dependencies {
    / / the latest version please see the official document: https://developer.android.com/reference/kotlin/androidx/startup/AppInitializer
    implementation "Androidx. Startup: startup - runtime: 1.1.0." " // The current version is 1.1.0
}
Copy the code

3.2 Custom Components

Implement the Initializer interface for the component, override the create() and dependencies() methods, and initialize them in create(). Dependencies () is a list of other components that the component depends on. Then the component initializes the other components first, and finally the current custom component.

class StartupLibInitializer : Initializer<Unit> {

    override fun create(context: Context) {
        StartupInit.init(context)
    }

    override fun dependencies(a): MutableList<Class<out Initializer<*>>> {
        return mutableListOf()
    }
}
Copy the code

In order to simulate the actual SDK development process, a StartupInit is written incidentally:

/** * initializes */
@SuppressLint("StaticFieldLeak")
object StartupInit {

    // Store context globally
    private var context: Context? = null

    /** * simulate initialization */
    fun init(context: Context): StartupInit {
        Log.e("HHHHHH"."StartupInit is initialized.")
        this.context = context
        return this}}Copy the code

3.3 Registering Components

Startup is essentially a ContentProvider, so you need to register it with the application in the manifest file:

<application>.<provider
            tools:replace="android:authorities"
            android:name="androidx.startup.InitializationProvider"
            android:authorities="com.roll.startuplibrary.androidx-startup"
            android:exported="false"
            tools:node="merge">
    <meta-data  android:name="com.roll.startuplibrary.StartupLibInitializer"
               android:value="androidx.startup" />
  </provider>
</application>
Copy the code

You can forget the other details for a while, but basically register the component you just wrote with meta-data in your provider.

3.4 Viewing the Effect

Start the App and the following will be printed:

2021-10-27 14:19:06.275 17974-17974/com.roll.demo E/HHHHHH: StartupInit is initializedCopy the code

4. Advanced use

4.1 Registering tools: Node for Components

Tools: Node has two common optional values, merge and remove

  • tools:node=”merge”

    This is often used with the Provider node to ensure that the manifest merge tool properly resolves any conflicting items.

  • tools:node=”remove”

    This is usually used by the meta-data node to tell the current meta-data that I want to delete this meta-data, not initialize it by default. So for example, if you integrate an SDK, it’s something that will initialize as soon as it starts, but you don’t want it to, you want it to be initialized in a particular situation, so you can use remove to remove the default behavior, and the SDK won’t initialize automatically.

4.2 Rely on other SDKS

To verify this effect, I defined four components, TestInitializer, Test1Initializer, Test2Initializer, and Test3Initializer, as follows:

  • TestInitializer relies on Test1Initializer and Test2Initializer, and Test1Initializer comes before Test2Initializer
  • Test1Initializer doesn’t rely on anything else
  • Test2Initializer relies on Test3Initializer
  • Test3Initializer doesn’t rely on anything else
class TestInitializer : Initializer<Unit> {

    override fun create(context: Context) {
        Log.e("HHHHH"."TestInitializer trigger")}override fun dependencies(a): MutableList<Class<out Initializer<*>>> {
        return mutableListOf(Test1Initializer::class.java,Test2Initializer::class.java)
    }
}


class Test1Initializer : Initializer<Unit> {
    override fun create(context: Context) {
        Log.e("HHHHH"."Test1Initializer initialized")}override fun dependencies(a): MutableList<Class<out Initializer<*>>> {
        return mutableListOf()
    }
}

class Test2Initializer : Initializer<Unit> {
    override fun create(context: Context) {
        Log.e("HHHHH"."Test2Initializer initialization")}override fun dependencies(a): MutableList<Class<out Initializer<*>>> {
        return mutableListOf(Test3Initializer::class.java)
    }
}

class Test3Initializer : Initializer<Unit> {
    override fun create(context: Context) {
        Log.e("HHHHH"."Test3Initializer initialization")}override fun dependencies(a): MutableList<Class<out Initializer<*>>> {
        return mutableListOf()
    }
}
Copy the code

Registered components:

<application>.<provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge"
            tools:replace="android:authorities">
    <meta-data
               android:name="${applicationId}.TestInitializer"
               android:value="androidx.startup" />
  </provider>
</application>
Copy the code

We only registered TestInitializer, but due to their respective dependencies, all our four components will be registered. Start the APP and print the following:

2021-10-27 14:41:29.479 20566-20566/com.roll.demo E/HHHHH: Test1Initializer is initialized2021-10-27 14:41:29.479 20566-20566/com.roll.demo E/HHHHH: Test3Initializer is initialized2021-10-27 14:41:29.479 20566-20566/com.roll.demo E/HHHHH: Test2Initializer is initialized2021-10-27 14:41:29.479 20566-20566/com.roll.demo E/HHHHH: TestInitializer is triggeredCopy the code

Description:

Test1Initializer depends on Test1Initializer and Test2Initializer, and Test1Initializer precedes Test1Initializer. Test2Initializer depends on Test3Initializer. Therefore, when initializing Test2Initializer, initialize Test3Initializer first, and then initialize Test2Initializer itself. Finally, initialize the TestInitializer.

4.3 Delayed Initialization

If our SDK does not need to be initialized in advance, we can use manual registration instead of registering in the provider, which is as follows:

AppInitializer.getInstance(context)
    .initializeComponent(TestInitializer::class.java)
Copy the code

5, conclusion

Although Startup is just a layer of encapsulation of our previous ideas, but the effect is still very obvious, especially in the performance of a great optimization, or worth recommending.

However, everything is not absolute, for example, you are only doing app application layer business, there is no need to use Startup to initialize, compared to the library developer, this component will be more useful.

Project address: github.com/MZCretin/Je…

Welcome to visit my personal home page: www.mxnzp.com Newly established blog main site: blog.mxnzp.com Github address: github.com/MZCretin