AndroidがMainActivityを起動する方法

私は最近、Javaのmain()メソッドと、それがどのようにJavaアプリケーションのエントリポイントとして機能するかについて調査しました。 考えさせられましたが、Androidアプリはどうですか? 基本的な方法はありますか? どのようにロードしますか? onCreate()が実行される前に、舞台裏で何が起こりますか? Michael BaileyがMain Threadの仕組みについて詳しく説明したので、これは彼の講演の簡単な概要に加えて、Android Open Source Project(AOSP)からの追加情報です。



この記事では、次のことを検討します。



  1. アプリケーションアイコンをクリックしてからMainActivityを起動するまでの流れ
  2. メインアプリケーションメソッドを見つけて、メインスレッド(別名UI、別名メインスレッド)がどのように目的を達成するかを調べます。
  3. ルーパーとハンドラーがメッセージングで果たす役割を検討してください。最終的にアクティビティの作成につながります。


アプリケーションの起動時に何が起こるか



アプリケーションを起動すると、カーネルレベルの奥深くで多くのことが起こります。たとえば、Zygoteブートストラップ、JVMでのクラスのロード、JVMの場合、mainメソッドstatic void main(String args [])を見つけて呼び出します。 Androidの場合、JVMはActivityThreadでメインのmain()メソッドを見つけます。 次にmain()を呼び出します。その後、カーネルはアプリケーションに制御を渡します。 そのため、エントリポイント-ActivityThreadを見つけましたが、これを詳細に検討する前に、プロセスロードマップを見て、操作全体を視覚化しましょう。



1アプリケーション起動スキーム



MainActivityのmain()メソッドとonCreate()の呼び出しの間には約15のステップがあり、この記事ではそれらを検証します。 図1は一般的なアプリケーションの起動図を示し、上からのさまざまな相互作用クラスと、対応するメソッドのチェーンを示しています。 ステップには番号が付けられており、参照するときは、Process3またはProcess14という表記を使用します。



画像

図1:MainActivityでmain()を呼び出してからonCreate()を呼び出すまでの手順でアプリケーションを起動するスキーム



2.クラスActivityThread



ActivityThreadクラスには6500行強あります。 簡潔にするために、私たちにとって最も重要な部分を特定しました。 Activityを開始するためにこのクラスとその基礎となるメインメソッドが行うことを見てみましょう。



/** * Code retrieved from https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ActivityThread.java * Modifications are indicated in the comments */ public static void main(String[] args) { //Modification - Removed unrelated initializers. //Android initializes some tracers, event loggers, enviroment initializers, trusted certificates and updates the process' state Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } // More logging // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
      
      





図2:ActivityThreadのmain()メソッド。アプリケーションを起動するためのエントリポイントとして機能します。



コードでわかるように、main()メソッドは3つの重要なことを行います。



1.メインルーパー(MainLooper)を準備します(プロセス2)

2. Handler'aセットアップ(プロセス4)

3.メインスレッド(MainThread)でLooper.loop()メソッドを呼び出す(プロセス6)



2.1メインルーパーの準備(プロセス2-3)



メインのルーパーは、Looper.prepareMainLooper()を呼び出すことで定義されます(コードの8行目を参照)。 これにより、現在のランダムスレッドがマークされ、メイン()メソッドをメインアプリケーションスレッドとして呼び出すすべての作業が実行されます。 Androidのアプリケーションの有名なメインスレッドが定義されているのはまさにここです。



2.2ハンドラーの呼び出し(プロセス4-5)



ActivityThreadクラスの内部には、プライベートな内部クラスHがあります。そうです、まさにHです。これは、Handlerクラスから継承します(図4および7を参照)。 12行目に、Hハンドラーインスタンスがスレッドのメインハンドラーとしてインストールされます。 クラスHについて知っておくと非常に興味深いのは、後で見るように、アプリケーションに含まれる可能性のある50を超える状態/イベント定義(LAUNCH_ACTIVITY、PAUSE_ACTIVITY、BI​​ND_SERVICEなど)が含まれていることです。



2.3ルーパーのloop()メソッドを呼び出す(プロセス6–7)



同じメインスレッドでメインスレッドを割り当てた後、その中で何かを実行できるように、Looper.loop()メソッドが呼び出されます(20行目を参照)。 これにより、Loopersメッセージキュー内のメッセージの実行が開始されます。 これでメインスレッドが開始され、キューからタスクの処理を開始できます。



18行目で、コード実行が17行目のLooper.loop()よりも先に進み、アプリケーションがループを終了すると、RuntimeExceptionがスローされることに注意してください。 これは、loop()メソッドが理想的には途中で終了しないことを示唆しています。 次のセクションでその方法を確認します。



3.ルーパーの無限ループ()(プロセス7,8,9)



 /** * AOSP * Looper class */ public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; //code removed for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } } }
      
      





図3:Looperクラスのloop()メソッド内のコード



コードでわかるように、Looper.loop()メソッドにはメッセージキューがあり(10行目)、queue.next()がループ内で呼び出されます。 MessageQueueには、前のセクションで説明したハンドラーが入力されます(プロセス8を参照)。 forループ内の条件の興味深い説明に注意してください。引数はありません。2つのセミコロンのみが無限ループであると言います。 したがって、特定のメッセージがnullでない場合、Looperは理想的には終了しません。



したがって、Looperによって実行されるメインスレッドを特定したので、HandlerがメッセージをLooper.loops()ループに追加し、メッセージを処理することも確認しました。 それらが一緒にアクティビティを呼び出す方法を見てみましょう。



4. MainActivityを起動します(プロセス10〜15)



この無限ループとメッセージ処理は、ActivityThreadクラスのmain()メソッドで実行されたことを覚えておくことは重要です。なぜなら、それらは呼び出されたためです(コードの行12〜17を参照)。 ルーパー、メッセージキュー、ハンドラーを表面的に見て、コンテキストを理解しました。 それでは、ActivityThreadクラス、特に、前に説明したメインクラスのメインハンドラーとして機能する内部クラスHに戻りましょう。



したがって、ハンドラーにメッセージを送信するルーパーがあります。これらのメッセージがどのように処理されるかを見てみましょう。 これはクラスH内で行われます。このクラスにはhandleMessage(Message msg)メソッドが含まれます。 Handlerを継承するすべてのクラスがこのメソッドをオーバーライドする必要があることに注意してください。



 /** * Retrieved from AOSP * H class embedded in the ActivityThread class */ private class H extends Handler { //Several Application State Identifiers ... public void handleMessage(Message msg) { //other code switch (msg.what) { case LAUNCH_ACTIVITY: { //create Activity records handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); ... //handle other cases eg ResumeActivity, PauseActivity, BindService, UnbindService etc. } } } }
      
      





図4:プライベート内部クラスHとそのhandleMessage()メソッド



コードからわかるように、8行目にはswitchステートメントがあり、その内容によって着信メッセージの処理を決定します。



ケースの1つはアクティビティの開始(11行目)であり、興味深いのは、このメソッドが約50のケースを処理するように設計されていることです。これは、アクティビティの再開、一時停止、開始、サービスのバインド、レシーバの処理、デバイスのメモリがいっぱいになったときにlowMemoryまたはtrimMemory警告を提供するなど。



LAUNCH_ACTIVITYの場合、13行目に示すように、handleLaunchActivity()メソッドが呼び出されます(図のProcess11を参照)。 次に、このメソッドは、performLaunchActivity()という別のメソッドを呼び出し、Activityオブジェクトを返します(図5の7行目を参照)。



 /** * Retrieved from AOSP. * ActivityThread class */ private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { //... initialize graphics,do some logging, call GC if need be, etc Activity a = performLaunchActivity(r, customIntent); //... handle how to resume an existing activity }
      
      





図5:アクティビティが作成されるhandleLaunchActivity()メソッド



performLaunchActivity()メソッドは、インストルメンテーション、コンテキスト、コンポーネント、インテントなどの重要な情報をアクティビティに追加します。 また、アプリケーションを設定します。 このメソッドは、Instrumentation.callActivityOnCreate()(プロセス13)を呼び出します。これは、アクティビティ(プロセス14〜15、図5(コード)、行8〜10を参照)でonCreate()メソッドを呼び出す前の最後のステップです。



 /** * @Retrieved from AOSP * Instrumentation class */ public void callActivityOnCreate(Activity activity, Bundle icicle) { //   Activity    prepareLaunchActivity(). //      onCreate() prePerformCreate(activity); //  Activity activity.performCreate(icicle); //  onCreate() postPerformCreate(activity); }
      
      





図6:インストルメンテーションクラスが最終的にアクティビティを開始



現時点では、アクティビティには多くの便利な変数とメソッドがロードされており、これらを使用して新しい素晴らしいAndroidアプリケーションを作成できます。 これはすべて、ActivityThread、HandlerとLooperの巧妙な作業、およびフラグメントを添付し、コンテキストを取得し、Viewを簡単に管理できるようにする7600行のコードの巨大なActivityクラスのおかげです。



これがアクティビティの作成方法です!



元の記事はこちらです。



All Articles