Android多线程机制

我们为什么需要多线程在Android应用程序吗?
让我们说你想做一个非常长的操作,当用户按下一个按钮。
如果你不使用另一个线程,看上去就像这样:


((Button)findViewById(R.id.Button01)).setOnClickListener(          
     new OnClickListener() {
         @Override
         public void onClick(View v) {
            int result = doLongOperation();
            updateUI(result);
         }


      });


将会发生什么?
可能大家都知道了,UI冻结。这是一个非常糟糕的UI体验。该计划甚至可能崩溃。






这个问题在使用线程在一个UI环境

那么将会发生什么如果我们用一个线程对一个长时间运行的操作。

让我们看一个简单的例子:

((Button)findViewById(R.id.Button01)).setOnClickListener(
02 new OnClickListener() {
03           
04          @Override
05          public void onClick(View v) {
06              
07             (new Thread(new Runnable() {
08                 
09                @Override
10                public void run() {
11                     int result = doLongOperation();
12                     updateUI(result);
13                }
14             })).start();
15              
16          }

在本例中,结果是,应用程序崩溃。

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 ...


显然安卓系统不让主线程的线程改变UI元素。

为什么了?

Android UI toolkit,像许多其他用户界面环境,不是线程安全的。

解决方法:

1:一个队列的消息。每个消息处理的工作。

2:线程可以添加信息。

3:只有一个线程将消息从队列中一个接一个。


Handler

这个handler处理线程和消息队列的中间人。
方案一:
运行新线程和使用处理程序发送消息ui更改
01 final Handler myHandler = new Handler(){
02    @Override
03 public void handleMessage(Message msg) {
04       updateUI((String)msg.obj);
05 }
06     
07 };
08  
09 (new Thread(new Runnable() {
10     
11    @Override
12    public void run() {
13       Message msg = myHandler.obtainMessage();
14        
15       msg.obj = doLongOperation();
16        
17       myHandler.sendMessage(msg);
18    }
19 })).start();
注意:更新UI应该仍然是一个短的操作,因为UI冻结在更新过程。
其他的可能性:
handler.obtainMessage with parameters
handler.sendMessageAtFrontOfQueue()
handler.sendMessageAtTime()
handler.sendMessageDelayed()
handler.sendEmptyMessage()

方案二:
运行新线程和使用处理程序来发布一个可运行更新GUI。
01 final Handler myHandler = new Handler();
02              
03             (new Thread(new Runnable() {
04                 
05                @Override
06                public void run() {
07                   final String res = doLongOperation();
08                   myHandler.post(new Runnable() {
09                       
10                      @Override
11                      public void run() {
12                         updateUI(res);
13                      }
14                   });
15                }
16             })).start();
17              
18          }


可看Android Runnable 运行在那个线程

Looper

如果我们想要较深入地介绍了android机制我们必须了解什么是Looper。

我们已经谈过了,主要的线程消息队列提取消息,并执行他们。

我们知道,每个处理程序创建引用此队列。

我们还没说但就是主要的线程对象的引用命名Looper。

Looper给线程执行消息队列。

只有主要的线程执行默认的Looper。创建一个新的线程,用消息队列的功能在这个线程上。

01 (new Thread(new Runnable() {
02  
03                   @Override
04                   public void run() {
05  
06                      innerHandler = new Handler();
07                       
08                      Message message = innerHandler.obtainMessage();
09                      innerHandler.dispatchMessage(message);
10                   }
11                })).start();
这里我们创建一个新的线程,使用handler处理程序将消息在消息队列。

运行的结果:

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 ...

新创建的线程没有Looper和队列连接到它。只有Ui线程有Looper。

但是我们也可以创建Looper给这个新的线程。

为了做到这一点,我们需要使用2功能: Looper.prepare() andLooper.loop().

01 (new Thread(new Runnable() {
02  
03                @Override
04                public void run() {
05  
06                   Looper.prepare();
07                   innerHandler = new Handler();
08                          
09                   Message message = innerHandler.obtainMessage();
10                   innerHandler.dispatchMessage(message);
11                   Looper.loop();
12                }
13             })).start();

如果我们使用了这样操作,不要忘记使用也quit()函数所以Looper不会永远循环。

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

AsyncTask

Android已经创建了一个类称为AsyncTask,总之一个线程能够处理UI。

01 class MyAsyncTask extends AsyncTask<Integer, String, Long> {
02        
03       @Override
04       protected Long doInBackground(Integer... params) {
05           
06          long start = System.currentTimeMillis();
07          for (Integer integer : params) {
08             publishProgress("start processing "+integer);
09             doLongOperation();
10             publishProgress("done processing "+integer);
11          }
12           
13          return start - System.currentTimeMillis();
14       }
15  
16        
17        
18       @Override
19       protected void onProgressUpdate(String... values) {
20          updateUI(values[0]);
21       }
22        
23       @Override
24       protected void onPostExecute(Long time) {
25          updateUI("Done with all the operations, it took:"+
26                                      time + " millisecondes");
27       }
28  
29       @Override
30       protected void onPreExecute() {
31          updateUI("Starting process");
32       }
33        
34        
35       public void doLongOperation() {
36           
37          try {
38             Thread.sleep(1000);
39          catch (InterruptedException e) {
40             e.printStackTrace();
41          }
42           
43       }
44        
45     }

像这样执行线程:

new MyAsyncTask().excute();

AsyncTask提供了方法来处理

1. onPreExecute()在执行前被调用。

2,doInBackground()主要的操作

3,onPostExecute()后才调用执行。

Android开发者网站也提到这四个规则对于AsyncTask:
1:必须创建任务实例在UI线程上。

2:execute(Params…)必须在UI线程上调用。

3:不要手动唤醒onPreExecute(),onPostExecute(result),InBackground(Params…),。

4:这个任务可以只执行一次(如果第二个执行是企图将抛出一个异常)。


Timer + TimerTask

另一个选择是使用一个计时器。

定时器是一个舒适的方式分派线程在未来,无论是一次或更多。

不要这样做:

01 Runnable threadTask = new Runnable() {
02     
03    @Override
04    public void run() {
05        
06       while(true){
07          try {
08             Thread.sleep(2000);
09          catch (InterruptedException e) {
10             e.printStackTrace();
11          }
12           
13          doSomething();
14       }
15    }
16 };
17     
18 (new Thread(threadTask)).start();
而是要这样做:

1 TimerTask timerTask = new TimerTask() {     
2    @Override
3    public void run() {
4       doSomething();      
5    }
6 };
7  
8 Timer timer = new Timer();
9 timer.schedule(timerTask, 2000,2000);



参考资料:http://www.aviyehuda.com/blog/2010/12/20/android-multithreading-in-a-ui-environment/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值