Androidカートン自動化検出実現
5842 ワード
最近、Androidの文章を読んだとき、Androidカートンを検出するオンライン実装案を見て、自分で簡単に実現しました.
その原理はAndroidのメッセージ処理メカニズムに由来し、1つのスレッドはいくらHandlerがあっても、1つのLooperしか存在せず、メインスレッドが実行する任意のコードはLooperを通過する.loop()メソッドが実行されます.Looper関数では、各message処理の前後で呼び出されるmLoggingオブジェクトがあります.メインスレッドにカートンが発生しました.それはdispatchMessage()メソッドで時間のかかる操作を実行したに違いありません.では、このmLoggingオブジェクトを介してdispatchMessage()を監視することができます.
Looperのメッセージループメソッドloopの実装:
Looperのloop()メソッドでは、各メッセージを実行する前後にloggingによって印刷出力が行われる.メッセージを実行する前に出力される「>>>>>>Dispatching to」であり、メッセージを実行した後に出力される「<<<したがって、具体的な実装手順は、次のとおりです.1、まずLooperを使う必要があります.getMainLooper().setMessageLogging()は、独自のPrinter実装クラスを設定して出力loggingを印刷します.これにより、各messageが実行される前と後に、私たちが設定したこのPrinterインプリメンテーションクラスが呼び出されます. 2、もし私たちが">>>>>Dispatching to"に一致したら、私たちは1行のコードを実行することができます:つまり指定した時間のしきい値の後で、私たちはサブスレッドで1つのタスクを実行して、このタスクは現在のメインスレッドのスタック情報と現在のシーン情報を取得して、例えば:メモリサイズ、携帯電話、ネットワークの状態など. 3、指定したしきい値内に「<<<<
上記の実装スキームにより、初歩的なコードを実装します.
メインスレッドLooperのmLoggingを設定するには:
独自に適用したApplication onCreate()メソッドでカスタムmLoggingを設定できます.
では、システムでLooperのmLoggingはどのように設定されているのでしょうか.Looperを追跡するsetMessageLogging()メソッドは、Activity Threadのmainメソッドで呼び出されたものであり、システムがこの設定を閉じただけであることがわかります.
上記のコードは、検証によってカートンタスクのスタック情報を取得することができるが、printlnではタスクごとにnew Threadが必要であり、スレッドの頻繁な作成と破棄はパフォーマンスの問題を引き起こすため、スレッドプールを作成することによってスレッドを多重化することができる.ここではHandlerThreadの使用を考慮することもできる.
システムのソースコードはActivity ThreadでmLoggingの設定がオフになっていることがわかります.log情報の出力もcpu性能を消費するので、実際に使用するときはカートン情報だけを出力することができます.
このシナリオをオンラインに展開するには,ユーザのサンプリング,スタック情報の重さ除去,ファイル圧縮など,収集情報のアップロードも考慮し,適切なタイミングでアップロードするなどする必要がある.
参照先:https://juejin.im/post/5e41fb7de51d4526c80e9108
その原理はAndroidのメッセージ処理メカニズムに由来し、1つのスレッドはいくらHandlerがあっても、1つのLooperしか存在せず、メインスレッドが実行する任意のコードはLooperを通過する.loop()メソッドが実行されます.Looper関数では、各message処理の前後で呼び出されるmLoggingオブジェクトがあります.メインスレッドにカートンが発生しました.それはdispatchMessage()メソッドで時間のかかる操作を実行したに違いありません.では、このmLoggingオブジェクトを介してdispatchMessage()を監視することができます.
Looperのメッセージループメソッドloopの実装:
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
...
try {
msg.target.dispatchMessage(msg);
} catch (Exception exception) {
} finally {
}
...
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
}
}
Looperのloop()メソッドでは、各メッセージを実行する前後にloggingによって印刷出力が行われる.メッセージを実行する前に出力される「>>>>>>Dispatching to」であり、メッセージを実行した後に出力される「<<<
上記の実装スキームにより、初歩的なコードを実装します.
import android.os.Looper;
import android.util.Log;
import android.util.LogPrinter;
public class LogMonitorPrinter extends LogPrinter {
private static final String TAG = "LogMonitorPrinter";
private long beginTime;
private int timeoutInterval = 1000;
private MonitorThread monitorThread;
/**
* Create a new Printer that sends to the log with the given priority
* and tag.
*
* @param priority The desired log priority:
* {@link Log#VERBOSE Log.VERBOSE},
* {@link Log#DEBUG Log.DEBUG},
* {@link Log#INFO Log.INFO},
* {@link Log#WARN Log.WARN}, or
* {@link Log#ERROR Log.ERROR}.
* @param tag A string tag to associate with each printed log statement.
*/
public LogMonitorPrinter(int priority, String tag) {
super(priority, tag);
}
public void setTimeoutInterval(int interval) {
this.timeoutInterval = interval;
}
@Override
public void println(String x) {
if (x.startsWith(">>>>> Dispatching to")) {
beginTime = System.currentTimeMillis();
monitorThread = new MonitorThread(timeoutInterval);
monitorThread.start();
}
if (x.startsWith("<<<<< Finished to")) {
long taskTime = System.currentTimeMillis() - beginTime;
monitorThread.interrupt();
if (taskTime > timeoutInterval) {
Log.w(TAG, "taskTime: " + taskTime);
}
}
super.println(x);
}
private static String getMainStackTrace() {
StackTraceElement[] stackTraceElements = Looper.getMainLooper().getThread().getStackTrace();
StringBuilder sb = new StringBuilder();
for (StackTraceElement element : stackTraceElements) {
sb.append(element.toString());
sb.append("
");
}
return sb.toString();
}
static class MonitorThread extends Thread {
private long interval;
public MonitorThread(int interval) {
this.interval = interval;
}
@Override
public void run() {
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
return;
}
Log.w(TAG, getMainStackTrace());
}
}
}
メインスレッドLooperのmLoggingを設定するには:
Looper.getMainLooper().setMessageLogging(new LogMonitorPrinter(Log.DEBUG, "Monitor"));
独自に適用したApplication onCreate()メソッドでカスタムmLoggingを設定できます.
では、システムでLooperのmLoggingはどのように設定されているのでしょうか.Looperを追跡するsetMessageLogging()メソッドは、Activity Threadのmainメソッドで呼び出されたものであり、システムがこの設定を閉じただけであることがわかります.
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
上記のコードは、検証によってカートンタスクのスタック情報を取得することができるが、printlnではタスクごとにnew Threadが必要であり、スレッドの頻繁な作成と破棄はパフォーマンスの問題を引き起こすため、スレッドプールを作成することによってスレッドを多重化することができる.ここではHandlerThreadの使用を考慮することもできる.
システムのソースコードはActivity ThreadでmLoggingの設定がオフになっていることがわかります.log情報の出力もcpu性能を消費するので、実際に使用するときはカートン情報だけを出力することができます.
このシナリオをオンラインに展開するには,ユーザのサンプリング,スタック情報の重さ除去,ファイル圧縮など,収集情報のアップロードも考慮し,適切なタイミングでアップロードするなどする必要がある.
参照先:https://juejin.im/post/5e41fb7de51d4526c80e9108