Thread线程笔记¶
UI线程模型(单一线程模型)
- 不能阻塞主线程;阻塞超过时限会被判定“应用程序未响应”
ANR
- 非UI线程不能访问UI工具包
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.textView); // Fail: Only the original thread that created a view hierarchy can touch its views. new Thread() { @Override public void run() { int count = 0; while (count < 1000) { textView.setText("当前TextView的值是" + count); try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } count++; } } }.start(); }
解决方案¶
- 工作线程尝试访问UI线程,并委托UI线程更新UI
- 工作线程与UI线程之间通信,让工作线程发送消息给UI线程,让UI线程根据消息更新UI
- 使用AsyncTask
方案1:从工作线程访问UI线程¶
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
View.post(Runnable)
new Thread() { @Override public void run() { count = 0; while (count < 1000) { textView.post(new Runnable() { @Override public void run() { textView.setText("当前计数器:" + count); } }); try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } count++; } } }.start();
方案3:使用AsyncTask¶
AsyncTask<T param, T progress, T result>
- param 发送给异步任务所需的参数类型
- progress 后台运算过程中进度的参数类型
- result 后台运算的结果类型
不需要的泛型参数可以标记为Void
任务通常经历的4个步骤:
onPreExecute()
执行异步任务之前,运行在UI线程中doInBackground(Params... )
执行在后台的长时间操作;运行的结果通过此步骤返回,并被传递到最后一步。此步中可以使用publishProgress(progress... )
发布进度onProgressUpdate(progress... )
调用publishProgress()
之后在UI线程中执行onPostExecute(result... )
后台操作结束后在UI线程中执行
上述4步骤中的方法不能手动调用,故AsyncTask实例必须在UI线程中创建,execute()
方法必须在UI线程中调用。
示例:下载图片并展示进度¶
@Override public void onClick(View view) { if (view.getId() == R.id.button) { new MyAsyncTask().execute("https://qfstudio.net/i_bg_cool.jpg"); } } public class MyAsyncTask extends AsyncTask<String, Integer, byte[]> { @Override protected void onPreExecute() { progress.setProgress(0); } @Override protected byte[] doInBackground(String... strings) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { URL url = new URL(strings[0]); HttpURLConnection con = (HttpURLConnection) url.openConnection(); Log.i("Code", new Integer(con.getResponseCode()).toString()); if (con.getResponseCode() == 200) { InputStream in = con.getInputStream(); long length = con.getContentLength(); int current = 0, temp; byte[] buff = new byte[1024]; while ((temp = in.read(buff)) != -1) { current += temp; int progress = (int)(current/(double)length * 100); publishProgress(progress); out.write(buff, 0, temp); out.flush(); } } } catch (Exception e) { e.printStackTrace(); } return out.toByteArray(); } @Override protected void onProgressUpdate(Integer... values) { progress.setProgress(values[0]); } @Override protected void onPostExecute(byte[] result) { if (result != null && result.length != 0) { Bitmap bm = BitmapFactory.decodeByteArray(result, 0, result.length); imageView.setImageBitmap(bm); } else { Toast.makeText(MainActivity.this, "网络连接出现了问题", Toast.LENGTH_LONG).show(); } } }
示例:取消异步任务¶
调用cancel(boolean)
来取消异步任务:调用此方法后isCanceled()
返回true,并且异步任务结束后运行onCancel(Object)
而不是onPostExecute()
应该在doInBackground()
中检查isCanceled()
的值。
@Override public void onClick(View view) { if (!onGoing) { task = new MyAsyncTask(); task.execute("https://qfstudio.net/i_bg_cool.jpg"); onGoing = true; } else { task.cancel(true); onGoing = false; } } public class MyAsyncTask extends AsyncTask<String, Integer, byte[]> { @Override protected void onPreExecute() { progress.setProgress(0); } @Override protected byte[] doInBackground(String... strings) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { URL url = new URL(strings[0]); HttpURLConnection con = (HttpURLConnection) url.openConnection(); Log.i("Code", new Integer(con.getResponseCode()).toString()); if (con.getResponseCode() == 200) { InputStream in = con.getInputStream(); long length = con.getContentLength(); int current = 0, temp; byte[] buff = new byte[1024]; while ((temp = in.read(buff)) != -1) { current += temp; int progress = (int)(current/(double)length * 100); publishProgress(progress); out.write(buff, 0, temp); out.flush(); if (isCancelled()) break; } } } catch (Exception e) { e.printStackTrace(); } return out.toByteArray(); } @Override protected void onProgressUpdate(Integer... values) { progress.setProgress(values[0]); } @Override protected void onCancelled() { Toast.makeText(MainActivity.this, "已取消", Toast.LENGTH_LONG).show(); } @Override protected void onPostExecute(byte[] result) { if (result != null && result.length != 0) { Bitmap bm = BitmapFactory.decodeByteArray(result, 0, result.length); imageView.setImageBitmap(bm); } else { Toast.makeText(MainActivity.this, "网络连接出现了问题", Toast.LENGTH_LONG).show(); } } }