Android JetpackのApp Startup
1.概要
ここ数日注目されているいくつかの公衆番号がApp Startupに関する文章を続々と送っているので、また新しい知識を学ぶことになります.App StartupはLifecycle、DataBindingと同様にJetpackのコンポーネントの1つです.現在、App Startupはalphaバージョンにあります.公式サイトのアドレス:https://developer.android.google.cn/topic/libraries/app-startup?hl=en.
2.App Startup定義
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.
翻訳は以下の通りです.
App Startupライブラリは、アプリケーションの起動時にコンポーネントを初期化するための簡単で効率的な方法を提供します.開発者は、App Startupを使用して起動シーケンスを簡略化し、初期化順序を明示的に設定できます.
App Startupでは、初期化が必要なコンポーネントごとにcontentProviderを定義するのではなく、単一のコンテンツプロバイダを共有するコンポーネント初期化器を定義できます.これにより、アプリケーションの起動時間が大幅に向上します.
3.ContentProviderで初期化
ContentProviderを定義すると、Appの起動段階でいつ初期化されますか?このロゴを見てください.
ここでContentProviderのonCreateメソッドの実行時間は、ApplicationのattachBaseContextとonCreateの間にあることがわかります.そのため、多くのライブラリはContentProviderを作成して初期化することを選択します.ContentProviderによる初期化については、ContentProviderを使用してLibraryを初期化する方法について説明します.ここではあまり説明しませんが、App Startupはこの方法に基づいています.
4.App Startupのインポート
現在、App Startupはalphaバージョンにあります.
5.App Startupの使用
現在、SdkA、SdkB、SdkCを初期化する必要があるライブラリが3つあると仮定します.ここでは、SdkAを示すコードのみが書かれています.SdkB、SdkC、SdkAの構造は似ています.
対応する初期化オブジェクトを作成するには、インタフェースInitializerが必要です.インタフェースInitializerのコードは次のとおりです.
createメソッドはオブジェクトの初期化に使用されます.
dependenciesメソッドは、現在のオブジェクトを初期化する前に初期化する必要があるオブジェクトに対応するInitializerを定義するために使用されます.
SdkAの対応するSdkAInitializer:
SdkAの初期化は他のライブラリの依存性を必要としないので,ここでdependenciesメソッドは空のリストを返す.
SdkBの対応するSdkBInitializer:
ここで、SdkAはSdkBより前に初期化しなければならないと仮定し、dependenciesはSdkAに対応するSdkAInitializerを返す.
SdkCの対応するSdkCInitializer:
ここで、SdkBは、SdkCの前に初期化されなければならないと仮定し、dependenciesは、SdkBに対応するSdkBInitializerを返す.
次に、manifestでInitializationProviderを定義します.
上の3つのsdkでは,C依存B,B依存A,Aは他に依存しなかった.ラベルにはSdkCInitializerを定義するだけです.
実行後のロゴは次のとおりです.
logからdependenciesメソッドの実行順序はC→B→A,createメソッドの実行順序はA→B→Cであることがわかる.ここでcreateメソッドの順序は、オブジェクトが作成される順序です.
5.1手動初期化
上記の例では、アプリケーションが実行されると、すべてのSDKが初期化される.しかし、すべてのSDKは起動段階で初期化され、起動速度が遅くなります.一部のSDKはどのように使用する必要があるときに初期化しますか?
次のようにSdkDを作成します.SdkCInitializerクラスは次のとおりです.
manifestでラベルでSdkDInitializerを定義し、toolsを追加する必要があります:node="remove":
Activityで初期化:
実行後のlogは次のとおりです.
logからSdkDが最後に初期化されたことがわかる.
6.ソース分析
M anifestでInitializationProviderを定義するのはContentProviderです.
1つのonCreateメソッドのみを保持し、AppInitializerのdiscoverAndInitializeメソッドを呼び出します.
AppInitializerは、AppInitializerの構築方法の一例です.
構築方法でHashMapを作成します.
discoverAndInitializeメソッド:
まずmeta-dataラベルを取得し、各meta-dataのInitializerオブジェクトを巡りdoInitializeメソッドを呼び出します.
注記1:まず、対応するInitializerを初期化し、そのdependenciesが空を返すかどうかを判断し、空でなければ対応するInitializerを取得し、doInitializeを実行し続ける(注記2).これにより,dependenciesメソッドは先に実行され,dependenciesがすべて実行された後にのみ注釈3のcreateメソッドが実行される.
遅延ロード時に呼び出されるinitializeComponentメソッドを見てみましょう.
ここでdoInitializeメソッドを呼び出します.
7.まとめ
App Startupは、すべての依存項目の初期化を実行するContentProviderを提供し、依存項目をApplicationに書くことを回避し、初期化が必要なライブラリごとにContentProviderを単独で初期化することを回避し、Appの起動速度を向上させる.App Startupでは、初期化が必要な依存初期化順序をカスタマイズできます.App Startupでは、初期化が必要な依存ごとにInitializerインタフェースを実装する必要があり、ファイルが増加します.App Startupは現在alphaバージョンにあり、後続のバージョンの更新でより多くのハイライトがあると信じています.
ここ数日注目されているいくつかの公衆番号がApp Startupに関する文章を続々と送っているので、また新しい知識を学ぶことになります.App StartupはLifecycle、DataBindingと同様にJetpackのコンポーネントの1つです.現在、App Startupはalphaバージョンにあります.公式サイトのアドレス:https://developer.android.google.cn/topic/libraries/app-startup?hl=en.
2.App Startup定義
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.
翻訳は以下の通りです.
App Startupライブラリは、アプリケーションの起動時にコンポーネントを初期化するための簡単で効率的な方法を提供します.開発者は、App Startupを使用して起動シーケンスを簡略化し、初期化順序を明示的に設定できます.
App Startupでは、初期化が必要なコンポーネントごとにcontentProviderを定義するのではなく、単一のコンテンツプロバイダを共有するコンポーネント初期化器を定義できます.これにより、アプリケーションの起動時間が大幅に向上します.
3.ContentProviderで初期化
ContentProviderを定義すると、Appの起動段階でいつ初期化されますか?このロゴを見てください.
2020-07-02 14:38:16.905 6928-6928/cn.zzw.template.startupdemo E/StartUpDemo: Application attachBaseContext
2020-07-02 14:38:16.907 6928-6928/cn.zzw.template.startupdemo E/StartUpDemo: ContentProvider onCreate
2020-07-02 14:38:16.908 6928-6928/cn.zzw.template.startupdemo E/StartUpDemo: Application onCreate
ここでContentProviderのonCreateメソッドの実行時間は、ApplicationのattachBaseContextとonCreateの間にあることがわかります.そのため、多くのライブラリはContentProviderを作成して初期化することを選択します.ContentProviderによる初期化については、ContentProviderを使用してLibraryを初期化する方法について説明します.ここではあまり説明しませんが、App Startupはこの方法に基づいています.
4.App Startupのインポート
現在、App Startupはalphaバージョンにあります.
dependencies {
implementation "androidx.startup:startup-runtime:1.0.0-alpha01"
}
5.App Startupの使用
現在、SdkA、SdkB、SdkCを初期化する必要があるライブラリが3つあると仮定します.ここでは、SdkAを示すコードのみが書かれています.SdkB、SdkC、SdkAの構造は似ています.
class SdkA {
companion object {
fun getInstance(): SdkA {
return Instance.instance
}
}
private object Instance {
val instance = SdkA()
}
}
対応する初期化オブジェクトを作成するには、インタフェースInitializerが必要です.インタフェースInitializerのコードは次のとおりです.
public interface Initializer {
/**
* Initializes and a component given the application {@link Context}
*
* @param context The application context.
*/
@NonNull
T create(@NonNull Context context);
/**
* @return A list of dependencies that this {@link Initializer} depends on. This is
* used to determine initialization order of {@link Initializer}s.
*
* For e.g. if a {@link Initializer} `B` defines another
* {@link Initializer} `A` as its dependency, then `A` gets initialized before `B`.
*/
@NonNull
List>> dependencies();
}
createメソッドはオブジェクトの初期化に使用されます.
dependenciesメソッドは、現在のオブジェクトを初期化する前に初期化する必要があるオブジェクトに対応するInitializerを定義するために使用されます.
SdkAの対応するSdkAInitializer:
class SdkAInitializer : Initializer {
override fun create(context: Context): SdkA {
Log.e("StartUpDemo", "SdkAInitializer create");
return SdkA.getInstance();
}
override fun dependencies(): MutableList>> {
Log.e("StartUpDemo", "SdkAInitializer dependencies");
return Collections.emptyList()
}
}
SdkAの初期化は他のライブラリの依存性を必要としないので,ここでdependenciesメソッドは空のリストを返す.
SdkBの対応するSdkBInitializer:
class SdkBInitializer : Initializer {
override fun create(context: Context): SdkB {
Log.e("StartUpDemo", "SdkBInitializer create");
return SdkB.getInstance();
}
override fun dependencies(): MutableList>> {
Log.e("StartUpDemo", "SdkBInitializer dependencies");
return mutableListOf(SdkAInitializer::class.java)
}
}
ここで、SdkAはSdkBより前に初期化しなければならないと仮定し、dependenciesはSdkAに対応するSdkAInitializerを返す.
SdkCの対応するSdkCInitializer:
class SdkCInitializer : Initializer {
override fun create(context: Context): SdkC {
Log.e("StartUpDemo", "SdkCInitializer create");
return SdkC.getInstance();
}
override fun dependencies(): MutableList>> {
Log.e("StartUpDemo", "SdkCInitializer dependencies");
return mutableListOf(SdkBInitializer::class.java)
}
}
ここで、SdkBは、SdkCの前に初期化されなければならないと仮定し、dependenciesは、SdkBに対応するSdkBInitializerを返す.
次に、manifestでInitializationProviderを定義します.
上の3つのsdkでは,C依存B,B依存A,Aは他に依存しなかった.ラベルにはSdkCInitializerを定義するだけです.
実行後のロゴは次のとおりです.
2020-07-02 17:01:11.446 8212-8212/cn.zzw.template.startupdemo E/StartUpDemo: Application attachBaseContext
2020-07-02 17:01:11.549 8212-8212/cn.zzw.template.startupdemo E/StartUpDemo: ContentProvider onCreate
2020-07-02 17:01:11.577 8212-8212/cn.zzw.template.startupdemo E/StartUpDemo: SdkCInitializer dependencies
2020-07-02 17:01:11.600 8212-8212/cn.zzw.template.startupdemo E/StartUpDemo: SdkBInitializer dependencies
2020-07-02 17:01:11.601 8212-8212/cn.zzw.template.startupdemo E/StartUpDemo: SdkAInitializer dependencies
2020-07-02 17:01:11.601 8212-8212/cn.zzw.template.startupdemo E/StartUpDemo: SdkAInitializer create
2020-07-02 17:01:11.601 8212-8212/cn.zzw.template.startupdemo E/StartUpDemo: SdkBInitializer create
2020-07-02 17:01:11.605 8212-8212/cn.zzw.template.startupdemo E/StartUpDemo: SdkCInitializer create
2020-07-02 17:01:11.647 8212-8212/cn.zzw.template.startupdemo E/StartUpDemo: Application onCreate
logからdependenciesメソッドの実行順序はC→B→A,createメソッドの実行順序はA→B→Cであることがわかる.ここでcreateメソッドの順序は、オブジェクトが作成される順序です.
5.1手動初期化
上記の例では、アプリケーションが実行されると、すべてのSDKが初期化される.しかし、すべてのSDKは起動段階で初期化され、起動速度が遅くなります.一部のSDKはどのように使用する必要があるときに初期化しますか?
次のようにSdkDを作成します.SdkCInitializerクラスは次のとおりです.
class SdkDInitializer : Initializer {
override fun create(context: Context): SdkD {
Log.e("StartUpDemo", "SdkDInitializer create");
return SdkD.getInstance();
}
override fun dependencies(): MutableList>> {
Log.e("StartUpDemo", "SdkDInitializer dependencies");
return Collections.emptyList()
}
}
manifestでラベルでSdkDInitializerを定義し、toolsを追加する必要があります:node="remove":
Activityで初期化:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
AppInitializer.getInstance(this).initializeComponent(SdkDInitializer::class.java)
}
実行後のlogは次のとおりです.
2020-07-02 18:03:21.070 8850-8850/cn.zzw.template.startupdemo E/StartUpDemo: Application attachBaseContext
2020-07-02 18:03:21.194 8850-8850/cn.zzw.template.startupdemo E/StartUpDemo: ContentProvider onCreate
2020-07-02 18:03:21.206 8850-8850/cn.zzw.template.startupdemo E/StartUpDemo: SdkCInitializer dependencies
2020-07-02 18:03:21.213 8850-8850/cn.zzw.template.startupdemo E/StartUpDemo: SdkBInitializer dependencies
2020-07-02 18:03:21.221 8850-8850/cn.zzw.template.startupdemo E/StartUpDemo: SdkAInitializer dependencies
2020-07-02 18:03:21.221 8850-8850/cn.zzw.template.startupdemo E/StartUpDemo: SdkAInitializer create
2020-07-02 18:03:21.221 8850-8850/cn.zzw.template.startupdemo E/StartUpDemo: SdkBInitializer create
2020-07-02 18:03:21.222 8850-8850/cn.zzw.template.startupdemo E/StartUpDemo: SdkCInitializer create
2020-07-02 18:03:21.228 8850-8850/cn.zzw.template.startupdemo E/StartUpDemo: Application onCreate
2020-07-02 18:03:21.729 8850-8850/cn.zzw.template.startupdemo E/StartUpDemo: SdkDInitializer dependencies
2020-07-02 18:03:21.729 8850-8850/cn.zzw.template.startupdemo E/StartUpDemo: SdkDInitializer create
logからSdkDが最後に初期化されたことがわかる.
6.ソース分析
M anifestでInitializationProviderを定義するのはContentProviderです.
@RestrictTo(RestrictTo.Scope.LIBRARY)
public final class InitializationProvider extends ContentProvider {
@Override
public boolean onCreate() {
Context context = getContext();
if (context != null) {
AppInitializer.getInstance(context).discoverAndInitialize();
} else {
throw new StartupException("Context cannot be null");
}
return true;
}
...
}
1つのonCreateメソッドのみを保持し、AppInitializerのdiscoverAndInitializeメソッドを呼び出します.
AppInitializerは、AppInitializerの構築方法の一例です.
AppInitializer(@NonNull Context context) {
mContext = context.getApplicationContext();
mInitialized = new HashMap<>();
}
構築方法でHashMapを作成します.
discoverAndInitializeメソッド:
void discoverAndInitialize() {
try {
Trace.beginSection(SECTION_NAME);
ComponentName provider = new ComponentName(mContext.getPackageName(),
InitializationProvider.class.getName());
ProviderInfo providerInfo = mContext.getPackageManager()
.getProviderInfo(provider, GET_META_DATA);
Bundle metadata = providerInfo.metaData;
String startup = mContext.getString(R.string.androidx_startup);
if (metadata != null) {
Set> initializing = new HashSet<>();
Set keys = metadata.keySet();
for (String key : keys) {
String value = metadata.getString(key, null);
if (startup.equals(value)) {
Class> clazz = Class.forName(key);
if (Initializer.class.isAssignableFrom(clazz)) {
Class extends Initializer>> component =
(Class extends Initializer>>) clazz;
if (StartupLogger.DEBUG) {
StartupLogger.i(String.format("Discovered %s", key));
}
doInitialize(component, initializing);
}
}
}
}
} catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
throw new StartupException(exception);
} finally {
Trace.endSection();
}
}
まずmeta-dataラベルを取得し、各meta-dataのInitializerオブジェクトを巡りdoInitializeメソッドを呼び出します.
T doInitialize(
@NonNull Class extends Initializer>> component,
@NonNull Set> initializing) {
synchronized (sLock) {
...
Object result;
if (!mInitialized.containsKey(component)) {
initializing.add(component);
try {
Object instance = component.getDeclaredConstructor().newInstance();
Initializer> initializer = (Initializer>) instance;
List>> dependencies =
initializer.dependencies(); // 1
if (!dependencies.isEmpty()) {
for (Class extends Initializer>> clazz : dependencies) {
if (!mInitialized.containsKey(clazz)) {
doInitialize(clazz, initializing); // 2
}
}
}
if (StartupLogger.DEBUG) {
StartupLogger.i(String.format("Initializing %s", component.getName()));
}
result = initializer.create(mContext); // 3
if (StartupLogger.DEBUG) {
StartupLogger.i(String.format("Initialized %s", component.getName()));
}
initializing.remove(component);
mInitialized.put(component, result);
} catch (Throwable throwable) {
throw new StartupException(throwable);
}
} else {
result = mInitialized.get(component);
}
return (T) result;
} finally {
Trace.endSection();
}
}
}
注記1:まず、対応するInitializerを初期化し、そのdependenciesが空を返すかどうかを判断し、空でなければ対応するInitializerを取得し、doInitializeを実行し続ける(注記2).これにより,dependenciesメソッドは先に実行され,dependenciesがすべて実行された後にのみ注釈3のcreateメソッドが実行される.
遅延ロード時に呼び出されるinitializeComponentメソッドを見てみましょう.
public T initializeComponent(@NonNull Class extends Initializer> component) {
return doInitialize(component, new HashSet>());
}
ここでdoInitializeメソッドを呼び出します.
7.まとめ
App Startupは、すべての依存項目の初期化を実行するContentProviderを提供し、依存項目をApplicationに書くことを回避し、初期化が必要なライブラリごとにContentProviderを単独で初期化することを回避し、Appの起動速度を向上させる.App Startupでは、初期化が必要な依存初期化順序をカスタマイズできます.App Startupでは、初期化が必要な依存ごとにInitializerインタフェースを実装する必要があり、ファイルが増加します.App Startupは現在alphaバージョンにあり、後続のバージョンの更新でより多くのハイライトがあると信じています.