现在大多数的移动设备已经变得越来越快,但是它们其实也不算是非常快。如果你想让你的APP既可以承受一些繁杂的工作而又不影响用户体验的话,那么必须把任务并行执行。在Android上,我们使用线程。
端一杯咖啡,然后仔细阅读这篇文章。我会给大家介绍一下线程的概念,还有在Java中怎么使用线程,在线程中怎么使用Handler等。
如果需要使用异步处理或者并行任务的话,那么你一定会用到线程。
线程或者线程执行本质上就是一串命令(也是程序代码),然后我们把它发送给操作系统执行。
一般来说,我们的CPU在任何时候一个核只能处理一个线程。多核处理器(目前大多数Android设备已经都是多核)顾名思义,就是可以同时处理多线程(通俗地讲就是可以同时处理多件事)。
上面我说的是一般情况,并不是所有的描述都是一定正确的。因为单核也可以用多任务模拟出多线程。
每个运行在线程中的任务都可以分解成多条指令,而且这些指令不用同时执行。所以,单核设备可以首先切换到线程1去执行指令1A,然后切换到线程2去执行指令2A,接着返回到线程1再去执行1B、1C、1D,然后继续切换到线程2,执行2B、2C等等,以此类推。
这个线程之间的切换十分迅速,以至于在单核的设备中也会发生。几乎所有的线程都在相同的时间内进行任务处理。其实,这都是因为速度太快造成的假象,就像电影《黑客帝国》里的特工Brown一样,可以变幻出很多的头和手。
接下来我们来看一些代码。
在Java中,如果要想做平行任务处理的话,会在Runnable里面执行你的代码。可以继承Thread类,或者实现Runnable接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// Version 1
public
class
IAmAThread
extends
Thread {
public
IAmAThread() {
super
(
"IAmAThread"
);
}
@Override
public
void
run() {
// your code (sequence of instructions)
}
}
// to execute this sequence of instructions in a separate thread.
new
IAmAThread().start();
// Version 2
public
class
IAmARunnable
implements
Runnable {
@Override
public
void
run() {
// your code (sequence of instructions)
}
}
// to execute this sequence of instructions in a separate thread.
IAmARunnable myRunnable =
new
IAmARunnable();
new
Thread(myRunnable).start();
|
这两个方法基本上是一样的。第一个版本是创建一个Thread类,第二个版本是需要创建一个Runnable对象,然后也需要一个Thread类来调用它。
第二个版是通常建议使用的方法。这也是一个很大的主题了,超过了本文的范围,以后会再做讨论。
无论何时启动APP,所有的组件都会运行在一个单独的线程中(默认的)——叫做主线程。这个线程主要用于处理UI的操作并为视图组件和小部件分发事件等,因此主线程也被称作UI线程。
如果你在UI线程中运行一个耗时操作,那么UI就会被锁住,直到这个耗时操作结束。对于用户体验来说,这是非常糟糕的!这也就是为什么我们要理解Android上的线程机制了。理解这些机制就可以把一些复杂的工作移动到其它的线程中去执行。如果你在UI线程中运行一个耗时的任务,那么很有可能会发生ANR(应用无响应),这样用户就会很快地结束掉你的APP。
Android和Java一样,它支持使用Java里面的Thread类来进行一步任务处理。所以可以轻松地像上面Java的例子一样来使用Android上的线程,不过那好像还是有点困难。
其实平行任务处理没有想象中的那么简单,你必须在多线程中保证并发,就像伟大的Tim Bray说的那样:ordinary humans can’t do concurrency at scale (or really at all) …
特别对于Android来说,以下这些功能就略显臃肿:
那么在Android上怎么进行任务并发处理呢?
你可能听过一些Android上一些常见的名词:
1、Handler
这就是我们今天要讨论的详细主题。
2、AsyncTask
使用AsyncTask是在Android上操作线程最简单的方式,也是最容易出错的方式。
3、IntentService
这种方式需要写更多的代码,但是这是把耗时任务移动到后台的很好的方式,也是我最喜欢的方式。配上使用一个EventBus机制的框架如Otto,这样的话实现IntentService就非常简单了。
4、Loader
关于处理异步任务,还有很多事情需要做,比如从数据库或者内容提供者那里处理一些数据。
5、Service
如果你曾经使用过Service的话,你应该知道这里会有一点误区,其中一个常见的误解就是服务是运行在后台线程的。其实不是!看似运行在后台是因为它们不与UI组件关联,但是它们(默认)是运行在UI线程上的……所以默认运行在UI线程上,甚至在上面没有UI部件。
如果想要把服务运行在后台线程中,那么必须自定义一个线程,然后把操作代码都运行在那个线程中(与上面提到的方法很类似)。事实上你应该使用IntentService实现,但是这不是本文讨论的主题。
以下是从 Android developer documentation for Handlers:中摘选的一段话:
> A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread/message queue of the thread that is creating it — from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
为了更好地了解这个概念,也许你需要去看看什么是Message Queues。
在线程里基本都有一个叫做“消息队列”的东西,它负责线程间通信。这是一种设计模式,所有控制指令或者内容在线程间传递。
消息队列如同它的名字那样,对于线程来说,它就是一个指令队列。这里我们还可以做一些更酷的事:
注意:这里说的“消息”和Runnable对象、指令队列的概念是一样的。
回到Android上的Handler……如果你仔细阅读的话,可以看到文档是这样说的:
> A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue.
所以Handler可以让你给线程队列发消息:
> Each Handler instance is associated with a single thread and that thread’s message queue.
一个Handler对象只能和一个线程关联:
> When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it
所以一个Handler到底和哪个线程关联呢?就是创造它的线程。
> — from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.、
在我们了解这些知识后,请继续看……
小贴士: 这里有几点可能你还不知道。每个线程都和一个Handler类实例绑定,而且可以和别的线程一起运行,相互通信。
还有一个小建议(如果用过AsyncTask的话),AsyncTask内部也是使用Handler进行处理的,只是不是运行在UI线程而已,它会提供一个channel来和UI线程通信,使用postExecute方法即可实现。
有两种方式:
请记住:
如果你现在看看Handler的API,可以清楚看到这几个方法:
这里的代码都是很基础的,不过你可以好好看看注释。
示例1:使用Handler的“post”方法
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
public
class
TestActivity
extends
Activity {
// ...
// all standard stuff
@Override
public
void
onCreate(Bundle savedInstanceState) {
// ...
// all standard stuff
// we're creating a new handler here
// and we're in the UI Thread (default)
// so this Handler is associated with the UI thread
Handler mHandler =
new
Handler();
// I want to start doing something really long
// which means I should run the fella in another thread.
// I do that by sending a message - in the form of another runnable object
// But first, I'm going to create a Runnable object or a message for this
Runnable mRunnableOnSeparateThread =
new
Runnable() {
@Override
public
void
run () {
// do some long operation
longOperation();
// After mRunnableOnSeparateThread is done with it's job,
// I need to tell the user that i'm done
// which means I need to send a message back to the UI thread
// who do we know that's associated with the UI thread?
mHandler.post(
new
Runnable(){
@Override
public
void
run(){
// do some UI related thing
// like update a progress bar or TextView
// ....
}
});
}
};
// Cool but I've not executed the mRunnableOnSeparateThread yet
// I've only defined the message to be sent
// When I execute it though, I want it to be in a different thread
// that was the whole point.
new
Thread(mRunnableOnSeparateThread).start();
}
}
|
如果根本就没有Handler对象,回调post方法会比较难办。
示例2:使用postDelayed方法
近期本站新介绍的特性中,我每次都要模拟EditText的自动完成功能,每次文字改变后都会触发一个API的调用,从服务器中检索数据。
我想减少APP调用API的次数,所以决定使用Handler的postDelayed方法来实现这个功能。
本例不针对平行处理,只是关于Handler给消息队列发送消息还有安排消息在未来的某一点执行等。
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
28
29
30
31
32
33
34
35
|
// the below code is inside a TextWatcher
// which implements the onTextChanged method
// I've simplified it to only highlight the parts we're
// interested in
private
long
lastChange =
0
;
@Override
public
void
onTextChanged(
final
CharSequence chars,
int
start,
int
before,
int
count) {
// The handler is spawned from the UI thread
new
Handler().postDelayed(
// argument 1 for postDelated = message to be sent
new
Runnable() {
@Override
public
void
run() {
if
(noChangeInText_InTheLastFewSeconds()) {
searchAndPopulateListView(chars.toString());
// logic
}
}
},
// argument 2 for postDelated = delay before execution
300
);
lastChange = System.currentTimeMillis();
}
private
boolean
noChangeInText_InTheLastFewSeconds() {
return
System.currentTimeMillis() - lastChange >=
300
}
|
最后我就把“postAtTime”这个方法作为联系留给读者们了,掌握Handler了吗?如果是的话,那么可以尽情使用线程了。
- class LooperThread extends Thread {
- public Handler mHandler;
- public void run() {
- Looper.prepare(); //创建本线程的Looper并创建一个MessageQueue
- mHandler = new Handler() {
- public void handleMessage(Message msg) {
- // process incoming messages here
- }
- };
- Looper.loop(); //开始运行Looper,监听Message Queue
- }
- }
- public class ListProgressDemo extends ListActivity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.listprogress);
- ((Button) findViewById(R.id.load_Handler)).setOnClickListener(new View.OnClickListener(){
- @Override
- public void onClick(View view) {
- data = null;
- data = new ArrayList<String>();
- adapter = null;
- showDialog(PROGRESS_DIALOG);
- new ProgressThread(handler, data).start();
- }
- });
- }
- @Override
- protected Dialog onCreateDialog(int id) {
- switch(id) {
- case PROGRESS_DIALOG:
- return ProgressDialog.show(this, "",
- "Loading. Please wait...", true);
- default: return null;
- }
- }
- private class ProgressThread extends Thread {
- private Handler handler;
- private ArrayList<String> data;
- public ProgressThread(Handler handler, ArrayList<String> data) {
- this.handler = handler;
- this.data = data;
- }
- @Override
- public void run() {
- for (int i=0; i<8; i++) {
- data.add("ListItem"); //后台数据处理
- try {
- Thread.sleep(100);
- }catch(InterruptedException e) {
- Message msg = handler.obtainMessage();
- Bundle b = new Bundle();
- b.putInt("state", STATE_ERROR);
- msg.setData(b);
- handler.sendMessage(msg);
- }
- }
- Message msg = handler.obtainMessage();
- Bundle b = new Bundle();
- b.putInt("state", STATE_FINISH);
- msg.setData(b);
- handler.sendMessage(msg);
- }
- }
- // 此处甚至可以不需要设置Looper,因为Handler默认就使用当前线程的Looper
- private final Handler handler = new Handler(Looper.getMainLooper()) {
- public void handleMessage(Message msg) { // 处理Message,更新ListView
- int state = msg.getData().getInt("state");
- switch(state){
- case STATE_FINISH:
- dismissDialog(PROGRESS_DIALOG);
- Toast.makeText(getApplicationContext(),
- "加载完成!",
- Toast.LENGTH_LONG)
- .show();
- adapter = new ArrayAdapter<String>(getApplicationContext(),
- android.R.layout.simple_list_item_1,
- data );
- setListAdapter(adapter);
- break;
- case STATE_ERROR:
- dismissDialog(PROGRESS_DIALOG);
- Toast.makeText(getApplicationContext(),
- "处理过程发生错误!",
- Toast.LENGTH_LONG)
- .show();
- adapter = new ArrayAdapter<String>(getApplicationContext(),
- android.R.layout.simple_list_item_1,
- data );
- setListAdapter(adapter);
- break;
- default:
- }
- }
- };
- private ArrayAdapter<String> adapter;
- private ArrayList<String> data;
- private static final int PROGRESS_DIALOG = 1;
- private static final int STATE_FINISH = 1;
- private static final int STATE_ERROR = -1;
- }
- ((Button) findViewById(R.id.load_AsyncTask)).setOnClickListener(new View.OnClickListener(){
- @Override
- public void onClick(View view) {
- data = null;
- data = new ArrayList<String>();
- adapter = null;
- //显示ProgressDialog放到AsyncTask.onPreExecute()里
- //showDialog(PROGRESS_DIALOG);
- new ProgressTask().execute(data);
- }
- });
- private class ProgressTask extends AsyncTask<ArrayList<String>, Void, Integer> {
- /* 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。*/
- @Override
- protected void onPreExecute() {
- // 先显示ProgressDialog
- showDialog(PROGRESS_DIALOG);
- }
- /* 执行那些很耗时的后台计算工作。可以调用publishProgress方法来更新实时的任务进度。 */
- @Override
- protected Integer doInBackground(ArrayList<String>... datas) {
- ArrayList<String> data = datas[0];
- for (int i=0; i<8; i++) {
- data.add("ListItem");
- }
- return STATE_FINISH;
- }
- /* 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,
- * 后台的计算结果将通过该方法传递到UI thread.
- */
- @Override
- protected void onPostExecute(Integer result) {
- int state = result.intValue();
- switch(state){
- case STATE_FINISH:
- dismissDialog(PROGRESS_DIALOG);
- Toast.makeText(getApplicationContext(),
- "加载完成!",
- Toast.LENGTH_LONG)
- .show();
- adapter = new ArrayAdapter<String>(getApplicationContext(),
- android.R.layout.simple_list_item_1,
- data );
- setListAdapter(adapter);
- break;
- case STATE_ERROR:
- dismissDialog(PROGRESS_DIALOG);
- Toast.makeText(getApplicationContext(),
- "处理过程发生错误!",
- Toast.LENGTH_LONG)
- .show();
- adapter = new ArrayAdapter<String>(getApplicationContext(),
- android.R.layout.simple_list_item_1,
- data );
- setListAdapter(adapter);
- break;
- default:
- }
- }
转自:http://blog.jobbole.com/73267/
http://android.blog.51cto.com/268543/343823