Android 哪些操作在子线程 - MrWu94/AndroidNote GitHub Wiki

哪些操作需要加上检查,检查是否在子线程进行

  • 本地IO读写
  • 网络操作
  • Bitmap相关的缩放等
  • 其他耗时的任务

这里,本文将介绍一个更加简单有效的方法。相比StrictMode来说更加便于发现问题,相比Method Tracing来说更加容易操作。

首先,我们有这样一个程序代码

@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();
            }
        }
    }
}

上面的代码需要优化,因为

writeContentToFile 是一个本地文件写操作,比较耗时 而writeContentToFile 这个方法却放在了主线程中,必然会阻塞主线程其他的工作顺利执行。

上面介绍StrictMode和Method Traing都可以检测这个问题,这里我们我们用一个更简单的方法

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()可以准确判断当前线程是否为主线程。 BuildConfig.DEBUG 条件控制,只有在debug环境下抛出异常,给予开发者明显的提示。当然也可以使用自定义的是否抛出异常的逻辑 如果当前线程不是主线程,那么就被认为是工作者线程。 比如上面的方法加入checkWorkerThread检查

private void writeContentToFile() {
    checkWorkerThread();
    //代码省略,具体实现参考上面
}

再次执行程序,会曝出异常。

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

如何选择工作者线程

Android中的工作者线程API有很多,简单的有Thread,AsyncTask,也有ThreadPool,HandlerThread等。

参考:http://droidyue.com/blog/2017/03/13/a-small-trick-to-detect-time-consuming-task/