Ⅰ android handler是不是唯一的
前段時間在工作的時候碰見一個問題,就是要待機時在設定的時間內執行操作,開始並沒有意識到問題的嚴重,後來嘗試很多辦法沒有成功,最後也是在網上找到解決辦法,在此稍作總結,希望能對大家有所幫助,不足之處還望大家指正。
1Android中的handler、timer、thread、在待機時都會停止運行,所設定的時間會在待機結束後繼續計算。所以如果想在Android待機時運行某些操作,使用以上幾種方法是不可行的。
2Android中有一個Alarmmanager對象,可以使用該對象執行待機時的操作。具體設置的方法如下:
2.1設置鬧鈴的類型
AlarmManager.RTC,硬體鬧鍾,不喚醒手機(也可能是其它設備)休眠;當手機休眠時不發射鬧鍾。
AlarmManager.RTC_WAKEUP,硬體鬧鍾,當鬧鍾發躰時喚醒手機休眠;
AlarmManager.ELAPSED_REALTIME,真實時間流逝鬧鍾,不喚醒手機休眠;當手機休眠時不發射鬧鍾。
AlarmManager.ELAPSED_REALTIME_WAKEUP,真實時間流逝鬧鍾,當鬧鍾發躰時喚醒手機休眠;
AlarmManager.POWER_OFF_WAKEUP:能喚醒系統,他是一種關機鬧鈴,就是說設備在關機狀態下也可以喚醒系統,所以我們把它稱為關機鬧鈴。
RTC鬧鍾和ELAPSED_REALTIME最大的差別就是前者可以通過修改手機時間觸發鬧鍾事件,後者要通過真實時間的流逝,即使在休眠狀態,時間也會被計算。
2.2設置鬧鈴的開始時間
如果使用ELAPSED_REALTIME或者ELAPSED_REALTIME_WAKEUP類型應該調用SystemClock.elapsedRealtime()獲取相對時間在加上你設定的延遲時間
如果使用RTC或者RTC_WAKEUP類型應該調用System.currentTimeMillis()獲取從1970.1.1號以來的時間在加上你設定的延遲時間
2.3pendingintent
一個PendingIntent對象,表示到時間後要執行的操作。PendingIntent與Intent類似,可以封裝Activity、BroadcastReceiver和Service。
但與Intent不同的是,PendingIntent可以脫離應用程序而存在。
接觸Android沒幾天,不太了解。
本來寫好的一個應用在無意中發現,待機的時候,應用中的一個線程停止了運行。
這個線程是每隔一分鍾上傳一個數據到伺服器上。
我當時測試的時候,沒想過待機(接開關鍵)下的情況是怎樣的,現在發現,只要手機一進入待機狀態,這個線程就停止工作了。
不過有一個奇怪的現象,因為我的應用中同時啟動了三個線程。
一個負責每隔一分鍾上傳一個數據,當待機的時候,這個線程暫停運行,當手機不待機的時候,馬上復活。
一個負責接收伺服器發過來的UDP數據包,這個線程倒是不受待機的影響,當有數據來的時候,可以正常處理。
難道是因為datagramSocket.receive(datagramPacket);阻塞的原因?
public void run()
{
while(true)
{
datagramSocket.receive(datagramPacket); //阻塞
}
}
到網上搜索了一下,看到別人說的:
實驗1:使用java.util.Timer
當連接USB線進行調試時,會發現一切工作正常,每5秒更新一次界面,即使是按下電源鍵,仍然會5秒觸發一次。
當拔掉USB線,按下電源鍵關閉屏幕後,過一段時間再打開,發現定時器明顯沒有繼續計數,停留在了關閉電源鍵時的數字。
實驗2:使用AlarmService:
2.1通過AlarmService每個5秒發送一個廣播,setRepeating時的類型為AlarmManager.ELAPSED_REALTIME。
拔掉USB線,按下電源鍵,過一段時間再次打開屏幕,發現定時器沒有繼續計數。
2.2setRepeating是的類型設置為AlarmManager.ELAPSED_REALTIME_WAKEUP
拔掉USB線,按下電源鍵,過一點時間再次打開屏幕,發現定時器一直在計數。
如此看來,使用WAKEUP才能保證自己想要的定時器一直工作,但是肯定會引起耗電量的增加。
我最後自已寫了一個Service類,然後使用AlarmService每隔一分鍾執行一次,在待機的時候也能正常運行。
Ⅱ android中handler如何使用
Handler在Android中主要是負責發送和處理消息。它的主要用途大致是下面兩個:
1)按計劃發送消息或執行某個Runnanble;
2)從其他線程中發送來的消息放入消息隊列中,避免線程沖突(常見於更新UI線程)
學寫一下,在UI線程中,系統已經有一個Activity來處理了,你可以再起若干個Handler來處理。在實例化Handler的時候,只要有Handler的指針,任何線程也都可以sendMessage。
Handler對於Message的處理是非同步處理的。一個Looper 只有處理完一條Message才會讀取下一條,所以消息的處理是阻塞形式的(handleMessage()方法里不應該有耗時操作,可以將耗時操作放在其他線程執行,操作完後發送Message(通過sendMessges方法),然後由handleMessage()更新UI)。
根據對視頻的學習寫了一個通過Button控制項啟動進度條(類似於下載等操作)的程序,簡單介紹一下,有兩個Button控制項,一個是開始,點擊之後會顯示一個進度條以每次十分之一的進度進行(一開始是隱藏的),另一個是停止,可以中斷進度。
java代碼:
1 package zzl.handleactivity;
2
3 import android.app.Activity;
4 import android.os.Bundle;
5 import android.os.Handler;
6 import android.os.Message;
7 import android.view.Gravity;
8 import android.view.View;
9 import android.view.View.OnClickListener;
10 import android.widget.Button;
11 import android.widget.ProgressBar;
12 import android.widget.Toast;
13
14 public class Handler_01 extends Activity {
15
16 //聲明變數
17 private Button startButton=null;
18 private Button endButton=null;
19 private ProgressBar firstBar=null;
20 private Toast toast=null;
21 @Override
22 protected void onCreate(Bundle savedInstanceState) {
23 super.onCreate(savedInstanceState);
24 setContentView(R.layout.main);
25
26 //根據ID獲取對象
27 startButton =(Button)findViewById(R.id.startButton);
28 endButton=(Button)findViewById(R.id.endButton);
29 firstBar=(ProgressBar)findViewById(R.id.firstBar);
30 //給對象設置動作監聽器
31 startButton.setOnClickListener(new StartButtonListener());
32 endButton.setOnClickListener(new EndButtonListener());
33 }
34
35 class StartButtonListener implements OnClickListener{
36
37 @Override
38 public void onClick(View v) {
39 // TODO Auto-generated method stub
40 //一開始執行,加入到消息隊列,不延遲,
41 //然後馬上執行run方法
42 firstBar.setVisibility(View.VISIBLE);
43 firstBar.setMax(100);
44 handler.post(upRunnable);
45 toast=Toast.makeText(Handler_01.this, "運行開始", Toast.LENGTH_SHORT);
46 toast.setGravity(Gravity.CENTER, 0, 0);
47 toast.show();
48 }
49 }
50 class EndButtonListener implements OnClickListener{
51
52 @Override
53 public void onClick(View v) {
54 // TODO Auto-generated method stub
55 //停止
56 handler.removeCallbacks(upRunnable);
57 System.out.println("It's time to stop...");
58 }
59 }
60
61 //創建handler對象,在調用post方法
62 //非同步消息處理:將下載或者處理大數據等等單獨放到另一個線程
63 //更好的用戶體驗
64 Handler handler=new Handler(){
65
66 @Override
67 public void handleMessage(Message msg){
68 firstBar.setProgress(msg.arg1);
69 firstBar.setSecondaryProgress(msg.arg1+10);
70 //handler.post(upRunnable);
71 if(msg.arg1<=100) {
72 handler.post(upRunnable); //將要執行的線程放入到隊列當中
73 }else {
74 handler.removeCallbacks(upRunnable);
75 }
76 }
77 };
78
79 //聲明線程類:實現Runnable的介面
80 Runnable upRunnable=new Runnable() {
81
82 int i=0;
83 @Override
84 public void run() {//程序的運行狀態
85 // TODO Auto-generated method stub
86 //postDelayed方法:把線程對象加入到消息隊列中
87 // 隔2000ms(延遲)
88 System.out.println("It's time to start...");
89 i=i+10;
90 //獲取Message消息對象
91 Message msg=handler.obtainMessage();
92 //將msg對象的arg1(還有arg2)對象的值設置
93 //使用這兩個變數傳遞消息優點:系統消耗性能較少
94 msg.arg1=i;
95 try{
96 //設置當前顯示睡眠1秒
97 Thread.sleep(1000);
98 }catch(InterruptedException e){
99 e.printStackTrace();
100 }
101 //將msg對象加入到消息隊列當中
102 handler.sendMessage(msg);
103 if(i==100){//當值滿足時,將線程對象從handle中剔除
104 handler.removeCallbacks(upRunnable);
105 firstBar.setVisibility(View.GONE);
106 //臨時彈出
107
108 toast=Toast.makeText(Handler_01.this, "運行結束", Toast.LENGTH_SHORT);
109 toast.setGravity(Gravity.CENTER, 0, 0);
110 toast.show();
111 }
112 }
113 };
114 }
main.xml
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:orientation="vertical"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent"
6 tools:context=".Handler_01" >
7
8 <ProgressBar
9 android:id="@+id/firstBar"
10 style="?android:attr/progressBarStyleHorizontal"
11 android:layout_width="200dp"
12 android:layout_height="wrap_content"
13 android:visibility="gone"/>
14
15 <Button
16 android:id="@+id/startButton"
17 android:layout_width="wrap_content"
18 android:layout_height="wrap_content"
19 android:text="@string/start" />
20
21 <Button
22 android:id="@+id/endButton"
23 android:layout_width="wrap_content"
24 android:layout_height="wrap_content"
25 android:text="@string/end" />
26
27 </LinearLayout>
總結:
1)當點擊開始或者運行結束的時候,都會通過調用Toas彈出臨時窗口,Toast.makeText(Handler_01.this, "運行結束", Toast.LENGTH_SHORT),這一句一開始總是執行出錯,原因在於必須調用它的show方法才可以顯示出來,還可以通過設置它的位置來顯示;
2)在xml中 android:text="@string/end",則需要在layout下的string文件中敲寫相應的代碼
3)原本代碼有一些瑕疵,就是沒有下面這一段代碼:
1 if(msg.arg1<=100) {
2 handler.post(upRunnable); //將要執行的線程放入到隊列當中
3 }else {
4 handler.removeCallbacks(upRunnable);
5 }
這樣就導致了upRunnable的run方法出現了死循環,這樣,雖然程序UI本身沒有問題,但是內部卻又很大的缺陷
這是因為
1 if(i==100){//當值滿足時,將線程對象從handle中剔除
2 handler.removeCallbacks(upRunnable);
3 firstBar.setVisibility(View.GONE);
4 toast=Toast.makeText(Handler_01.this, "運行結束", Toast.LENGTH_SHORT);
5 toast.setGravity(Gravity.CENTER, 0, 0);
6 toast.show();
7 }
這一段代碼看似是把upRunnable線程從線程對象隊列中移除,但是再次之前又前執行了handler.sendMessage(msg);這句代碼
從而導致下面的代碼又被執行到
1 public void handleMessage(Message msg){
2 firstBar.setProgress(msg.arg1);
3 firstBar.setSecondaryProgress(msg.arg1+10);
4
5 }
這樣肯定會使upRunnable線程重新加入到線程對象隊列中,updateThread的run方法重復執行,這就導致了死循環。所以必須加上之前的那段代碼,通過判斷來控制循環終止。並且run方法中的if(i==100)的那段代碼也是可以不用的,不過我是寫了一些其他代碼就懶得優化了,這是後話了。
4) 剛剛大家看到我們可以通過敲寫System.out.println在logcat中顯示,一開始eclipse編譯器中是沒有,這是如何顯示出來的?
大家可以再window的show view中找到logCat(deprecated)通過下圖中綠色的「+」添加出來
然後顯示內容的時候,選擇右上角「V D I W E 」的I就可以比較清晰的顯示出來了,當然你也可以選擇另外一個logCat來顯示,方法類似。
5)實際上,Handler在默認情況下,和調用它的Activity是處於同一個線程的。 上述Handler的使用示例中,雖然聲明了線程對象,但是在實際調用當中它並沒有調用線程的start()方法,而是直接調用當前線程的run()方法。
如果要實現調用另一個新的線程,只要注釋post方法,然後加上這樣兩段代碼即可: Thread t = new Thread(r); t.start();
Ⅲ Android中Handler的主要作用是什麼通俗點,初學。
Handler的使用主要是android中無法在主線程(即UI線程)中訪問網路、無法在子線程中訪問UI線程元素。
一般是在子線程中訪問網路,然後使用Handler發送message通知主線程處理UI更新操作
Ⅳ 能講講Android的Handler機制嗎
Android的Handler機制是通俗講為了互相發消息,一般是子線程給主線程發消息完成相應操作。
安卓中最常見的操作是子線程操作完事後得到數據想更新UI,安卓有規定不允許在子線程中刷新UI,所以Handler出現了。
使用和理解大致步驟。
創建全局Handler對象handler,然後在主線程中初始化它(一般在oncreate中),把它的handmessage裡面的方法重寫,這個方法是收到子線程發給它的消息後執行的邏輯。
在子線程中獲取數據,調用handler.sendmessage,把要發的消息放在message中。message會添加到Messagequue(消息隊列中,handler創建就帶的)。
3.對象handler被創建和初始化的時候,系統自動會啟動Handler.looper,也就是一個消息輪詢器,它不斷的去查看有沒有消息進入到Messagequue(消息隊列中),有就取出交給handler的handmessage去處理。//這段邏輯是系統自動執行,理解就行。*純手打,不騙人~~~
Ⅳ 如何生動形象的理解Android Handler消息處理機制
在一個Android 程序開始運行的時候,會單獨啟動一個Process。默認的情況下,所有這個程序中的Activity,Service,Content Provider,Broadcast Receiver(Android 4大組件)都會跑在這個Process。一個Android 程序默認情況下也只有一個Process,但一個Process下卻可以有許多個Thread。在這么多Thread當中,有一個Thread,稱之為UI Thread。UI Thread在Android程序運行的時候就被創建,是一個Process當中的主線程Main Thread,主要是負責控制UI界面的顯示、更新和控制項交互。在Android程序創建之初,一個Process呈現的是單線程模型,所有的任務都在一個線程中運行。因此,UI Thread所執行的每一個函數,所花費的時間都應該是越短越好。而其他比較費時的工作(訪問網路,下載數據,查詢資料庫等),都應該交由子線程去執行,以免阻塞主線程,導致ANR。那麼問題來了,UI 主線程和子線程是怎麼通信的呢。這就要提到這里要講的Handler機制。
簡單來說,handler機制被引入的目的就是為了實現線程間通信的。handler一共幹了兩件事:在子線程中發出message,在主線程中獲取、處理message。聽起來好像so easy,如果面試中讓闡述下Handler機制,這么回答顯然不是面試官想要的答案。忽略了一個最重要的問題:子線程何時發送message,主線程何時獲取處理message。
為了能讓主線程「適時」得處理子線程所發送的message,顯然只能通過回調的方式來實現——開發者只要重寫Handler類中處理消息的方法,當子線程發送消時,Handler類中處理消息的方法就會被自動回調。
Handler類包含如下方法用於發送處理消息
void handleMessage(Message msg):處理消息的方法,該方法通常用於被重寫。
final boolean hasMessage(int what):檢查消息隊列中是否包含what屬性為指定值的消息。
final boolean hasMessage(int what,Object object):檢查消息隊列中是否包含what屬性為指定值且object屬性為指定對象的消息。
sendEmptyMessage(int what)發送空消息。
sendEmptyMessageDelayed(int what,longdelayMillis);指定多少毫秒之後發送空消息。
sendMessage(Message msg)立即發送消息。
sendMessageDelayed(int what,longdelayMillis);指定多少毫秒之後發送消息。
藉助以上方法,就可以自由穿梭於主線程和子線程之中了。
到這里就結束了么?當然沒有。要講的東西才剛剛開始,要知道消息處理這件事,不是handler一個人在戰斗,android的消息處理有三個核心類:Handler,Looper,和Message。其實還有一個MessageQueue(消息隊列),但是Message Queue被封裝到Looper裡面了,不會直接與Message Queue打交道。
Looper的字面意思是「循環裝置」,它被設計用來使一個普通線程變成Looper線程。所謂Looper線程就是循環工作的線程。在程序開發中,經常會需要一個線程不斷循環,一旦有新任務則執行,執行完繼續等待下一個任務,這就是Looper線程。Looper是用於給一個線程添加一個消息隊列(MessageQueue),並且循環等待,當有消息時會喚起線程來處理消息的一個工具,直到線程結束為止。通常情況下不會用到Looper,因為對於Activity,Service等系統組件,Frameworks已經為初始化好了線程(俗稱的UI線程或主線程),在其內含有一個Looper,和由Looper創建的消息隊列,所以主線程會一直運行,處理用戶事件,直到某些事件(BACK)退出。
如果,需要新建一個線程,並且這個線程要能夠循環處理其他線程發來的消息事件,或者需要長期與其他線程進行復雜的交互,這時就需要用到Looper來給線程建立消息隊列。
使用Looper也非常的簡單,它的方法比較少,最主要的有四個:
public static prepare();為線程初始化消息隊列。
public static myLooper();獲取此Looper對象的引用
public static loop();讓線程的消息隊列開始運行,可以接收消息了。
public void quit();退出具體哪個Looper
在整個消息處理機制中,message又叫task,封裝了任務攜帶的信息和處理該任務的handler,這個很好理解,就不做介紹了。
說了這么多,一定沒聽太明白。沒關系,再對Handler運行機製做個總結:
子線程(無looper)借用主線程(有looper)里的Hander發送一條message到主線程,這個message會被主線程放入message queue,主線程裡面有一個looper,在輪詢message queue的時候發現有一條message。調用handler消息處理者,執行handlemessage方法,去處理這個message,就可以在handlemessage的方法裡面更新ui。好像畫面感不是太強,舉個例子吧。試想有一個生產方便麵的車間,這個車間有兩條生產線,一條是生產面餅的,一條是生產調料包的,面餅的生產線比較先進一點,配備了一個工人叫handler,還配備了一個調料包分類循環器。這個車間能生產很多種方便麵,有老壇酸菜,有泡椒鳳爪,有香菇燉雞,有紅燒牛肉等等。其中方便麵的面餅都是一樣的,只有調料包和包裝袋不一樣,包裝袋是根據調料包來定的。那麼生產線運作起來了:工人Handler把子生產線(子線程)上的調料包(message)放到了主生產線(主線程)上的調料包分類循環器(Looper)上,通過輪詢分類篩選後工人Handler確定這是一包合格的老壇酸菜味調料包,於是工人把老壇酸菜調料包和面餅放在一塊(sendmessage),告訴主生產線,這是一包老壇酸菜方便麵,請給這包方便麵包一個老壇酸菜的包裝袋(更新UI).
Ⅵ 求ANDROID中handler的問題
Handler不等於使用線程。
傳統的UI架構,如swing。將繪制展示、事件派發都放在主線程(UI)線程中進行。
UI線程的實現模型通常是一個死循環,不斷接受Message。組織派發
android中Handler-MessageQueue-Looper,三者構成了這種死循環+消息通信的模型。此處的postDelayed實際上是將一個Runnable任務投入了MessageQueue中,並期望在3000毫秒後執行。
另外不要誤以為Runnable是線程。在java.util.concurrent中,Dogn大神已經明確將執行過程與任務分割出來。Runnable介面只是表示一項任務,既可以同步執行,也可以在新線程中執行。
為何需要Handler而不用Thread。除了消息模型是UI框架的經典模式外,還涉及到UI組件不允許跨線程訪問的限制。無論是.NET也好,swing也好,android也好,不允許在非UI線程中操作這一點都一樣。
Handler便是android框架中非同步線程代碼到達同步線程的官方通道。從另一個角度說,這種基於消息模型的通信模式有時也很有用。相關的例子有IntentService,HandlerThread等。
Ⅶ 我用Android studio開發老是遇到一些方法不能用,比如這個handler.sendMes
背景模糊,要麼自己模糊處理,要麼直接讓UI提供這張圖片,建議直接用圖片其他的功能都很簡單了,直接XML寫布局EditText跟Button就好了
Ⅷ android中handle和線程的關系是什麼
作者:李板溪
鏈接:http://www.hu.com/question/24766848/answer/53037579
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請註明出處。
問題背景,假設你要下載一張美女圖顯示出來。 使用這個問題就可以說明主要的問題了。
好了 上代碼,下載美女圖片,然後顯示在 ImageView 中。 代碼如下:
public class MainActivity extends AppCompatActivity {
public static final String beautyUrl = "http://ww3.sinaimg.cn/large/.jpg";
ImageView mBeautyImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBeautyImageView = (ImageView)findViewById(R.id.beauty);
mBeautyImageView.setImageBitmap(downloadImage(beautyUrl));
}
@Nullable
public Bitmap downloadImage(String urlString){
try {
final URL url = new URL(urlString);
try(InputStream is = url.openStream()){
return BitmapFactory.decodeStream(is);
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
然後這樣的一段看似沒有問題的代碼,在 Android 3 以上是會直接報錯的。 主要錯誤原因在
Caused by: android.os.NetworkOnMainThreadException at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1147)
為了保證用戶體驗, Android 在 3.0 之後,就不允許在 主線程(MainThread)即 UI線程 中執行網路請求了。 那怎麼辦呢?
好吧,我們暫不考試 Android 提供的一系統組件及工具類, 用純 Java 的方式來解決這個問題。
在新的線程中下載顯示圖片
在新創建的線程中下載顯示圖片如下:
new Thread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(downloadImage(beautyUrl));
}
}).start();
看起來來錯的樣子,跑起來看看。 啊,又報錯了。
android.view.ViewRootImpl$: Only the original thread that created a view hierarchy can touch its views.
說只能在創建View 層級結構的線程中修改它。 創建它的線程就是主線程。 在別的線程不能修改。 那怎麼辦?
那現在我們遇到的問題是: 下載不能在主線程下載。 更新ImageView 一定要在 主線程中進行。 在這樣的限制下,我們自然而然想去一個解決辦法: 就是在新創建的線程中下載。 下載完成在主線程中更新 ImageView。
但是,怎麼在下載完成之後,將圖片傳遞給主線程呢?這就是我們問題的關鍵了。
線程間通信
我們的通信要求,當下載線程中下載完成時,通知主線程下載已經完成,請在主線程中設置圖片。 純 Java 的實現上面的線程間通信的辦法我暫沒有找到,於是我想到使用 FutureTask 來實現在主線程中等待圖片下載完成,然後再設置。
FutureTask<Bitmap> bitmapFutureTask = new FutureTask<>(new Callable<Bitmap>() {
@Override
public Bitmap call() throws Exception {
return downloadImage(beautyUrl);
}
});
new Thread(bitmapFutureTask).start();
try {
Bitmap bitmap = bitmapFutureTask.get();
mBeautyImageView.setImageBitmap(bitmap);
} catch (InterruptedException |ExecutionException e) {
e.printStackTrace();
}
不過這雖然騙過了 Android 系統,但是雖然系統阻塞的現象沒有解決。 例如我在 get() 方法前後設置了輸出語句:
Log.i(TAG,"Waiting Bitmap");
Bitmap bitmap = bitmapFutureTask.get();
Log.i(TAG,"Finished download Bitmap");
設置了下載時間至少 5 秒鍾之後,輸出如下:
06-27 23:30:18.058 21298-21298/com.banxi1988.androiditc I/MainActivity﹕ Waiting Bitmap 06-27 23:30:23.393 21298-21298/com.banxi1988.androiditc I/MainActivity﹕ Finished download Bitmap
讓主線程什麼事做不做,就在那傻等了半天。然後由於 onCreate沒有返回。用戶也就還沒有看到界面。 導致說應用半天啟動不了。。卡死了。。
查閱了一些資料,沒有不用 Android 的框架層的東西而實現在其他進程執行指定代碼的。
而 Android 中線程間通信就得用到 android.os.Handler 類了。 每一個 Handler 都與一個線程相關聯。 而 Handler 的主要功能之一便是: 在另一個線程上安插一個需要執行的任務。
這樣我的問題就通過一個 Handler 來解決了。
於是 onCreate 中的相關代碼變成如下了:
final Handler mainThreadHandler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
}
}).start();
看起來很酷的樣子嘛,一層套一層的 Runnable. mainThreadHandler 因為是在 主線程中創建的, 而 Handler創建時,綁定到當前線程。 所以 mainThreadHandler 綁定到主線程中了。
當然 Android 為了方便你在向主線程中安排進操作,在 Activity類中提供了 runOnUiThread 方法。 於是上面的代碼簡化為:
new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
runOnUiThread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
}
}).start();
你不用自己創建一個 Handler了。
而 runOnUiThread 的具體實現,也跟我們做得差不多。UI線程即主線程。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
Handler 與 Loop 簡介
上面說到 每一個 Handler 都與一個線程相綁定。 實際上是,通過 Handler 與 Loop 綁定,而每一個 Loop 都與一個線程想綁定的。 比如 Handler 中兩個主要構造函數大概如下:
public Handler(...){
mLooper = Looper.myLooper();
// ...
}
public Handler(Looper looper,...){
mLooper = looper;
// ...
}
public static Looper myLooper() {
return sThreadLocal.get();
}
然後 Looper 的構造函數如下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
綁定了當前的線程和生成了一個消息隊列。
值得提起的一點是, Looper 類保持了對 應用的主線程的 Looper 對象的靜態應用。
private static Looper sMainLooper; // guarded by Looper.class
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
這樣就可以方便你在其他線程中,使用一個綁定到主線程的 Handler,從而方便向主線程安插任務。 例如一般的圖片處理庫即是如此。這樣你只要指定一個 圖片的 url,及要更新的ImageView 即可。 如 Picasso 庫可以用如下代碼的讀取並更新圖片。 Picasso.with(context).load(url).into(imageView);
然後 Handler 可以做的事情還有很多。 因為它後面有 Looper 有 MessageQueue。可以深入了解下。
談一下線程池與 AsyncTask 類
Android 早期便有這個便利的類來讓我們方便的處理 工作線程及主線程的交互及通信。 但是現在先思考一下,我們上面的代碼可能遇到的問題。 比如,我們現在要顯示一個圖片列表。 一百多張圖片。 如果每下載一張就開一個線程的話,那一百多個線程,那系統資源估計支持不住。特別是低端的手機。
正確的做法是使用一個線程池。
final ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
runOnUiThread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
executor.shutdown();
}
});
由於我們是在在一個局部方法中使用了一個線程池。所以處理完了之後應該將線程停止掉。 而我們上面只有一個線程,所以直接在下載完成之後,調用 executor停掉線程池。 那如果執行了多個圖片的下載請求。需要怎麼做呢? 那就要等他們都完成時,再停止掉線程池。 不過這樣用一次就停一次還是挺浪費資源的。不過我們可以自己保持一個應用級的線程池。 不過這就麻煩不少。
然後 Android 早已經幫我們想法了這一點了。 我們直接使用 AsyncTask 類即可。
於是我們下載圖片並顯示圖片的代碼如下:
new AsyncTask<String,Void,Bitmap>(){
@Override
protected Bitmap doInBackground(String... params) {
return downloadImage(params[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
mBeautyImageView.setImageBitmap(bitmap);
}
}.execute(beautyUrl);
相比之前的代碼簡潔不少。
看一下 AsyncTask 的源代碼,正是集合我們之前考慮的這些東西。
一個全局的線程池
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
一個綁定主線程的 Handler ,在線程中處理傳遞的消息
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
小結
Android 提供的 Looper 和 Handler 可以讓我們非常方便的在各線程中安排可執行的任務
Ⅸ 安卓 handle和callback的區別
android.os.Handler Handler在android里負責發送和處理消息。它的主要用途有: 1)按計劃發送消息或執行某個Runnanble(使用POST方法); 2)從其他線程中發送來的消息放入消息隊列中,避免線程沖突(常見於更新UI線程) 默認情況下,Handler接受的是當前線程下的消息循環實例(使用Handler(Looper looper)、Handler(Looper looper, Handler.Callback callback)可以指定線程),同時一個消息隊列可以被當前線程中的多個對象進行分發、處理(在UI線程中,系統已經有一個Activity來處理了,你可以再起若干個Handler來處理)。在實例化Handler的時候,Looper可以是任意線程的,只要有Handler的指針,任何線程也都可以sendMessage。Handler對於Message的處理不是並發的。一個Looper 只有處理完一條Message才會讀取下一條,所以消息的處理是阻塞形式的(handleMessage()方法里不應該有耗時操作,可以將耗時操作放在其他線程執行,操作完後發送Message(通過sendMessges方法),然後由handleMessage()更新UI)。 android中Handle類的用法 當我們在處理下載或是其他需要長時間執行的任務時,如果直接把處理函數放Activity的OnCreate或是OnStart中,會導致執行過程中整個Activity無響應,如果時間過長,程序還會掛掉。Handler就是把這些功能放到一個單獨的線程里執行,與Activity互不影響。 當用戶點擊一個按鈕時如果執行的是一個常耗時操作的話,處理不好會導致系統假死,用戶體驗很差,而Android則更進一步,如果任意一個Acitivity沒有響應5秒鍾以上就會被強制關閉,因此我們需要另外起動一個線程來處理長耗時操作,而主線程則不受其影響,在耗時操作完結發送消息給主線程,主線程再做相應處理。那麼線程之間的消息傳遞和非同步處理用的就是Handler。 加群討論研究 QQ群:56839077
Ⅹ Android中的Handler詳解以及和Thread的區別
一、Handler的定義:
主要接受子線程發送的數據, 並用此數據配合主線程更新UI.
解釋: 當應用程序啟動時,Android首先會開啟一個主線程 (也就是UI線程) , 主線程為管理界面中的UI控制項,進行事件分發, 比如說, 你要是點擊一個 Button ,Android會分發事件到Button上,來響應你的操作。 如果此時需要一個耗時的操作,例如: 聯網讀取數據, 或者讀取本地較大的一個文件的時候,你不能把這些操作放在主線程中,,如果你放在主線程中的話,界面會出現假死現象, 如果5秒鍾還沒有完成的話,,會收到Android系統的一個錯誤提示 "強制關閉". 這個時候我們需要把這些耗時的操作,放在一個子線程中,因為子線程涉及到UI更新,,Android主線程是線程不安全的,也就是說,更新UI只能在主線程中更新,子線程中操作是危險的. 這個時候,Handler就出現了.,來解決這個復雜的問題 , 由於Handler運行在主線程中(UI線程中), 它與子線程可以通過Message對象來傳遞數據, 這個時候,Handler就承擔著接受子線程傳過來的(子線程用sedMessage()方法傳弟)Message對象,(裡麵包含數據) , 把這些消息放入主線程隊列中,配合主線程進行更新UI。
二、Handler一些特點
handler可以分發Message對象和Runnable對象到主線程中, 每個Handler實例,都會綁定到創建他的線程中(一般是位於主線程),
它有兩個作用: (1): 安排消息或Runnable 在某個主線程中某個地方執行, (2)安排一個動作在不同的線程中執行
Handler中分發消息的一些方法
[html] view plain
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
以上post類方法允許你排列一個Runnable對象到主線程隊列中,
sendMessage類方法, 允許你安排一個帶數據的Message對象到隊列中,等待更新.
三、Handler實例
(1) 子類需要繼承Hendler類,並重寫handleMessage(Message msg) 方法, 用於接受線程數據
以下為一個實例,它實現的功能為 : 通過線程修改界面Button的內容
[html] view plain
public class MyHandlerActivity extends Activity {
Button button;
MyHandler myHandler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.handlertest);
button = (Button) findViewById(R.id.button);
myHandler = new MyHandler();
// 當創建一個新的Handler實例時, 它會綁定到當前線程和消息的隊列中,開始分發數據
// Handler有兩個作用, (1) : 定時執行Message和Runnalbe 對象
// (2): 讓一個動作,在不同的線程中執行.
// 它安排消息,用以下方法
// post(Runnable)
// postAtTime(Runnable,long)
// postDelayed(Runnable,long)
// sendEmptyMessage(int)
// sendMessage(Message);
// sendMessageAtTime(Message,long)
// sendMessageDelayed(Message,long)
// 以上方法以 post開頭的允許你處理Runnable對象
//sendMessage()允許你處理Message對象(Message里可以包含數據,)
MyThread m = new MyThread();
new Thread(m).start();
}
/**
* 接受消息,處理消息 ,此Handler會與當前主線程一塊運行
* */
class MyHandler extends Handler {
public MyHandler() {
}
public MyHandler(Looper L) {
super(L);
}
// 子類必須重寫此方法,接受數據
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
Log.d("MyHandler", "handleMessage......");
super.handleMessage(msg);
// 此處可以更新UI
Bundle b = msg.getData();
String color = b.getString("color");
MyHandlerActivity.this.button.append(color);
}
}
class MyThread implements Runnable {
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.d("thread.......", "mThread........");
Message msg = new Message();
Bundle b = new Bundle();// 存放數據
b.putString("color", "我的");
msg.setData(b);
MyHandlerActivity.this.myHandler.sendMessage(msg); // 向Handler發送消息,更新UI
}
}
}
例外一個案例:
[html] view plain
package com.example.span.view;
import java.util.LinkedList;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Toast;
import com.example.span.view.domain.Block;
/**
* 2013-6-6 上午9:24:58
*
* @author 喬曉松
*/
public class GameView extends View {
public static boolean flag = true;
public static Block block;
public Handler handler;
public static int dir = 2;
public static final int DIRTOP = -1;
public static final int DIRLEFT = -2;
public static final int DIRDOWN = 1;
public static final int DIRRIGHT = 2;
public static int descount = 2;
public Canvas canvas;
public static Food food;
public LinkedList<Point> points = new LinkedList<Point>();
public LinkedList<Point> getPoints() {
return points;
}
public void setPoints(LinkedList<Point> points) {
this.points = points;
}
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
block = new Block(this);
for (int i = 0; i < 3; i++) {
Point point = new Point(block.getCx(), block.getCy());
block.setCx(block.getCx() - 20);
points.addLast(point);
}
food = new Food(this);
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case DIRLEFT:
if (msg.what + descount != 0) {
descount = -2;
block.moveLeft();
} else {
block.moveRight();
}
break;
case DIRRIGHT:
if (msg.what + descount != 0) {
descount = 2;
block.moveRight();
} else {
block.moveLeft();
}
break;
case DIRTOP:
if (msg.what + descount != 0) {
descount = -1;
block.giveUp();
} else {
block.downLoad();
}
break;
case DIRDOWN:
if (msg.what + descount != 0) {
descount = 1;
block.downLoad();
} else {
block.giveUp();
}
break;
case -3:
Toast.makeText(getContext(), "Game Over", Toast.LENGTH_LONG)
.show();
new AlertDialog.Builder(getContext())
.setTitle("游戲提示")
.setMessage("Game Over")
.setPositiveButton("退出",
new DialogInterface.OnClickListener() {
@Override
public void onClick(
DialogInterface dialog,
int which) {
Thread.currentThread().stop();
}
})
.setNegativeButton("返回菜單",
new DialogInterface.OnClickListener() {
@Override
public void onClick(
DialogInterface dialog,
int which) {
Intent intent = new Intent();
intent.setAction("android.intent.action.MAI");
intent.addCategory("android.intent.category.LAUNCHER");
intent.setFlags(0x10200000);
intent.setComponent(new ComponentName(
"com.example.span",
"com.example.span.SpanActivity"));
getContext().startActivity(intent);
}
}).show();
break;
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
while (flag) {
try {
Thread.sleep(500);
handler.sendEmptyMessage(dir);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
handler.sendEmptyMessage(-3);
}
}).start();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
System.out.println(keyCode);
return super.onKeyDown(keyCode, event);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
this.canvas = canvas;
food.chsw(canvas);
/*
* if (descount == 2) { chsw(); } else { if (points.contains(new
* Point(40, 40))) { System.out.println("吃掉食物了,,.."); } else { chsw(); }
* }