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