测试是安卓开发中
技术进阶的重要内容,对于基建开发更是必备技能。所谓工欲善其事必先利其器,首先应该掌握安卓官方的提供的各种工具的使用,进一步的,通过探究其原理来开发我们自己的性能检测工具。
测试指标和测试方法

·AndroidGodEye
UI性能 当一个xml布局首次加载到activity/fragment中时,它需要经历:

Java代码,X2C就是这样开源项目。不过这也不是一个完美的解决方案,比如当xml属性没有对应的setXXX方法时(ProgressBar、SeekBar等)生成的Java代码就会有问题。
网络优化
·优化DNS:使用HttpDNS
· 连接复用:开启keep-alive,默认开启
· 数据压缩:开启gzip压缩
· 资源分级:根据网络状况下载不同清晰度图片 网络请求策略: 1. 无网络则不发起网络请求; 2. 优先使用WiFi; 3. 统计日志等非重要非重要网络请求等到WiFi环境再发送。
电量优化
· 定位服务、消息推送服务等共性服务复用;
· 前台不申请WakeLock或使用带超时的方法申请,使用FLAG_KEEP_SCREEN_ON保持屏幕常亮。
SysTrace sysTrace是老版的用于系统跟踪的CLI工具,基于python 2.7实现,。其生成的报告是个HTML文件,用
浏览器打开其界面大致如下:
Systrace HTML报告截图,其中显示了与某个应用之间5秒的交互情况

记录某个时间段内设备的活动情况(包括但不限于CPU使用率、线程活动、磁盘活动等),并生成跟踪文件(Perfetto或Systrace格式,根据具体的工具),用这些文件可以生成系统报告(html或其他展示形式)。
生成报告
CLI命令生成 python systrace.py -a com.example.myapp -b 16384 \ -o my_systrace_report.html sched freq idle am wm gfx view binder_driver hal \ dalvik camera input res
-a:指定进程名称,通常就是包名; -b:指定缓冲区大小; -o:指定输出报告的文件路径; -t:跟踪设备活动N秒。如果您未指定此选项,systrace 会提示您在命令行中按 Enter 键结束跟踪; -e:指定设备序列号。 categories:指定要跟踪的信息,包括sched、freq、idle、am、wm、gfx渲染图形的系统进程、view、binder_driver、hal、dalvik、camera、input、res等,可通过systrace -l查看已连接设备可用的服务列表。 1. 经实际测试systrace.py仅支持python 2.7无法在python 3.x中使用,这点需要注意; 2. 出了实时捕获还支持通过指定trace文件生成HTML报告。
android.os.Debug生成 可用于生成.trace文件,用于在Systrace或CPU Profiler中导入分析。使用示例如下: public class XXXActivity extends BaseActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { // 生成的.trace文件位于:sdcard/Android/data/package_name/files/XXXActivity_1111.trace Debug.startMethodTracing("XXXActivity_"+ System.currentTimeMillis()); ... } @Override protected void onDestroy() { super.onDestroy(); Debug.stopMethodTracing(); } }
然后用adb pull到开发机器导入到Profiler或则直接用AS的Device File Explorer双击打开:

读懂报告 从Systrace报告的官网介绍中可以了解到我们通常关注Frames中为黄色的帧,通过点击对应的黄色圆点可以查看调用栈,从而定位问题点。
自定义事件 android.os.Trace作为SDK提供的辅助API工具,可以帮助我们实现自定义事件的统计。根据官网介绍,使用示例如下:
·演示Java层新增定义自定义事件 list_item.setOnItemClickListener((parent, view, position, id) -> { // 追踪列表项点击事件的耗时情况 Trace.beginSection("OnListItemClick"); try { ...... } finally { // 为确保Trace.endSection执行,建议放到finally块中 Trace.endSection(); } });
·演示native层新增定义自定义事件 //1. 定义在游戏内捕获自定义事件所用 ATrace 函数的函数指针 #include <android/trace.h> #include <dlfcn.h> void *(*ATrace_beginSection) (const char* sectionName); void *(*ATrace_endSection) (void); typedef void *(*fp_ATrace_beginSection) (const char* sectionName); typedef void *(*fp_ATrace_endSection) (void); //2. 在运行时加载 ATrace 符号,如以下代码段所示。通常在对象构造函数中执行此过程 // Retrieve a handle to libandroid. void *lib = dlopen("libandroid.so", RTLD_NOW || RTLD_LOCAL); // Access the native tracing functions. if (lib != NULL) { // Use dlsym() to prevent crashes on devices running Android 5.1 // (API level 22) or lower. ATrace_beginSection = reinterpret_cast<fp_ATrace_beginSection>( dlsym(lib, "ATrace_beginSection")); ATrace_endSEction = reinterpret_cast<fp_ATrace_endSection>( dlsym(lib, "ATrace_endSection")); } //3. 在自定义事件的开头和结尾分别调用 ATrace_beginSection() 和 ATrace_endSection() #include <android/trace.h> char *customEventName = new char[32]; sprintf(customEventName, "User tapped %s button", buttonName); ATrace_beginSection(customEventName); // Your
app or game's response to the button being pressed. ATrace_endSection();
然后运行APP,点击操作列表项,同事启用AS的CPU Profiler对过程进行录制,结束录制后将在分析报告中看到对应的自定义事件:

System Tracing 一款运行在Android 9(API 级别 28)或更高版本的终端工具,其功能与systrace类似,用于直接在
手机上获取跟踪数据,生成.perfetto-trace[新系统,用Perfetto打开]/.ctrace[老系统,用Systrace生成报告]文件。
TraceView 一款搭配Android Device Monitor的图形化日志查看工具,可以打开Systrace生成的.trace。截图如下:

LayoutInspector AS自带的布局检测工具,可以查看当前运行的activity布局的详细树状结构。
Profiler
CPU Profiler


代码检测
Looper监听 为什么主线程的代码最终都会在Looper.loop()中执行? 当APP启动后,其实主线程ActivityThread多数时间是阻塞在loop的queue.next()中的nativePollOnce()方法里,得益于
Linux pipe/epoll机制,主线不会像我们日常多线程开发时使用独占锁导致的线程阻塞那样占用CPU资源,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。 主线程休眠后被谁唤醒呢?答案是同个进程中的其他线程发过来的消息。比如:当我们启动一个activity时,实际是执行了如下的IPC通讯过程:

代码实现示例 private void check() { Looper.getMainLooper().setMessageLogging(new Printer() { private final String START = ">>>>> Dispatching to"; private final String END = "<<<<< Finished to"; @Override public void println(String s) { if (s.startsWith(START)) { mCheckTask.start(); } else if (s.startsWith(END)) { mCheckTask.end(); } } }); } private class CheckTask { private HandlerThread mHandlerThread = new HandlerThread("卡顿检测"); private Handler mHandler; private final int THREAD_HOLD = 1000; public CheckTask() { mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); } private Runnable mRunnable = new Runnable() { @Override public void run() { log(); } }; public void start() { mHandler.postDelayed(mRunnable, THREAD_HOLD); } public void end() { mHandler.removeCallbacks(mRunnable); } } /** * 输出当前异常或及错误堆栈信息。 */ private void log() { StringBuilder sb = new StringBuilder(); StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace(); for (StackTraceElement s : stackTrace) { sb.append(s + "\n"); } Log.w(TAG, sb.toString()); }
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
21天更文挑战,赢取价值500元大礼,还有机会成为签约作者!