- 211012.《Learning Android中文版》学习笔记
- 211004.《Learning Android中文版》学习笔记
- 210727.《Learning Android中文版》学习笔记
- 210808.《Learning Android中文版》学习笔记
- 211004.《Learning Android中文版》学习笔记
- 210612.《Learning Android中文版》学习笔记
- 210808.《Learning Android中文版》学习笔记
- 210615.《Learning Android中文版》学习笔记
- 210727.《Learning Android中文版》学习笔记
- 210615.《Learning Android中文版》学习笔记
《Learning Android中文版》学习笔记06.7
6.7 Android的线程机制
一个线程就是一个连续的指令序列。每个CPU一次只能处理一条指令,不过大部分操作系统都支持在单个CPU中轮流执行多个线程,也支持在多个CPU中同时执行多个线程。不同的线程拥有不同的优先级,操作系统以此为依据,安排线程的调度。
Android操作系统建基于Linux,这就获得了多线程的支持。作为开发者要开发正确的程序,就需要对应用程序使用线程的方法有所了解。
6.7.1. 单线程执行
Android程序默认运行在单线程之下。单线程顺序执行所有的操作,这一操作完成之前,下一个操作绝不会执行。这一行为被称作“阻塞”(blocking)。
图 6.8. 单线程执行
![](https://img.haomeiwen.com/i22406019/b9aca0b265865cac.png)
这个线程也被称作UI线程,意思是程序中用户界面的相关操作都在这里执行。除处理所有UI元素的渲染之外,事件的响应也由它负责。比如触摸屏幕、点击按钮等等。图6.8 "单线程执行"展示了在只有一个UI线程时,代码的执行过程。
让StatusActivity在单线程中运行会遇到一个问题,那就是执行网络操作时,用户界面会响应不灵。这是因为阻塞在网络操作上的时间是不可预知的。我们不知道用户的网络接入方式是快速的WiFi,还是慢速的GPRS,因此在调用twitter.updateStatus()时,必须考虑延时的问题。
Note:
程序若长时间无响应(一般是五秒),Android系统会自动弹出一个对话框,询问是否将该程序所在的进程杀死。这个对话框就是Application Not Responding(应用程序无响应),简称ANR。如 图6.9 "Application Not Responding对话框"。
图 6.9. Application Not Responding对话框
![](https://img.haomeiwen.com/i22406019/5e07589dcc635f57.png)
6.7.2. 多线程执行
与在单线程中阻塞相比,更好的做法是让程序运行在多个线程之中。系统负责分配CPU时间,几个线程仿佛在同一时刻同时运行。这样可以避免某线程独占计算资源。
图 6.10. 多线程执行
![](https://img.haomeiwen.com/i22406019/6be40bcad4bd299c.png)
在例子中,我们将网络操作的相关代码放到独立的线程里面。这样我们的主线程可以避免阻塞在网络操作上,用户界面不会响应不灵。按惯例,我们一般认为主线程是运行于前台,而其它的线程都是运行于后台。这是因为前端的用户界面运行于主线程。但在调度上,它们都是平等的。图 6.10 "多线程执行" 展示了拥有两个线程——UI线程和一个辅助线程——的时候,代码的执行过程。
使用多线程的方法有很多,Java的Thread类是其中之一。使用Thread类,就是使用Java的原生特性。
缺点:
但这里有一点,那就是Java线程不可以直接访问其它线程的私有数据,比如UI线程中的控件。这样设计可以避免一些同步问题。比如UI线程若在执行中,就并不希望其它线程来扰乱自己的状态。
为弥补这点不足,也作为Java标准的线程机制的补充,Android提供了一个AsyncTask类。
6.7.3. AsyncTask
既要避免长时间的阻塞,又要访问UI线程的私有数据,那该怎么办?使用Android内置的AsyncTask(异步任务)机制。要使用它,我们需要创建一个AsyncTask的子类,并覆盖doInBackground()与onPostExecute()两个方法。这两个方法里面分别对应后台执行的相关代码:任务进行时的操作,以及任务完成时的操作。
例6.5是使用AsyncTask修改过的代码,实现异步发布到服务端。它的开头部分与例6.3相似,不同在于它是使用了异步任务,不会阻塞在网络操作上。
例 6.5. StatusActivity.java, version 2
package com.marakana.yamba1;
import winterwell.jtwitter.Twitter;
import winterwell.jtwitter.TwitterException;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class StatusActivity2 extends Activity implements OnClickListener {
private static final String TAG = "StatusActivity";
EditText editText;
Button updateButton;
Twitter twitter;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.status);
// Find views
editText = (EditText) findViewById(R.id.editText);
updateButton = (Button) findViewById(R.id.buttonUpdate);
updateButton.setOnClickListener(this);
twitter = new Twitter("student", "password");
twitter.setAPIRootUrl("http://yamba.marakana.com/api");
}
// Asynchronously posts to twitter
class PostToTwitter extends AsyncTask<String, Integer, String> { // http://dev.icybear.net/learning-android-cn/images/1.png
// Called to initiate the background activity
@Override
protected String doInBackground(String... statuses) { // http://dev.icybear.net/learning-android-cn/images/2.png
try {
Twitter.Status status = twitter.updateStatus(statuses[0]);
return status.text;
} catch (TwitterException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return "Failed to post";
}
}
// Called when there's a status to be updated
@Override
protected void onProgressUpdate(Integer... values) { // http://dev.icybear.net/learning-android-cn/images/3.png
super.onProgressUpdate(values);
// Not used in this case
}
// Called once the background activity has completed
@Override
protected void onPostExecute(String result) { // http://dev.icybear.net/learning-android-cn/images/4.png
Toast.makeText(StatusActivity2.this, result, Toast.LENGTH_LONG).show();
}
}
// Called when button is clicked
public void onClick(View v) {
String status = editText.getText().toString();
new PostToTwitter().execute(status); // http://dev.icybear.net/learning-android-cn/images/5.png
Log.d(TAG, "onClicked");
}
}
PostToTwitter是StatusActivity的内部类,也是AsyncTask的子类。留意其中对泛型的使用,通过三个类型参数,决定了后面函数的类型:第一个类型参数与doInBackground()相关,第二个与onPostExecute()相关,第三个与onPostExecute()相关。
doInBackground()是个回调方法,表示异步执行的网络操作,就像执行在后台一样。它的参数String...来自前面声明中的第一个类型参数,后面的三个.表示它接纳多个字符串作为参数,即使只用到一个字符串也必须如此。
onProgressUpdate()在任务进度更新时触发,其中的“进度”由doInBackground()负责更新。一个应用样例是,下载工具显示已完成的百分比。但在这里没有显示进度信息的必要,因此留空。
onPostExecute()在任务完成时触发。我们可以在这个回调方法里面更新用户界面,显示任务完成。这里使用了Android的Toast机制,在屏幕上弹出一条消息。创建Toast的静态方法是makeText(),但在创建一条Toast消息之后,还需要调用show()才能显示出来,对这点需要小心。另外onPostExecute()方法参数的类型与前面的第三个类型参数相关。
设置AsyncTask完毕,接下来使用它:简单创建一个实例,然后调用execute()即可。它的参数会原样传给doInBackground()。留意,这是个可变参数,因此单个字符串会被转化为字符串数组的形式。
到这里用户只要单击Upate Status按钮,Activity即可隐式地创建出一个独立的线程,在里面执行异步任务中的网络操作。到任务结束时,AsyncTask会弹出一个Toast显示任务的成功与否。这样可以避免应用程序长时间的阻塞,也可以避免用户见到讨厌的Application Not Responding: Force Close or Wait对话框,如 图6.9 "Application Not Responding对话框"。到现在,我们程序的执行效果如 图 6.11 "StatusActivity, 1"。
图 6.11. StatusActivity, 1
![](https://img.haomeiwen.com/i22406019/69aa94a0ecc7f8a9.png)
网友评论