来自:http://www.aviyehuda.com/blog/2010/12/20/android-multithreading-in-a-ui-environment/

Why do we need multithreading in Android applications?

Let’s say you want to do a very long operation when the user pushes a button.
If you are not using another thread, it will look something like this:

1 ((Button)findViewById(R.id.Button01)).setOnClickListener(
2 newOnClickListener() {
3
4 @Override
5 publicvoidonClick(View v) {
6 intresult = doLongOperation();
7 updateUI(result);
8 }
9 });

What will happen?
The UI freezes. This is a really bad UI experience. The program may even crash.



The problem in using threads in a UI environment
So what will happen if we use a Thread for a long running operation.
Let’s try a simple example:

01 ((Button)findViewById(R.id.Button01)).setOnClickListener(
02 newOnClickListener() {
03
04 @Override
05 publicvoidonClick(View v) {
06
07 (newThread(newRunnable() {
08
09 @Override
10 publicvoidrun() {
11 intresult = doLongOperation();
12 updateUI(result);
13 }
14 })).start();
15
16 }

The result in this case is that the application crashes.
12-07 16:24:29.089: ERROR/AndroidRuntime(315): FATAL EXCEPTION: Thread-8
12-07 16:24:29.089: ERROR/AndroidRuntime(315): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
12-07 16:24:29.089: ERROR/AndroidRuntime(315): at ...

Clearly the Android OS wont let threads other than the main thread change UI elements.

But why?
Android UI toolkit, like many other UI environments, is not thread-safe.



The solution

  • A queue of messages. Each message is a job to be handled.
  • Threads can add messages.
  • Only a single thread pulls messages one by one from the queue.

The same solution was implemented in swing (Event dispatching thread
andSwingUtilities.invokeLater())



Handler
TheHandleris the middleman between a new thread and the message queue.


Option 1– Run the new thread and use the handler to send messages for ui changes

01 finalHandler myHandler =newHandler(){
02 @Override
03 publicvoidhandleMessage(Message msg) {
04 updateUI((String)msg.obj);
05 }
06
07 };
08
09 (newThread(newRunnable() {
10
11 @Override
12 publicvoidrun() {
13 Message msg = myHandler.obtainMessage();
14
15 msg.obj = doLongOperation();
16
17 myHandler.sendMessage(msg);
18 }
19 })).start();

* keep in mind that updating the UI should still be a short operation, since the UI freezes during the updating process.

Other possibilities:
handler.obtainMessage with parameters
handler.sendMessageAtFrontOfQueue()
handler.sendMessageAtTime()
handler.sendMessageDelayed()
handler.sendEmptyMessage()


Option 2– run the new thread and use the handler to post a runnable which updates the GUI.

01 finalHandler myHandler =newHandler();
02
03 (newThread(newRunnable() {
04
05 @Override
06 publicvoidrun() {
07 finalString res = doLongOperation();
08 myHandler.post(newRunnable() {
09
10 @Override
11 publicvoidrun() {
12 updateUI(res);
13 }
14 });
15 }
16 })).start();
17
18 }








Looper
If we want to dive a bit deeper into the android mechanism we have to understand what is aLooper.
We have talked about the message queue that the main thread pulls messages and runnables from it and executes them.
We also said that each handler you create has a reference to this queue.
What we haven’t said yet is that the main thread has a reference to an object named Looper.
The Looper gives the Thread the access to the message queue.
Only the main thread has executes to the Looper by default.

Lets say you would like to create a new thread and you also want to take advantage of the message queue functionality in that thread.

01 (newThread(newRunnable() {
02
03 @Override
04 publicvoidrun() {
05
06 innerHandler =newHandler();
07
08 Message message = innerHandler.obtainMessage();
09 innerHandler.dispatchMessage(message);
10 }
11 })).start();

Here we created a new thread which uses the handler to put a message in the messages queue.

This will be the result:
12-10 20:41:51.807: ERROR/AndroidRuntime(254): Uncaught handler: thread Thread-8 exiting due to uncaught exception
12-10 20:41:51.817: ERROR/AndroidRuntime(254): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
12-10 20:41:51.817: ERROR/AndroidRuntime(254): at android.os.Handler.(Handler.java:121)
12-10 20:41:51.817: ERROR/AndroidRuntime(254): at ...



The new created thread does not have a Looper with a queue attached to it. Only the UI thread has a Looper.
We can however create a Looper for the new thread.
In order to do that we need to use 2 functions:Looper.prepare()andLooper.loop().

01 (newThread(newRunnable() {
02
03 @Override
04 publicvoidrun() {
05
06 Looper.prepare();
07 innerHandler =newHandler();
08
09 Message message = innerHandler.obtainMessage();
10 innerHandler.dispatchMessage(message);
11 Looper.loop();
12 }
13 })).start();


If you use this option, don’t forget to use also the quit() function so the Looper will not loop for ever.

1 @Override
2 protectedvoidonDestroy() {
3 innerHandler.getLooper().quit();
4 super.onDestroy();
5 }




AsyncTask
I have explained to you that a Handler is the new thread’s way to communicate with the UI thread.
If while reading this you were thinking to yourself, isn’t there an easier way to do all of that… well, you know what?! There is.

Android team has created a class calledAsyncTaskwhich is in short a thread that can handle UI.

Just like in java you extend the class Thread and aSwingWorkerin Swing, in Android you extend the class AsyncTask.
There is no interface here like Runnable to implement I’m afraid.

01 classMyAsyncTaskextendsAsyncTask<Integer, String, Long> {
02
03 @Override
04 protectedLong doInBackground(Integer... params) {
05
06 longstart = System.currentTimeMillis();
07 for(Integer integer : params) {
08 publishProgress("start processing "+integer);
09 doLongOperation();
10 publishProgress("done processing "+integer);
11 }
12
13 returnstart - System.currentTimeMillis();
14 }
15
16
17
18 @Override
19 protectedvoidonProgressUpdate(String... values) {
20 updateUI(values[0]);
21

更多相关文章

  1. 代码中设置drawableleft
  2. android 3.0 隐藏 系统标题栏
  3. Android开发中activity切换动画的实现
  4. Android(安卓)学习 笔记_05. 文件下载
  5. Android中直播视频技术探究之—摄像头Camera视频源数据采集解析
  6. 技术博客汇总
  7. android 2.3 wifi (一)
  8. AndRoid Notification的清空和修改
  9. Android中的Chronometer

随机推荐

  1. 通过gae访问android官方文档
  2. Android设备屏幕问题
  3. Android Permission denied 错误
  4. android apk签名生成及配置
  5. android 飞行模式分析
  6. android 开发之电子钢琴 源码
  7. Android(安卓)中点击两次返回键才退出
  8. Android(安卓)源码 图形系统概述
  9. Android图文识别
  10. Android开发之数据保存技术(一)