在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行!
1. 当Handler对象的构造函数的参数为空,则为当前所在线程的Looper;
2. Looper.getMainLooper()得到的是主线程的Looper对象,Looper.myLooper()得到的是当前线程的Looper对象。
现在来看一个例子,模拟从网络获取数据,加载到ListView的过程:
1. public class ListProgressDemo extends ListActivity {
2.
3. @Override
4. public void onCreate(Bundle savedInstanceState) {
5. super.onCreate(savedInstanceState);
6. setContentView(R.layout.listprogress);
7.
8. ((Button) findViewById(R.id.load_Handler)).setOnClickListener(new View.OnClickListener(){
9.
10. @Override
11. public void onClick(View view) {
12. data = null;
13. data = new ArrayList<String>();
14.
15. adapter = null;
16.
17. showDialog(PROGRESS_DIALOG);
18. new ProgressThread(handler, data).start();
19. }
20. });
21. }
22.
23. @Override
24. protected Dialog onCreateDialog(int id) {
25. switch(id) {
26. case PROGRESS_DIALOG:
27. return ProgressDialog.show(this, "",
28. "Loading. Please wait...", true);
29.
30. default: return null;
31. }
32. }
33.
34. private class ProgressThread extends Thread {
35.
36. private Handler handler;
37. private ArrayList<String> data;
38.
39. public ProgressThread(Handler handler, ArrayList<String> data) {
40. this.handler = handler;
41. this.data = data;
42. }
43.
44. @Override
45. public void run() {
46. for (int i=0; i<8; i++) {
47. data.add("ListItem"); //后台数据处理
48. try {
49. Thread.sleep(100);
50. }catch(InterruptedException e) {
51.
52. Message msg = handler.obtainMessage();
53. Bundle b = new Bundle();
54. b.putInt("state", STATE_ERROR);
55. msg.setData(b);
56. handler.sendMessage(msg);
57.
58. }
59. }
60. Message msg = handler.obtainMessage();
61. Bundle b = new Bundle();
62. b.putInt("state", STATE_FINISH);
63. msg.setData(b);
64. handler.sendMessage(msg);
65. }
66.
67. }
68.
69. // 此处甚至可以不需要设置Looper,因为Handler默认就使用当前线程的Looper
70. private final Handler handler = new Handler(Looper.getMainLooper()) {
71.
72. public void handleMessage(Message msg) { // 处理Message,更新ListView
73. int state = msg.getData().getInt("state");
74. switch(state){
75. case STATE_FINISH:
76. dismissDialog(PROGRESS_DIALOG);
77. Toast.makeText(getApplicationContext(),
78. "加载完成!",
79. Toast.LENGTH_LONG)
80. .show();
81.
82. adapter = new ArrayAdapter<String>(getApplicationContext(),
83. android.R.layout.simple_list_item_1,
84. data );
85.
86. setListAdapter(adapter);
87.
88. break;
89.
90. case STATE_ERROR:
91. dismissDialog(PROGRESS_DIALOG);
92. Toast.makeText(getApplicationContext(),
93. "处理过程发生错误!",
94. Toast.LENGTH_LONG)
95. .show();
96.
97. adapter = new ArrayAdapter<String>(getApplicationContext(),
98. android.R.layout.simple_list_item_1,
99. data );
100.
101. setListAdapter(adapter);
102.
103. break;
104.
105. default:
106.
107. }
108. }
109. };
110.
111.
112. private ArrayAdapter<String> adapter;
113. private ArrayList<String> data;
114.
115. private static final int PROGRESS_DIALOG = 1;
116. private static final int STATE_FINISH = 1;
117. private static final int STATE_ERROR = -1;
118. }
这个例子,我自己写完后觉得还是有点乱,要稍微整理才能看明白线程间交互的过程以及数据的前后变化。随后了解到AsyncTask类,相应修改后就很容易明白了!
2.3 AsyncTask
AsyncTask版:
1. ((Button) findViewById(R.id.load_AsyncTask)).setOnClickListener(new View.OnClickListener(){
2.
3. @Override
4. public void onClick(View view) {
5. data = null;
6. data = new ArrayList<String>();
7.
8. adapter = null;
9.
10. //显示ProgressDialog放到AsyncTask.onPreExecute()里
11. //showDialog(PROGRESS_DIALOG);
12. new ProgressTask().execute(data);
13. }
14. });
15.
16. private class ProgressTask extends AsyncTask<ArrayList<String>, Void, Integer> {
17.
18.
19. @Override
20. protected void onPreExecute() {
21. // 先显示ProgressDialog
22. showDialog(PROGRESS_DIALOG);
23. }
24.
25.
26. @Override
27. protected Integer doInBackground(ArrayList<String>... datas) {
28. ArrayList<String> data = datas[0];
29. for (int i=0; i<8; i++) {
30. data.add("ListItem");
31. }
32. return STATE_FINISH;
33. }
34.
35.
36. @Override
37. protected void onPostExecute(Integer result) {
38. int state = result.intValue();
39. switch(state){
40. case STATE_FINISH:
41. dismissDialog(PROGRESS_DIALOG);
42. Toast.makeText(getApplicationContext(),
43. "加载完成!",
44. Toast.LENGTH_LONG)
45. .show();
46.
47. adapter = new ArrayAdapter<String>(getApplicationContext(),
48. android.R.layout.simple_list_item_1,
49. data );
50.
51. setListAdapter(adapter);
52.
53. break;
54.
55. case STATE_ERROR:
56. dismissDialog(PROGRESS_DIALOG);
57. Toast.makeText(getApplicationContext(),
58. "处理过程发生错误!",
59. Toast.LENGTH_LONG)
60. .show();
61.
62. adapter = new ArrayAdapter<String>(getApplicationContext(),
63. android.R.layout.simple_list_item_1,
64. data );
65.
66. setListAdapter(adapter);
67.
68. break;
69.
70. default:
71.
72. }
73. }
Android另外提供了一个工具类:AsyncTask。它使得UI thread的使用变得异常简单。它使创建需要与用户界面交互的长时间运行的任务变得更简单,不需要借助线程和Handler即可实现。
1) 子类化AsyncTask
2) 实现AsyncTask中定义的下面一个或几个方法
onProgressUpdate(Progress...) 在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
onPostExecute(Result) 执行完成后的操作,传送结果给UI 线程。
1) AsyncTask的实例必须在UI thread中创建;
2) AsyncTask.execute方法必须在UI thread中调用;
同时要注意:该task只能被执行一次,否则多次调用时将会出现异常。而且是不能手动停止的,这一点要注意,看是否符合你的需求!
在使用过程中,发现AsyncTask的构造函数的参数设置需要看明白: AsyncTask<Params, Progress, Result>
Params对应doInBackground(Params...)的参数类型。而new AsyncTask().execute(Params... params),就是传进来的Params数据,你可以execute(data)来传送一个数据,或者execute(data1, data2, data3)这样多个数据。
Progress对应onProgressUpdate(Progress...)的参数类型;
Result对应onPostExecute(Result)的参数类型。
当以上的参数类型都不需要指明某个时,则使用Void,注意不是void。不明白的可以参考上面的例子,或者API Doc里面的例子。