wordpress 联系我们 设置,关键词优化排名推广搜ノ牛霸天排名软件,网络推广这个工作好做吗,网站开发架构图【性能优化】安卓性能优化之CPU优化 CPU优化及常用工具原理与文章参考常用ADB常用原理、监控手段原理监控手段多线程并发解决耗时UI相关 常见场景排查CPU占用过高常用系统/开源分析工具AndroidStudio ProfilerSystraceBtracePerfettoTraceView和 Profile ANR相关ANR原理及常见场… 【性能优化】安卓性能优化之CPU优化 CPU优化及常用工具原理与文章参考常用ADB常用原理、监控手段原理监控手段多线程并发解决耗时UI相关 常见场景排查CPU占用过高常用系统/开源分析工具AndroidStudio ProfilerSystraceBtracePerfettoTraceView和 Profile ANR相关ANR原理及常见场景ANR/卡顿检测卡顿检测 CPU 优化案例 CPU优化及常用工具
原理与文章参考
编舞者、looper、JankStats方法
常用ADB
含义命令备注查看CPU状态adb shell top -H -d 1 -p pid -O cpu-O cpu 查看对应在那个核心 修改采样间隔为1s导出当前进程所有线程状态到tombstonedadb shell run-as kill -3 实际上是以一个异常状态导出了利用了墓碑机制查看进程的所有线程adb shell ps -Tgrep pid查看进程占用cpu情况adb shell dumpsys cpuinfogrep [进程名]查看进程内线程占用cpu的情况adb shell top -n 1 -d 0.5grep proc_ id获取设备cpu信息adb shell cat /proc/cpuinfo或者查看 /sys/devices/system/cpu 目录下的文件夹
常用原理、监控手段
原理
普通手机默认60帧刷新率相当于每帧16.6ms利用系统预留接口 对每个帧率/handler消息等 进行统计
监控手段
设置looperPrinter字节码插桩检测慢函数martix dokit编舞者获取frame帧率jetpack JankStats获取丢帧信息
多线程并发解决耗时 线程池/数量参考 CPU密集线程数设置为CPU核心数 1IO密集线程数设置为CPU核心数 * 2 UI相关
利用font stylecolor:rgb(77, 77, 77);IdelHandler/font对一些常用view进行预绘制通过排查布局减少过度绘制
常见场景
过度绘制频繁IO主线程耗时任务
排查CPU占用过高 规范线程命名定位线程抓取top数据查看具体哪个线程占用高cpu指标含义解释线程各参数详解 常用系统/开源分析工具
AndroidStudio Profiler
抓取CPU火焰图卡顿/ANR 主要监测主线程是否会出现耗时操作
Systrace
官方指令参考官方推荐指令 $ python systrace.py -o mynewtrace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res要求环境 python2.7 安装 python six 模块命令 : pip install six “No module named win32con” 问题安装相关 pip install pypiwin32 拉取到信息后用perfetto 打开即可 但是这个主要是针对系统的 对应用开发帮助不大分析自己应用可以用btrace
Btrace
官方链接
Perfetto
官方-快速开始工具界面入门使用线程状态 TraceView和 Profile
traceview官方参考traceview使用导出的日志分析使用DDMS查看 新版路径Sdk\tools\monitor.bat Incl Cpu Time方法在CPU中执行所有时间包含其调用的方法所消耗的时间Excl Cpu Time: 方法在CPU中执行的时间不包含其调用的方法所消耗的时间Incl Real Time方法运行消耗的所有时间包含子方法Excl Real Time方法运行消耗的时间不包含子方法Calls Recur Calls/Total 方法调用、递归次数重要指标防止死循环Cpu Time/Call 该方法平均占用 CPU 的时间重要指标可以看出单个方法占用CPU的平均时间但是要防止在个别调用处出现长时间占用然后被平均了Real Time/Call 平均执行时间包括切换、阻塞的时间重要指标可以看出单个方法执行的平均时间值但是要防止在个别调用处出现长时间调用然后被平均了 TraceView优势 可以精确埋点 Debug.startMethodTracing(sample);...
Debug.stopMethodTracing();ANR相关
ANR原理及常见场景
原理 ANRApplication Not Responding的监测原理本质上是消息机制设定一个delay消息超时未被移除则触发ANR。具体逻辑处理都在system server端包括发送超时消息移除超时消息处理超时消息以及ANR弹框展示等对于app而言触发ANR的条件是主线程阻塞。 常见场景
Service ANR前台20s后台200sstartForeground超时10sBroadcast ANR前台10s后台60sInput ANR按键或触摸事件在5s内无响应ContentProvider ANR10s少见
ANR/卡顿检测
通过设置Looper的printer可以检测耗时WatchDog机制子线程发送消息自增休眠后检查参考ANR日志导出
// 安卓21以下有权限可以获取到 anr 日志
private FileObserver fileObserver null;
void initialize(....){// 实例化FileObserver ,监控路径/data/anr/监听文件被写入fileObserver new FileObserver(/data/anr/, CLOSE_WRITE) {public void onEvent(int event, String path) {try {if (path ! null) {String filepath /data/anr/ path;// 写入的文件是否有关键字 “trace”if (filepath.contains(trace)) {// 处理anr异常handleAnr(filepath);}}} catch (Exception e) {XCrash.getLogger().e(Util.TAG, AnrHandler fileObserver onEvent failed, e);}}};try {// 启动FileObserver 监控fileObserver.startWatching();} catch (Exception e) {fileObserver null;XCrash.getLogger().e(Util.TAG, AnrHandler fileObserver startWatching failed, e);}
}private void handleAnr(String filepath) {...// 读取anr文件 /data/anr/trace*.txt。返回文件内容String trace getTrace(filepath, anrTime.getTime());//删除其他的anr异常日志文件if (!FileManager.getInstance().maintainAnr()) {return;}//获取 tombstone 的文件头String emergency null;try {emergency getEmergency(anrTime, trace);} catch (Exception e) {XCrash.getLogger().e(Util.TAG, AnrHandler getEmergency failed, e);}// 创建anr异常日志保存文件File logFile null;try {String logPath String.format(Locale.US, %s/%s_%020d_%s__%s%s, logDir, Util.logPrefix, anrTime.getTime() * 1000, appVersion, processName, Util.anrLogSuffix);logFile FileManager.getInstance().createLogFile(logPath);} catch (Exception e) {XCrash.getLogger().e(Util.TAG, AnrHandler createLogFile failed, e);}if (logFile ! null){// 根据配置将日志文件头traceslogcat日志保存在文件中。}
}// 高版本通过AMS获取日志
public class ANRMoniter implements Runnable {private final String TAG ANRMoniter;private HandlerThread handlerThread new HandlerThread(WatchMainHandler);private ILog logImpl;private Application app;private Handler watchHandler;private Handler mainHandler;private ScheduleCheckTask scheduleCheckTask;private int CHECK_INTERVAL 5_000;public ANRMoniter(Application app, ILog logImpl) {this.app app;this.logImpl logImpl;init();}private void init() {handlerThread.start();Looper looper handlerThread.getLooper();watchHandler new Handler(looper);mainHandler new Handler(Looper.getMainLooper());scheduleCheckTask new ScheduleCheckTask();}public void start() {watchHandler.post(this);}Overridepublic void run() {mainHandler.post(scheduleCheckTask);long endTime System.currentTimeMillis() CHECK_INTERVAL;long sleepTime endTime - System.currentTimeMillis();while (sleepTime 0) {try {Thread.sleep(sleepTime);} catch (InterruptedException e) {e.printStackTrace();}sleepTime endTime - System.currentTimeMillis();}if (scheduleCheckTask.isBlocking()) {logImpl.Loge(TAG,main handler blocking);checkRealANR(mainHandler.getLooper().getThread().getStackTrace());}scheduleCheckTask.reset();watchHandler.post(this);}private void checkRealANR(StackTraceElement[] stack) {ThreadPool.getInstance().execute(new Runnable() {Overridepublic void run() {ActivityManager.ProcessErrorStateInfo processErrorStateInfo getANRInfo(app);if (processErrorStateInfo ! null) {logImpl.Loge(TAG,ANR action);//real ANRRuntimeException e new RuntimeException(processErrorStateInfo.shortMsg);e.setStackTrace(stack);e.printStackTrace();logImpl.Loge(TAG,e.getMessage());}}});}private ActivityManager.ProcessErrorStateInfo getANRInfo(Application app) {try {final long sleepTime 500L;final long loop 20;long times 0;do {ActivityManager activityManager (ActivityManager) app.getSystemService(Context.ACTIVITY_SERVICE);ListActivityManager.ProcessErrorStateInfo processesInErrorState activityManager.getProcessesInErrorState();if (processesInErrorState ! null) {for (ActivityManager.ProcessErrorStateInfo proc : processesInErrorState) {if (proc.condition ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING) {return proc;}}}Thread.sleep(sleepTime);} while (times loop);} catch (Exception e) {e.printStackTrace();}return null;}private class ScheduleCheckTask implements Runnable {private boolean isBlocking;ScheduleCheckTask() {isBlocking true;}Overridepublic void run() {isBlocking false;}public boolean isBlocking() {return isBlocking;}public void reset() {isBlocking true;}}
}
自定义线程WatchDog参考
package com.aispeech.util;import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;import com.aispeech.common.ThreadNameUtil;
import com.aispeech.lite.BaseKernel;import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;/**
* Description: 检测Kernel层 是否阻塞的工具类
* Author: junlong.huang
* CreateTime: 2023/8/21
*/
public class KernelWatchDog {private static final String TAG KernelWatchDog;HandlerThread innerThread;Handler innerHandler;long timeoutMillis 2000;static final int MSG_INCREMENT 0x01;private static volatile KernelWatchDog mInstance;private ConcurrentHashMapBaseKernel, AtomicInteger monitorMap;private VectorBaseKernel removeList;public static KernelWatchDog getInstance() {if (mInstance null) {synchronized (KernelWatchDog.class) {if (mInstance null) {mInstance new KernelWatchDog();}}}return mInstance;}private KernelWatchDog() {init();}private void init() {monitorMap new ConcurrentHashMap();removeList new Vector();innerThread new HandlerThread(ThreadNameUtil.getSimpleThreadName(watchdog-k));innerThread.start();innerHandler new InnerHandler(innerThread.getLooper());innerHandler.sendMessage(innerHandler.obtainMessage(MSG_INCREMENT));}public void addChecker(BaseKernel baseKernel) {Log.i(TAG, addChecker: baseKernel.getInnerThreadName());monitorMap.put(baseKernel, new AtomicInteger(baseKernel.getTick()));}public void removeChecker(BaseKernel baseKernel) {if (monitorMap.containsKey(baseKernel)) {Log.i(TAG, removeChecker: baseKernel.getInnerThreadName());monitorMap.remove(baseKernel);}}class InnerHandler extends Handler {public InnerHandler(Looper looper) {super(looper);}Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (innerHandler null) return;switch (msg.what) {case MSG_INCREMENT:if (monitorMap null || monitorMap.size() 0) {innerHandler.sendMessageDelayed(innerHandler.obtainMessage(MSG_INCREMENT), timeoutMillis);break;}for (BaseKernel baseKernel : monitorMap.keySet()) {if (baseKernel.getInnerThread() ! null !baseKernel.getInnerThread().isAlive()) {Log.i(TAG, Detected thread quit,Add to list to be removed);removeList.add(baseKernel);continue;}AtomicInteger lastTick monitorMap.get(baseKernel);if (lastTick null) lastTick new AtomicInteger(baseKernel.getTick());if (lastTick.get() ! baseKernel.getTick()) {Log.w(TAG, Detected target thread may blocked,export thread stack);Thread innerThread baseKernel.getInnerThread();if (innerThread ! null) {Log.w(TAG, getThreadStack(innerThread.getStackTrace()));}}lastTick.incrementAndGet();baseKernel.tick();}for (BaseKernel baseKernel : removeList) {monitorMap.remove(baseKernel);}removeList.clear();innerHandler.sendMessageDelayed(innerHandler.obtainMessage(MSG_INCREMENT), timeoutMillis);break;}}}public void release() {innerHandler.removeMessages(MSG_INCREMENT);innerThread.quit();monitorMap.clear();removeList.clear();}private String getThreadStack(StackTraceElement[] elements) {StringBuilder stackTraceString new StringBuilder();for (StackTraceElement element : elements) {stackTraceString.append(element.toString()).append(\n);}return stackTraceString.toString();}}卡顿检测
matrix 字节码插桩慢函数检测采样率法通过一个外置的工作线程Handler按一段时间采样如果大部分都是某个方法则这个方法可能存在风险点
/**
* 按照一定频率采样
* 目标是找到卡顿时刻前后的堆栈做大致定位无法做到精准定位
* 原则上采样越高定位越精准
* 还有目前只采样了java层的堆栈c层的需要另外实现这个后续补充
*/
public class CallstackSampler {private static final String TAG CallstackSampler;private final Thread thread;private final Handler mHandler;private final long sThreshold 1000;private final Runnable mRunnable new Runnable() {Overridepublic void run() {doSample();mHandler.postDelayed(this, sThreshold);}};public CallstackSampler(Thread thread) {this.thread thread;HandlerThread mWorkThread new HandlerThread(StackSampler thread.getName());mWorkThread.start();mHandler new Handler(mWorkThread.getLooper());}private void doSample() {// 采集指定线程当前堆栈信息StackTraceElement[] stackTrace thread.getStackTrace();String stackTraceString Arrays.toString(stackTrace);if (!stackTraceString.contains(nativePollOnce)) {Log.d(TAG, thread.getName() Callstack sample taken at time: System.currentTimeMillis() stackTraceString);}}public void startSampling() {mHandler.postDelayed(mRunnable, sThreshold);}public void stopSampling() {mHandler.removeCallbacks(mRunnable);}
}主线程耗时检测设置一个printer
CPU 优化案例
线程池复用减少CPU调度开销资源拷贝优化减少读取IO时间线程命名方便定位问题非必要内容延迟初始化初始化任务优先级分配削峰填谷