LeakCanary のサービス ライフサイクル監視を参照してください。

著者: Xiaohaiコーディング日記

LeakCanary を使用するとプロジェクト内のメモリ リークを監視できることは誰もが知っていますが、LeakCanary はどのようにしてそれを実現するのでしょうか? LeakCanary は、プログラム内のオブジェクトの参照関係を検出し、リサイクルされる必要があるオブジェクトを収集してマークし、GC がオブジェクトが期待どおりにリサイクルされているかどうかを確認するのを待ちます。現在、LeakCanary は Service、Activity、Fragment、ViewModel、およびリーク検出の表示 次に、サービスの関連部分を見てみましょう。

まず最初に、Service オブジェクトがリサイクル可能であるかどうかを判断したい場合、前提条件は何でしょうか? もちろん、Service は onDestroyed メソッドを実行します。つまり、Service のメモリ リークを検出する必要があります。まず、Service のライフ サイクル監視を実装する必要があります。このようにして、Service が onDestroyed メソッドを実行するときに、メソッドを使用すると、サービス オブジェクトがマークされ、監視されていることを確認できます。では、サービスのライフサイクルを監視するにはどうすればよいでしょうか?

サービス起動処理

上図で説明されているサービスの起動プロセスにはプロセスの作成が含まれていますが、プロセスは非常に明確であり、説明はありませんので、興味のある学生はソースコードを読むことができます。

サービス破棄プロセス

上の図に示すように、Service コールバック onDestroyed が完了すると、Service が破棄されたことが AMS に通知されます。

サービスライフサイクルの監視

前回の記事で、Service の起動と破棄のプロセスを大まかに理解しましたが、Service の作成にせよ、Service の破棄にせよ、プロセス全体に非常に重要な Handler の役割が関与していることがわかります。 ApplicationThread と ActivityThread の間のブリッジとして機能し、Service が作成されると CREATE_SERVICE メッセージを受け入れて処理し、Service が破棄されると STOP_SERVICE メッセージを受け入れて処理し、Service onDestroy メソッドをコールバックします。

そう、これが mH オブジェクトです。このオブジェクトは、ActivityThread で定義されています。その内部のメッセージ処理を監視できれば、必然的に Setvice のライフサイクル監視機能を実現できます。

mH が受信した STOP_SERVEICE メッセージを聞いてください (サービスが破棄されようとしています)

mH Handler オブジェクトが受信した STOP_SERVICE メッセージを監視するにはどうすればよいですか? まず、Handler? 内でメッセージがどのように配布されるかを見てみましょう。

Handler 内の mCallback オブジェクトが空でない場合、メッセージはまず実行のために mCallback オブジェクトに配布され、関数が false を返した場合は配布が続行されることがわかります。これは、元のメッセージ配信ロジックに影響を与えることなく、mH Handler オブジェクトの mCallback メンバーを変更し、このメンバー オブジェクトを通じて Handler でのメッセージ配信の監視を完了できることも意味します。

mH Handler オブジェクトを反映し、mCallback メンバーを設定します

上記とリフレクション関連の知識を組み合わせると、次のリフレクションの実装と mH Handler オブジェクトの mCallback メンバーの設定を取得できます。コードは次のとおりです。

 try {
     Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
     Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
     currentActivityThreadMethod.setAccessible(true);
     // currentActivityThread是一个static函数所以可以直接invoke,不需要带实例参数
     Object currentActivityThread = currentActivityThreadMethod.invoke(null);
 ​
     // 获取mH Handler对象
     Field mH = activityThreadClass.getDeclaredField("mH");
     mH.setAccessible(true);
     Handler handler = (Handler) mH.get(currentActivityThread);
 ​
     // 获取Handler中Callback对象并赋值
     Field callBack = Handler.class.getDeclaredField("mCallback");
     callBack.setAccessible(true);
 ​
     callBack.set(handler,new Handler.Callback(){
 ​
         @Override
         public boolean handleMessage(Message msg) {
             Log.d(TAG, "handleMessage: msg " + msg);
 ​
             return false;
         }
     });
 } catch (ClassNotFoundException | NoSuchFieldException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
     e.printStackTrace();
 }

次のように TestService を作成し、その前後で startService と stopService を呼び出し、ログ出力を確認します。

msg.what = 116 でサービス破棄のメッセージをリッスンしたことがわかります。

mH Handler オブジェクトの STOP_SERVICE の値は 116 です。

ログから、STOP_SERVICE メッセージには破壊された Service 情報がないことがわかります。どの Service が破壊されたかをどのように照合すればよいでしょうか?

破壊されようとしているサービスを見つける

破棄されたサービスに関する情報は STOP_SERVICE メッセージには含まれていません。では、システムはどのようにしてサービスが破棄されたことを知るのでしょうか? ソース コードをチェックして次のことを確認してみましょう。

ActivityThread では、msg.obj が Service のインデックスとマーク付けに使用されていることがわかります。msg.obj は IBinder オブジェクトです。handleStopService では、Service s = mServices.remove(token);msg.obj に対応する Service オブジェクトを取得し、Service の onDestroy メソッドと deatchAndCleanUp メソッドを呼び出します。このコードでは、handleStopService によって参照される mServices は Map のようなデータ構造であると考えるのは難しくありません。その Remove メソッドが呼び出されると、データ構造内のデータがキーに従って削除されます。キー値に対応する値オブジェクトが返されます。mServices の宣言と初期化を見てみましょう。

ソース コードから、mServices はキーとして IBinder オブジェクト、値として Service を持つ ArrayMap であることがわかります。これは、mServices メンバーにアクセスできる限り、監視されているメッセージを通じて対応する Service オブジェクトを取得できることを意味します。 .obj.

mServices オブジェクトを取得するにはどうすればよいですか? 当然、これもリフレクションによるもので、関連するコードは次のとおりです。

 private void hookServicesInActivityThread(){
     try {
         Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
         Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
         currentActivityThreadMethod.setAccessible(true);
         // currentActivityThread是一个static函数所以可以直接invoke,不需要带实例参数
         Object currentActivityThread = currentActivityThreadMethod.invoke(null);
 ​
         Field mServices = activityThreadClass.getDeclaredField("mServices");
         mServices.setAccessible(true);
         mActivityThreadServices = (Map<IBinder, Service>) mServices.get(currentActivityThread);
 ​
     } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException |
              InvocationTargetException | NoSuchFieldException e) {
         e.printStackTrace();
     }
 }
 ​
 private Service findServiceFromActivityThreadServices(IBinder token) {
     if (mActivityThreadServices == null) {
         hookServicesInActivityThread();
     }
     return mActivityThreadServices.get(token);
 }

実行後のログ出力は次のとおりです。破棄されようとしている Service オブジェクトが実際に見つかったことがわかります。

サービス破棄プロセスのセクションのフローチャートと handleStopService コードを組み合わせると、メッセージを聞くと、サービスが実際には onDestroy メソッドを呼び出していないことがわかります。これは、mH の STOP_SERVICE メッセージは、サービスが停止していることを意味するだけであることを意味します。破壊されようとしていて、いつ破壊されますか?

そうです。フローチャートと handleStopService コードから、ActivityManager.getService().serviceDoneExecuting() メソッドが呼び出されたとき、それはサービスが破棄されたことを意味することがわかります。

サービスの破棄の完了を監視する

フローチャートと handleStopService コードから、サービスの破棄の完了を監視したい場合、つまり、serviceDoneExecuting メソッドの呼び出しを監視する必要がある場合、どのように行うのでしょうか?

リフレクション + プロキシ。ActivityManager.getService によって返されたオブジェクトをプロキシでラップし、リセットして戻します。

Android 8.0 以降では、IActivityManager は IActivityManagerSingleton から取得されるオブジェクトであり、コードは次のとおりです。

Android 8.0 より前では、IActivityManager は、ActivityManagerNative のメンバーである gDefault から取得されていました。コードは次のとおりです。

コードは以下のように表示されます。

 try {
     Object defaultSingleton = null;
     if (Build.VERSION.SDK_INT >= 26) {
         Class<?> activityManageClazz =
                 Class.forName("android.app.ActivityManager");
         Field field = activityManageClazz.getDeclaredField("IActivityManagerSingleton");
         field.setAccessible(true);
         //获取activityManager中的IActivityManagerSingleton字段
         defaultSingleton = field.get(null);
     } else {
         Class<?> activityManagerNativeClazz =
                 Class.forName("android.app.ActivityManagerNative");
         //获取ActivityManagerNative中的gDefault字段
         Field field = activityManagerNativeClazz.getDeclaredField("gDefault");
         field.setAccessible(true);
         defaultSingleton = field.get(null);
     }
     
     Class<?> singletonClazz = Class.forName("android.util.Singleton");
     Field mInstanceField = singletonClazz.getDeclaredField("mInstance");
     mInstanceField.setAccessible(true);
     
     //获取iActivityManager
     Object iActivityManager = mInstanceField.get(defaultSingleton);
     Class<?> iActivityManagerClazz =
             Class.forName("android.app.IActivityManager");
     
     Object proxy = Proxy.newProxyInstance(
             Thread.currentThread().getContextClassLoader(),
             new Class<?>[]{iActivityManagerClazz},
             new IActivityManagerProxy(iActivityManager));
     
     mInstanceField.set(defaultSingleton, proxy);
 } catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException e) {
     e.printStackTrace();
 }

 public class IActivityManagerProxy implements InvocationHandler {
     private Object mActivityManager;
 ​
     public IActivityManagerProxy(Object activityManager) {
         mActivityManager = activityManager;
     }
 ​
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         Log.d("ServiceWatcher", "proxy receive method:" + method.getName());
         return method.invoke(mActivityManager, args);
     }
 }

実行すると、serviceDoneExecuting メソッド呼び出しが IActivityManagerProxy で監視されており、ログが次のようになっていることがわかります。

要約すると、サービス破壊の監視が完了しました。

この記事ではサービス破棄プロセスの監視のみに焦点を当てていますが、記事内のコード構造に基づいて、独自のグローバルなサービス ライフ サイクル監視を実装することは難しくありません。これは、サービスのライフ サイクルの変化を監視するために使用されます。サービス中です。

Androidの勉強メモ

Android パフォーマンスの最適化: https://qr18.cn/FVlo89
Android Vehicle: https://qr18.cn/F05ZCM
Android リバース セキュリティ調査ノート: https://qr18.cn/CQ5TcL
Android フレームワークの原則: https://qr18.cn/AQpN4J
Android オーディオとビデオ: https://qr18.cn/Ei3VPD
Jetpack (Compose を含む): https://qr18.cn/A0gajp
Kotlin: https://qr18.cn/CdjtAF
Gradle: https://qr18.cn/DzrmMB
OkHttp ソース コード分析ノート: https://qr18.cn/Cw0pBD
Flutter: https://qr18.cn/DIvKma
Android Eight Knowledge Body: https://qr18.cn/CyxarU
Android Core Notes: https://qr21.cn/CaZQLo
Android過去の面接の質問: https://qr18.cn/CKV8OZ
2023 年最新の Android 面接の質問集: https://qr18.cn/CgxrRy
Android 車両開発の就職面接の演習:https://qr18.cn/FTlyCJ
音声とビデオの面接の質問:https://qr18.cn/AcV6Ap

おすすめ

転載: blog.csdn.net/weixin_61845324/article/details/132186813