在Android中,性能优化是我们持之不懈的工作。这其中,在主线程执行耗时的任务,可能会导致界面卡顿,甚至是ANR(程序未响应)。当然Android提供了很多优秀的工具,比如StrictMode,Method Tracing等,便于我们检测问题。
这里,本文将介绍一个更加简单有效的方法。相比StrictMode来说更加便于发现问题,相比Method Tracing来说更加容易操作。
首先,我们有这样一个程序代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); writeContentToFile(); } private void writeContentToFile() { File log = new File(Environment.getExternalStorageDirectory(), "Log.txt"); Writer outWriter = null; try { outWriter = new BufferedWriter(new FileWriter(log.getAbsolutePath(), false)); outWriter.write(new Date().toString()); outWriter.write(" : \n"); outWriter.flush(); } catch (Exception e) { e.printStackTrace(); } finally { if (outWriter != null) { try { outWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } } |
---|
上面的代码需要优化,因为
上面介绍StrictMode和Method Traing都可以检测这个问题,这里我们我们用一个更简单的方法
1 2 3 4 5 6 7 8 | public void checkWorkerThread() { boolean isMainThread = Looper.myLooper() == Looper.getMainLooper(); if (isMainThread) { if (BuildConfig.DEBUG) { throw new RuntimeException("Do not do time-consuming work in the Main thread"); } } } |
---|
这段方法有几点注意的。
Looper.myLooper() == Looper.getMainLooper()
可以准确判断当前线程是否为主线程。比如上面的方法加入checkWorkerThread检查
1 2 3 4 | private void writeContentToFile() { checkWorkerThread(); //代码省略,具体实现参考上面 } |
---|
再次执行程序,会曝出异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | java.lang.RuntimeException: Unable to start activity ComponentInfo{com.droidyue.checkthreadsample/com.droidyue.checkthreadsample.MainActivity}: java.lang.RuntimeException: Do not do time-consuming work in the Main thread at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2664) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2733) at android.app.ActivityThread.access$900(ActivityThread.java:187) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1584) at android.os.Handler.dispatchMessage(Handler.java:111) at android.os.Looper.loop(Looper.java:194) at android.app.ActivityThread.main(ActivityThread.java:5869) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1019) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:814) Caused by: java.lang.RuntimeException: Do not do time-consuming work in the Main thread at com.droidyue.checkthreadsample.MainActivity.checkWorkerThread(MainActivity.java:34) at com.droidyue.checkthreadsample.MainActivity.writeContentToFile(MainActivity.java:40) at com.droidyue.checkthreadsample.MainActivity.onCreate(MainActivity.java:27) at android.app.Activity.performCreate(Activity.java:6127) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2617) ... 10 more |
---|
通过分析crash stacktrace 我们可以很轻松的发现问题的根源并解决。
Android中的工作者线程API有很多,简单的有Thread,AsyncTask,也有ThreadPool,HandlerThread等。关于如何选择,可以参考这篇文章。关于Android中工作者线程的思考
当你刚刚写完一个方法时,考虑这一下这个方法会不会很耗时,如果耗时,不妨增加一个线程的check。注意,一定要加载debug版,不要影响到线上的用户。