在android中,有很多功能是不能放在onCreate或者onStart方法里面,因為這些功能相對
來說費時比較長,比如說下載一個文件,下載的過程比較長,但是如果寫在Activity中,
那么這段時間Activity是完全沒有響應的,那么就可以將這種處理大量數據或者耗時比較
長的東西放在一個單獨的線程中來完成,即Activity是一個線程,而下載的是在另外一個
線程,那么這樣就可以使得下載跟Activity之間互不影響,從而得到了良好的用戶體驗
這里有兩種隊列,一種是線程隊列,就是用postXX方法或者removeCallbacks方法對線程對象的操作。另一種是消息隊列,用sendMessage和handleMessage方法來對消息對象進行處理
handler采用的是一個消息隊列的方式,每一個handler都有一個與之關聯的消息隊列,而且是先進先出的方式執行,即:每次加入一個handler,然后拿出來,對其進行處理,然后再拿出另一個,再進行處理
例子一:這個例子僅僅是對線程對象進行操作的測試
運行結果如下:
程序解釋:首先創建一個Handler對象,然后創建一個繼承自Runnable接口的線程
程序首先點擊按鈕“開始”,于是會馬上執行post方法,將執行的線程對象添加到線程隊列中,這時會馬上執行
然后,執行postDelayed方法,由于里面設置的間隔時間,所以每3秒會調價一個handler對象到線程隊列中,并且一直執行,直到點擊“結束”按鈕,調用removeCallbacks方法將其從線程隊列中移除
例子2:下面的例子將簡單的對線程對象和消息對象進行處理
main.xml
運行結果:
程序說明:
1、當點擊按鈕后,會執行按鈕的onClick方法中的
將進度條顯示出來,并且將線程對象加入到線程隊列中
2、線程對象對先打印出一個“開始線程”,然后i的值增加10,然后從系統中獲取一個Message對象
3、將i賦給Message對象的參數arg1
4、當前線程休眠5秒,然后通過sendMessage方法發送一個Message對象發送到消息隊列中
5、然后再執行,通過handleMessage方法設置進度條的值,并且將其加入到進程隊列中
6、循環執行,直到i=100,進度條隱藏,并將線程對象從線程隊列中取出
對于Handler來說,它和與它調用它的Activity是出于同一線程的,上一篇并沒有調用線程
的start方法,而是直接執行的run方法。而啟動一個線程是調用的start方法
上一篇博客里的對Handler的調用時通過Runnable接口來實現的,并且是通過run()方法來啟動那個線程的,而且是Activity和 Handler是兩個線程獨立運行的,互補干擾,但是實際情況確實,Activity所在的線程和Handler的線程是同一個線程,下面進行一下實驗
運行結果:
證明是同一個線程的兩個依據:
①Activity的id或name和Handler的id或name是同樣的
②我設置了
handler.post(r);
setContentView(R.layout.main);
也就是,如果執行后馬上顯示文本信息,那么可以證明它們不在同一個線程,但是實際情況是要先執行了handler后5秒,才顯示文本信息,說明它們在同一線程
如果將代碼改為
再次執行,運行結果如下,通過start啟動線程,它們不在同一個線程中
----------------------------------------------------------------------------------------------------------------
Looper即循環的從隊列當中取得消息的功能,如果在線程中使用Looper
那么,就會循環的從線程隊列當中取得消息并處理,如果隊列當中沒有消息的話
,線程就進入了休眠狀態
Looper很少自己創建,在Android中給出了HandlerThread類,并且具有循環取得并處理消息的功能
下面來實現這種Activity和Handler分別在兩個線程中執行,實現真正的異步處理
運行結果:
可以看到,Activity和Handler是在兩個不同的線程中執行的,這樣就是實現了真正的異步處理
1、首先創建一個HandlerThread對象,這個HandlerThread類實現了循環的取得消息并處理
2、用start方法啟動一個新線程
3、創建MyHandler類,里面傳遞的參數即Looper方法所獲得的可以循環在隊列中取得的消息
4、MyHandler類調用的是帶參數Looper的構造方法,并且實現了handlerMessage方法
5、獲取一個Message對象
6、將這個對象發送到生成該msg對象的handler對象,從而執行了handleMessage方法
-----------------------------------------------------------------------------------------------------
最后,將說一下Message里傳送的數據的使用,這里的msg對象可以使用arg1,arg2或者obj
arg1 and arg2 are lower-cost alternatives to using setData()
if you only need to store a few integer values. 也就是相對于setData()方法,如果你僅僅保存一些簡單的整形數的話,arg1,arg2對資源的要求較低,而setData()方法一般用于傳遞 大量數據的時候會用到
如果是msg.obj,那么可以這樣用
msg.obj = "Welcome to china";
然后在handleMessage()方法中用
String str = (String)msg.obj;來獲得傳遞的值
如果使用getData()方法的話,需要用到Bundle對象來傳遞,下面用個例子來說明
上面的代碼用來設置要傳遞的數據
下面的代碼用來獲取Bundle傳遞過來的數據并且用Toast來顯示
package org.hualang.handlertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;
public class HandlerTest4 extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.d("System.out","Activity所在線程的id:"+Thread.currentThread().getId());
/**
* 生成一個HandlerThread對象,實現了使用Looper來處理消息隊列的功能
* 這個類由Android應用程序框架提供
*/
HandlerThread handlerThread = new HandlerThread("handlerThread");
/**
* 使用HandlerThread的getLooper()方法之前,必須先調用該類的start()方法,否則是個null,會報錯
*/
handlerThread.start();
MyHandler handler = new MyHandler(handlerThread.getLooper());
Message msg = handler.obtainMessage();
/**
* 將Message對象發送到目標對象
* 所謂的目標對象,就是生成該msg對象的handler對象
*/
//msg.obj = "Hello world";
Bundle b = new Bundle();
b.putInt("age", 22);
b.putString("name", "loulijun");
msg.setData(b);
msg.sendToTarget();
}
class MyHandler extends Handler
{
public MyHandler()
{
}
public MyHandler(Looper looper)
{
super(looper);
}
public void handleMessage(Message msg)
{
//String str = (String)msg.obj
Bundle b = msg.getData();
int age = b.getInt("age");
String name = b.getString("name");
Toast toast = Toast.makeText(getApplicationContext(), "age="+age+"name="+name, Toast.LENGTH_LONG);
toast.show();
Log.d("System.out", "handler所在線程的id:"+Thread.currentThread().getId());
}
}
}
運行結果: