Android性能指標(biāo)與監(jiān)控

Android性能指標(biāo)大致分為以下幾點(diǎn)
FPS、CPU使用率、內(nèi)存占用、頁(yè)面加載時(shí)間、網(wǎng)絡(luò)請(qǐng)求

內(nèi)存使用

在加載圖片、視頻、聲音等這些比較耗費(fèi)內(nèi)存的資源的時(shí)候去獲取一次應(yīng)用內(nèi)存占用及系統(tǒng)剩余內(nèi)存保存到日志中。

Android提供了API可以獲取系統(tǒng)總內(nèi)存大小及當(dāng)前可用內(nèi)存大小,以及獲取應(yīng)用中內(nèi)存的使用情況。

    Runtime runtime = Runtime.getRuntime();
    long totalSize = runtime.maxMemory() >> 10;
    this.memoryUsage = (runtime.totalMemory() - runtime.freeMemory()) >> 10;
    this.memoryUsageRate = this.memoryUsage * 100 / totalSize;

CPU使用

可以每隔一段時(shí)間去獲取APP進(jìn)程CPU時(shí)間,也可以針對(duì)一些場(chǎng)景手動(dòng)獲取。可以設(shè)定一個(gè)閾值,比如大于50%的時(shí)候,將各個(gè)線程名及線程占用的CPU時(shí)間一起保存到日志中。

系統(tǒng)沒(méi)有提供API接口,可以直接在應(yīng)用中讀取/proc/stat,/proc/pid/stat,/proc/pid/task/tid/stat這幾個(gè)文件來(lái)獲取,不需要root權(quán)限

int mPid;
long mLastCpuTime;//當(dāng)前手機(jī)的CPU總時(shí)間
long mLastAppTime;//當(dāng)前app的CPU耗時(shí)
RandomAccessFile procStatFile;
static final int RATE_LIMIT = 50;//CPU占比閾值

if (mPid == 0) {
            mPid = android.os.Process.myPid();
        }

        try {
            if (procStatFile == null || appStatFile == null) {
                procStatFile = new RandomAccessFile("/proc/stat", "r");
                appStatFile = new RandomAccessFile("/proc/" + mPid + "/stat", "r");
            }

            //文件開(kāi)頭
            procStatFile.seek(0);
            appStatFile.seek(0);

            String proStatSrc = procStatFile.readLine();
            String appStatSrc = appStatFile.readLine();

            String[] proStats = null;
            String[] appStats = null;
            if (proStatSrc != null || appStatSrc != null) {
                proStats = proStatSrc.trim().split(" ");
                appStats = appStatSrc.trim().split(" ");
            }

            long cpuTime = 0L;
            long appTime = 0L;
            if (proStats != null && proStats.length >= 9) {
                for (int i = 2; i <= 8; i++) {
                    cpuTime += Long.valueOf(proStats[i]);
                }
            }

            if (appStats != null && appStats.length >= 15) {
                appTime = Long.valueOf(appStats[13]) + Long.valueOf(appStats[14]);
            }

            if (mLastCpuTime == 0 || mLastAppTime == 0) {
                mLastCpuTime = cpuTime;
                mLastAppTime = appTime;
                return;
            }

            double cpuRate = (double) (appTime - mLastAppTime) / (double) (cpuTime - mLastCpuTime);
            mLastCpuTime = cpuTime;
            mLastAppTime = appTime;

            //CPU使用率
            int cpuRateInt = (int) (cpuRate * 100);
            //TODO
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Log.d(TAG, "init randomfile failed");
        } catch (IOException e) {
            e.printStackTrace();
            Log.d(TAG, "read file failed");
        }
    }

FPS(卡頓)

Android系統(tǒng)從4.1(API 16)開(kāi)始加入Choreographer這個(gè)類來(lái)控制同步處理輸入(Input)、動(dòng)畫(huà)(Animation)、繪制(Draw)三個(gè)操作。其實(shí)UI顯示的時(shí)候每一幀要完成的事情只有這三種。Choreographer接收顯示系統(tǒng)的時(shí)間脈沖(垂直同步信號(hào)-VSync信號(hào)),在下一個(gè)幀渲染時(shí)控制執(zhí)行這些操作。收到VSync信號(hào)后,順序執(zhí)行3個(gè)操作,然后等待下一個(gè)信號(hào),再次順序執(zhí)行3個(gè)操作。假設(shè)在第二個(gè)信號(hào)到來(lái)之前,所有的操作都執(zhí)行完成了,即Draw操作完成了,那么第二個(gè)信號(hào)來(lái)到時(shí),此時(shí)界面將會(huì)更新為第一幀的內(nèi)容,因?yàn)镈raw操作已經(jīng)完成了。否則界面將不會(huì)更新,還是現(xiàn)實(shí)上一個(gè)幀的內(nèi)容,表示丟幀了。丟幀是造成卡頓的原因。

通過(guò) Choreographer 類設(shè)置它的 FrameCallback,可以在每一幀被渲染的時(shí)候記錄下它開(kāi)始渲染的時(shí)間,每一次同步的周期為16ms,代表一幀的刷新頻率,一次界面渲染會(huì)回調(diào) FrameCallback的doFrame(longframeTimeNanos)方法,如果兩次 doFrame 之間的間隔大于16ms說(shuō)明丟幀了。用這種方法,可以實(shí)時(shí)監(jiān)控應(yīng)用的丟幀情況。

static final int FPS_LIMIT = 30;//最低幀率,低于這個(gè)幀代表頁(yè)面不流暢
static final int INTERVAL = 300;//Frame采樣時(shí)間

@Override
    public void doFrame(long frameTimeNanos) {
        long currentTimeMillis = TimeUnit.NANOSECONDS.toMillis(frameTimeNanos);

        if (mLastFrameStartTime > 0) {
            //兩幀的時(shí)間間隔
            final long timeSpace = currentTimeMillis - mLastFrameStartTime;
            mFrameRendered++;

            if (timeSpace >= INTERVAL) {
                //計(jì)算幀率
                mFPS = (int) (mFrameRendered * 1000 / timeSpace);
                mLastFrameStartTime = currentTimeMillis;
                mFrameRendered = 0;
            }
        } else {
            mLastFrameStartTime = currentTimeMillis;
        }
        mChoreographer.postFrameCallback(this);
    }

使用:
if (mFPS < FPS_LIMIT) {
           ...
} else {
           ...
}

頁(yè)面加載時(shí)間

如何判定UI渲染完成?
在Activity的rootView中插入一個(gè)FrameLayout,并且監(jiān)聽(tīng)這個(gè)FrameLayout是否調(diào)用了dispatchDraw方法實(shí)現(xiàn)的

class AutoSpeedFrameLayout extends FrameLayout {
    public static View wrap(int pageObjectKey, @NonNull View child) {
        ...
        //將頁(yè)面根View作為子View,其他參數(shù)保持不變
        ViewGroup vg = new AutoSpeedFrameLayout(child.getContext(), pageObjectKey);
        if (child.getLayoutParams() != null) {
            vg.setLayoutParams(child.getLayoutParams());
        }
        vg.addView(child, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        return vg;
    }
    private final int pageObjectKey;//關(guān)聯(lián)的頁(yè)面key
    private AutoSpeedFrameLayout(@NonNull Context context, int pageObjectKey) {
        super(context);
        this.pageObjectKey = pageObjectKey;
    }
    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        AutoSpeed.getInstance().onPageDrawEnd(pageObjectKey);
    }
}

自定義了一層 FrameLayout 作為所有頁(yè)面根View的父View,其 dispatchDraw() 方法執(zhí)行super后,記錄相關(guān)頁(yè)面繪制結(jié)束的時(shí)間點(diǎn)。

網(wǎng)絡(luò)請(qǐng)求

一,流量

具體方案:后臺(tái)定時(shí)任務(wù)->獲取間隔內(nèi)流量->記錄前后臺(tái)->分別計(jì)算->上報(bào) 后臺(tái)->流量治理依據(jù)
實(shí)現(xiàn)方法:
1.TrafficStats
2.可使用okhttp的interceptor中監(jiān)控每個(gè)api的流量,但無(wú)法獲知其他如webview流量
3.stetho

二,成功率

返回狀態(tài)碼

三,速度

計(jì)算每次request和response之間時(shí)間間隔

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容