/ The company Operating state Open source state
Matrix tencent
Booster drops
xCrash iQIYI
ArgusAPM 360
Emmagee netease
U-Meng Ali.
Bugly tencent
Tingyun Listen to the cloud
  • Open source address
  1. Matrix:github.com/Tencent/mat…
  2. Booster:github.com/didi/booste…
  3. XCrash:github.com/iqiyi/xCras…
  4. ArgusAPM:github.com/Qihoo360/Ar…
  5. Emmagee:github.com/NetEase/Emm…
  • Service page
  1. U-Meng:www.umeng.com/
  2. Bugly:bugly.qq.com/
  3. Tingyun:www.tingyun.com/

The core index

  • Crash report

  • Consumptive index:

    • traffic
    • electricity
    • CPU
  • The startup time

    • Application startup time
    • Page startup time
  • Application state

    • FPS/card/ANR
    • Memory leak/memory status detection

Crash report

Java Crash

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
        // Record}});Copy the code

Native Crash

The collapse of the Android platform Native code capture mechanism and implementation: cloud.tencent.com/developer/a…

  • Core principles

  • The core function
#include <signal.h> 
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact));
Copy the code
  • Core tools
  1. Dladdr () gets the start address of the shared library and subtracts the PC value at the crash point to get the relative address at the crash point
  2. Addr2line gets the function call stack by relative address

The startup time

Application startup

  • sStartUpTimeStamp
// ContentProvider >>> Application
public class LauncherHelpProvider extends ContentProvider {
    public static long sStartUpTimeStamp = SystemClock.uptimeMillis();
}
Copy the code
  • coldLauncherTime
public class MainApplication extends Application implements Application.ActivityLifecycleCallbacks {
    @Override
    public void onCreate(a) {
        registerActivityLifecycleCallbacks(this);
    }

    @Override
    public void onActivityResumed(@NonNull final Activity activity) {
        onActivityVisible(activity, () -> {
            final long coldLauncherTime = SystemClock.uptimeMillis() - LauncherHelpProvider.sStartUpTimeStamp;
        });
    }
    
    private void onActivityVisible(Activity activity, Runnable runnable) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
            activity.runOnUiThread(runnable);
        } else{ activity.getWindow().getDecorView().getViewTreeObserver().addOnWindowFocusChangeListener(hasFocus -> runnable.run()); }}}Copy the code

Page to start the

  • OnPause for the previous Activity and onResume for the next Activity
@Override
public void onActivityPaused(@NonNull Activity activity) {
    mActivityLauncherTimeStamp = SystemClock.uptimeMillis();
}

@Override
public void onActivityResumed(@NonNull final Activity activity) {
    onActivityVisible(activity, () -> {
        final long actvityLauncherTime = SystemClock.uptimeMillis() - mActivityLauncherTimeStamp;
    });
}
Copy the code

Page state

FPS

  • Instantaneous drop FPS >>> Average FPS

  • Matrix standard: github.com/Tencent/mat…

The number of frames dropped is less than 3, and so on

Start time

@Override
public void onActivityResumed(@NonNull final Activity activity) { activity.getWindow().getDecorView().getViewTreeObserver().addOnWindowFocusChangeListener(hasFocus -> { resumeTrack(); }}@Override
public void onActivityPaused(@NonNull Activity activity) {
    pauseTrack();
}
Copy the code

Detection method

  • ArgusAPM: Monitors Choreographer VSYNC signal time differences

Monitoring is too frequent and takes up CPU when no page refresh is required

Choreographer.getInstance().postFrameCallback(frameTimeNanos -> {
    mFpsCount++;
    mFrameTimeNanos = frameTimeNanos;
    Choreographer.getInstance().postFrameCallback(this);
});
Copy the code
  • BlockCanary: Monitors UI thread Message time

Github.com/markzhai/An…

/ / which the source code
public static void loop(a) {
    // ...
    final Printer logging = me.mLogging;
    if(logging ! =null) {
        logging.println(">>>>> Dispatching to " + msg.target + "" +
                msg.callback + ":" + msg.what);
    }
    // ...
    if(logging ! =null) {
        logging.println("<<<<< Finished to " + msg.target + ""+ msg.callback); }}Copy the code
// Custom Printer
class LooperPrinter implements Printer {
    @Override
    public void println(String x) {
        // ">>>>>" indicates the start and "<<<<<" indicates the end
        dispatch(x.charAt(0) = ='>', x); }}Copy the code
  • Matrix: reflect Choreographer FrameCallback, distinguish between doFrame
  1. UIThreadMonitor initialization
private static final String ADD_CALLBACK = "addCallbackLocked";

public void init(TraceConfig config) {
    callbackQueues = ReflectUtils.reflectObject(choreographer, "mCallbackQueues".null);
    if (null! = callbackQueues) { addInputQueue = ReflectUtils.reflectMethod(callbackQueues[CALLBACK_INPUT], ADD_CALLBACK,long.class, Object.class, Object.class);
        addAnimationQueue = ReflectUtils.reflectMethod(callbackQueues[CALLBACK_ANIMATION], ADD_CALLBACK, long.class, Object.class, Object.class);
        addTraversalQueue = ReflectUtils.reflectMethod(callbackQueues[CALLBACK_TRAVERSAL], ADD_CALLBACK, long.class, Object.class, Object.class); }}Copy the code
  1. UIThreadMonitor. AddFrameCallback reflection calls Choreographer addCallbackLocked, rather than postCallback
private synchronized void addFrameCallback(int type, Runnable callback, boolean isAddHeader) {
    // ...
    synchronized (callbackQueueLock) {
        Method method = null;
        switch (type) {
            case CALLBACK_INPUT:
                method = addInputQueue;
                break;
            case CALLBACK_ANIMATION:
                method = addAnimationQueue;
                break;
            case CALLBACK_TRAVERSAL:
                method = addTraversalQueue;
                break;
        }
        if (null! = method) { method.invoke(callbackQueues[type], ! isAddHeader ? SystemClock.uptimeMillis() : -1, callback, null);
            callbackExist[type] = true; }}// ...
}
Copy the code
  1. UIThreadMonitor watches Looper distribution and monitors CALLBACK_INPUT queue messages

doFrameBegin -> isVsyncFrame = true -> doFrameEnd

Github.com/Tencent/mat…

sequenceDiagram
  UIThreadMonitor ->> Choreographer: addFrameCallback(CALLBACK_INPUT, this)
  par run
    Choreographer ->> UIThreadMonitor: run
    UIThreadMonitor ->> UIThreadMonitor: doFrameBegin
  end
  LooperMonitor ->> UIThreadMonitor: dispatchBegin
  alt isVsyncFrame
    LooperMonitor ->> UIThreadMonitor: dispatchEnd
    UIThreadMonitor ->> UIThreadMonitor: doFrameEnd
  end

Memory monitoring

  • Memory leak, LeakCanary: github.com/square/leak…

Github.com/square/leak…

@Override
public void onActivityDestroyed(@NonNull final Activity activity) {
    mActivityStringWeakHashMap.put(activity, activity.getClass().getSimpleName());
}

@Override
public void onActivityStopped(@NonNull final Activity activity) {
    Runtime.getRuntime().gc();
    mHandler.postDelayed(() -> {
        Runtime.getRuntime().gc();
        SystemClock.sleep(100);
        System.runFinalization();
      
        HashMap<String, Integer> countMap = new HashMap<>();
        for(Map.Entry entry : mActivityStringWeakHashMap.entrySet()) { String name = entry.getKey().getClass().getName(); Integer count = countMap.get(name); countMap.put(name, count ! =null ? count + 1 : 1);
        }
        // notify leak
    }, 10 * 1000);
}
Copy the code
  • Memory state
  1. TotalPss (Total memory, Native + Dalvik + Shared)
  2. Native PSS (Native memory)
  3. DalvikPss (Java memory OOM reason)
Debug.MemoryInfo debugMemoryInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(debugMemoryInfo);
appMemory.nativePss = debugMemoryInfo.nativePss >> 10;
appMemory.dalvikPss = debugMemoryInfo.dalvikPss >> 10;
appMemory.totalPss = debugMemoryInfo.getTotalPss() >> 10;
Copy the code

Traffic/power statistics

  • Traffic: Unified network traffic
@Override
public void onActivityStarted(@NonNull Activity activity) {
    mStartBytes = TrafficStats.getUidRxBytes(Process.myUid());
}

@Override
public void onActivityPaused(@NonNull Activity activity) {
    mCurrentBytes = TrafficStats.getUidRxBytes(Process.myUid());
}

@Override
public void onActivityDestroyed(@NonNull Activity activity) {
    mTotalCount = TrafficStats.getUidRxBytes(Process.myUid()) - mStartBytes;
}
Copy the code
  • Electric quantity: percentage electric quantity
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
android.content.Intent batteryStatus = application.registerReceiver(null, filter);
int status = batteryStatus.getIntExtra("status".0);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
        status == BatteryManager.BATTERY_STATUS_FULL;
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
Copy the code

No intrusion

  • Dexposed
  • AspectD

reference

  • www.jianshu.com/p/978b7bce6…
  • Cloud.tencent.com/developer/a…