Android中handler的使用及原理---学习笔记
Handler類的基本介紹以及使用:
Android中UI操作是線程不安全的操作,如果有多個線程并發操作UI組件,就會出現線程安全問題,所以Android中制定了一個規則:在Android中只允許主線程(UI線程)修改Activity中的UI組件
但是現在問題又來了,在開發中我們會需要在子線程中更新UI組件的情況,那怎么進行處理呢?其實Handler就是為了解決這種問題而生的。
Handler類的主要作用有兩個:
1.在新啟動的線程中發送消息
2.在主線程中獲取,處理消息
Handler類中用于發送、處理消息的方法:
1. void handleMessage(Message msg):處理消息的方法。該方法通常用于被重寫
2. boolean hasMessages(int what):檢查消息隊列中是否包含what屬性為指定值得消息
3. boolean hasMeesages(int what,Object obj):檢查消息隊列中時候包含what屬性為指定值且object屬性為指定對象的消息
4. 多個重載的Message obtainMessage():獲取消息
5. sendEmptyMessage(int what):發送空消息
6. boolean sendEmptyMessageDelayed(int what,long delayMillis):指定多少毫秒之后發送空消息
7. boolean sendMessage(Message msg):立即發送消息
8. boolean sendMessageDelayed(Message msg,long delayMillis):指定多少毫秒之后發送消息
下面是一個使用Handler類更新UI組件的一個使用小例子:
package com.my.Mytimetest;import java.util.Timer; import java.util.TimerTask;import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.ImageView;public class MainActivity extends Activity {private ImageView imageview;private int[] images = {R.drawable.image1,R.drawable.image2,R.drawable.image3,R.drawable.image4};private int index;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);imageview = (ImageView) findViewById(R.id.id_images);final Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {if(msg.arg1==123){index++;imageview.setImageResource(images[index%4]);}}};//定義一個計時器,設置為延遲0ms后執行,每隔1s執行一次(這里如果設置為 timer.schedule(task,1000)表示執行一次)new Timer().schedule(new TimerTask(){@Overridepublic void run() {Message message = new Message();message.arg1 = 123;mHandler.sendMessage(message);}}, 0, 1000);} }
效果如圖:
上邊的案例,就是簡單的用到了Handler類來實現一個圖片輪播的效果。
Handler的實現原理:
與Handler一起工作的幾個組件:
1.Message:Handler接收和處理的消息對象
2.Looper:每個線程只能擁有一個Looper.它的loop方法負責讀取MessageQueue中的消息,
讀到信息之后就把消息交給發送該消息的Handler進行處理。
3.MessageQueue:消息隊列,它采用先進先出的方式來管理Message.
程序創建Looper對象時會在他的構造器中創建Looper對象。
Looper提供的構造器源代碼如下:
private Looper() {mQueue = new MessageQueue();mRun = true;mThread = Thread.currentThread();} 從源代碼中可以看出:
(1). ?構造方法是私有的,所以不允許我們自己創建Looper對象
(2). 程序在初始化Looper時會創建一個與之關聯的MessageQueue,這個MessageQueue就負責管理消息。
4.Handler:
簡而言之,Handler的作用:Handler的構造方法,其實就是在Handler中持有一個指向該Looper.mQueue對象,
當handler調用sendMessage方法時,其實就是往該mQueue中去插入一個message,然后Looper.loop()就會取出執行。
作用有兩個-----發送消息和處理消息,程序中使用Handler發送消息,
被Handler發送的消息必須被送到指定的MessageQueue。
也就是說,如果希望Handler正常工作,必須在當前的線程中有一個MessageQueue,
否則消息就沒有MessageQueue進行保存了。不過MessageQueue是由Looper負責管理的,
也就是說,如果希望Handler正常工作,必須在當前的線程中有一個Looper對象,
為了當前線程中有Looper對象,可以分為兩種情況處理:
(1).主UI線程中,系統已經初始化了一個Looper對象,因此程序直接創建Handler即可,
然后就可以通過Handler來發送消息,處理消息了。
(2).在自己啟動的子線程中,必須自己創建一個Looper對象,并啟動它。
------創建Looper對象調用它的prepare()方法即可。
Looper類中prepare()方法的源碼:
public static final void prepare() {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper());} prepare()方法保證每個線程最多只有一個Looper對象。
------然后調用Looper的靜態loop()方法來啟動它。loop()方法使用一個死循環不斷的取出MessageQueue中的消息,
并將取出的消息分給該消息對應的Handler進行處理。
Looper類中loop()方法的源碼:
public static final void loop() {Looper me = myLooper();MessageQueue queue = me.mQueue;while (true) {Message msg = queue.next(); // might block//if (!me.mRun) {// break;//}if (msg != null) {if (msg.target == null) {// No target is a magic identifier for the quit message.return;}if (me.mLogging!= null) me.mLogging.println(">>>>> Dispatching to " + msg.target + " "+ msg.callback + ": " + msg.what);msg.target.dispatchMessage(msg);if (me.mLogging!= null) me.mLogging.println("<<<<< Finished to " + msg.target + " "+ msg.callback);msg.recycle();}}}
總結一下:
Looper,MessageQueue,Handler各自的作用如下:
Looper:每個線程只用一個Looper,負責管理MessageQueue,會不斷的從MessageQueue中取出消息,
并將消息分給對應的Handler處理。
MessageQueue:由Looper負責管理,它采用先進先出的方式管理Message。
Handler: 它能把消息發送給Looper管理的MessageQueue,并負責處理Looper分給它的消息。
簡而言之:
Handler負責發送消息,
Looper負責接收Handler發送的消息并直接把消息回傳給Handler自己,
MessageQueue就是一個存儲消息的容器。
在線程中使用Handler的步驟如下:
1. 調用Looper的prepare()方法為當前的線程創建Looper對象,
創建Looper對象時,Looper的構造方法中會創建與之配套的MessageQueue.
2. 有了Looper對象之后,創建Handler子類的實例,重寫handleMessage()方法,
該方法負責處理來自于其他線程的消息。
3. 調用Looper的loop()方法啟動Looper。
下面對比一下在主線程中和在子線程中使用Handler的區別:
在主線程中使用Handler計算一定范圍之內的質數的例子:
package com.my.Mytimetest;import java.util.ArrayList; import java.util.List;import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast;public class SecondActivity extends Activity implements OnClickListener {private EditText edt;private TextView textview;private Button stop_btn;private Handler mHandler;private List<Integer> nums = new ArrayList<Integer>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();setListener();mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {if (msg.what == 123) {int num = msg.getData().getInt("num");//計算從2開始,到num的所有質數outer: for (int i = 2; i < num; i++) {//從i除以2開始,到i的平方根的所有數for (int j = 2; j < Math.sqrt(i); j++) {//如果可以整除,則表明這個數不是質數if (i != 2 && i % j == 0) {continue outer;}}nums.add(i);}textview.setText(nums.toString());nums.removeAll(nums);}}};}private void setListener() {stop_btn.setOnClickListener(this);}private void initView() {edt = (EditText) findViewById(R.id.id_edt);textview = (TextView) findViewById(R.id.id_textview);stop_btn = (Button) findViewById(R.id.id_start);}@Overridepublic void onClick(View v) {int num = Integer.parseInt(edt.getText().toString());Message message = new Message();message.what = 123;Bundle bundle = new Bundle();bundle.putInt("num", num);message.setData(bundle);// 將消息發送給子線程中的Handler來處理mHandler.sendMessage(message);}}
在子線程中使用Handler計算一定范圍之內的質數的例子:
package com.my.Mytimetest;import java.util.ArrayList; import java.util.List;import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast;public class MainActivity extends Activity implements OnClickListener {private EditText edt;private Button stop_btn;private MyThread myThread;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();setListener();myThread = new MyThread();//開啟子線程myThread.start();}private void setListener() {stop_btn.setOnClickListener(this);}private void initView() {edt = (EditText) findViewById(R.id.id_edt);stop_btn = (Button) findViewById(R.id.id_start);}@Overridepublic void onClick(View v) {int num = Integer.parseInt(edt.getText().toString());Message message = new Message();message.what = 123;Bundle bundle = new Bundle();bundle.putInt("num", num);message.setData(bundle);//將消息發送給子線程中的Handler來處理myThread.mHandler.sendMessage(message);}class MyThread extends Thread{public Handler mHandler;private List<Integer> nums = new ArrayList<Integer>();@Overridepublic void run() {Looper.prepare();mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {if(msg.what==123){int num = msg.getData().getInt("num");outer:for (int i = 2; i < num; i++) {for (int j = 2; j < Math.sqrt(i); j++) {if(i!=2&&i%j==0){continue outer;}}nums.add(i);}Toast.makeText(MainActivity.this, nums.toString(), Toast.LENGTH_LONG).show();nums.removeAll(nums);}}};Looper.loop();}}}效果圖:
HandlerThread類的使用:
介紹:
? ? ? ??? ?HandlerThread繼承自Thread,當線程開啟時,也就是它run方法運行起來后,
線程同時創建了一個含有消息隊列的Looper,并對外提供自己這個Looper對象的get方法,這就是它和普通Thread唯一不同的地方。
好處:
為什么要使用HandlerThread。
1.開發中如果多次使用類似new Thread(){...}.start()
這種方式開啟一個子線程,會創建多個匿名線程,使得程序運行起來越來越慢,
而HandlerThread自帶Looper使他可以通過消息來多次重復使用當前線程,節省開支;
2.android系統提供的Handler類內部的Looper默認綁定的是UI線程的消息隊列,
對于非UI線程又想使用消息機制,那么HandlerThread內部的Looper是最合適的,它不會干擾或阻塞UI線程。
用法:
?HandlerThread既然本質是Thread,為何前面加了一個Handler?
android中Handler類本質上就是從它內部的Looper中不斷取消息,
然后觸發它內部的Callback接口的handleMessage方法,讓用戶去實現對消息的具體處理。
而HandlerThread本身自帶Looper,只要它實現了Callback接口,
那么HandlerThread也可以在自己線程內處理自己線程發出的消息,
充分實現非UI線程中較低開支下的消息處理。
HandlerThread類的使用:
package com.my.Mytimetest;import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.util.Log;public class FourActivity extends Activity{@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);HandlerThread handlerThread = new HandlerThread("handler thread");handlerThread.start();Handler mHandler = new Handler(handlerThread.getLooper()){@Overridepublic void handleMessage(Message msg) {Log.i("tag", "當前的線程:"+Thread.currentThread());}};mHandler.sendEmptyMessage(1);}}
效果圖:
可以看出,打印的是子線程的信息。HandlerThread中加了同步鎖,保證了線程的安全。
HandlerThread類的run()方法的源碼:
public void run() {mTid = Process.myTid();Looper.prepare();synchronized (this) {mLooper = Looper.myLooper();notifyAll();}Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid = -1;}
HandlerThread類中的getLooper()方法的源碼:
public Looper getLooper() {if (!isAlive()) {return null;}// If the thread has been started, wait until the looper has been created.synchronized (this) {while (isAlive() && mLooper == null) {try {wait();} catch (InterruptedException e) {}}}return mLooper;}
下面介紹一下主線程與子線程之間通過Handler傳遞消息的方法:
主線程給子線程發送、子線程給主線程發送消息的代碼:
package com.my.Mytimetest;import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button;public class ThreedActivity extends Activity implements OnClickListener {private Button button1;private Button button2;private Handler threadHandler;private Message messageMain;private Message messageThread;//創建主線程的handlerprivate Handler mHandler = new Handler() {public void handleMessage(android.os.Message msg) {messageMain = new Message();Log.i("tag", "主線程中的Handler");//向子線程發送消息threadHandler.sendMessageDelayed(messageMain, 1000);};};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.three);button1 = (Button) findViewById(R.id.id_btn1);button2 = (Button) findViewById(R.id.id_btn2);button1.setOnClickListener(ThreedActivity.this);button2.setOnClickListener(ThreedActivity.this);HandlerThread handlerThread = new HandlerThread("handler thread");handlerThread.start();//創建子線程的handlerthreadHandler = new Handler(handlerThread.getLooper()){@Overridepublic void handleMessage(Message msg) {messageThread = new Message();Log.i("tag", "子線程中的Handler");//向主線程發送消息mHandler.sendMessageDelayed(messageThread, 1000);}};}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.id_btn1://主線程給子線程發送一個消息mHandler.sendEmptyMessage(1);break;case R.id.id_btn2:break;default:break;}}} 效果圖:
Android中更新UI的幾種方式:
1.runOnUiThread
2.handler post
3.handler sendMessage
4.view post
代碼:
package com.my.Mytimetest;import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView;public class FiveActivity extends Activity implements OnClickListener {private TextView textview;private Button changeBtn1;private Button changeBtn2;private Button changeBtn3;private Button changeBtn4;private int index;private Handler handler = new Handler() {public void handleMessage(android.os.Message msg) {textview.setText("handler sendMessage方式更新UI");Log.i("tag", "handler sendMessage方式更新UI");};};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.five);textview = (TextView) findViewById(R.id.id_textview);changeBtn1 = (Button) findViewById(R.id.id_change1);changeBtn1.setOnClickListener(this);changeBtn2 = (Button) findViewById(R.id.id_change2);changeBtn2.setOnClickListener(this);changeBtn3 = (Button) findViewById(R.id.id_change3);changeBtn3.setOnClickListener(this);changeBtn4 = (Button) findViewById(R.id.id_change4);changeBtn4.setOnClickListener(this);dealUI();}private void dealUI(){// 在子線程中更新UInew Thread() {@Overridepublic void run() {try {Thread.sleep(2000);switch (index) {case 1:// 方式1handler1();break;case 2://方式2:handler2();break;case 3://方式3:updateUI();break;case 4://方式4:viewUI();break;default:System.out.println("不會吧");break;}} catch (InterruptedException e) {e.printStackTrace();}}}.start();}/*** 方式1:handler post方式*/private void handler1() {handler.post(new Runnable() {@Overridepublic void run() {textview.setText("handler post方式更新UI");Log.i("tag", "handler post方式更新UI");}});}/*** 方式2:handler sendMessage方式*/private void handler2() {handler.sendEmptyMessage(1);}/*** 方式三:runOnUiThread方式*/private void updateUI() {runOnUiThread(new Runnable() {@Overridepublic void run() {textview.setText("runOnUiThread方式更新UI");Log.i("tag", "runOnUiThread方式更新UI");}});}/*** 方式四:view post方式*/private void viewUI() {textview.post(new Runnable() {@Overridepublic void run() {textview.setText("view post方式更新UI");Log.i("tag", "view post方式更新UI");}});}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.id_change1:index=1;dealUI();break;case R.id.id_change2:index=2;dealUI();break;case R.id.id_change3:index=3;dealUI();break;case R.id.id_change4:index=4;dealUI();break;default:break;}}} 效果圖:
實際上,上邊的這四種更新UI的方式都是使用到了handler機制來更新UI的,只是內部一些代碼的處理不一樣,看一下這四種方式的源碼,我們就知道了。
源碼對比:
handler post方式:
public final boolean post(Runnable r){return sendMessageDelayed(getPostMessage(r), 0);} runOnUiThread方式:
public final void runOnUiThread(Runnable action) {if (Thread.currentThread() != mUiThread) {mHandler.post(action);} else {action.run();}} view?post方式:
public boolean post(Runnable action) {Handler handler;if (mAttachInfo != null) {handler = mAttachInfo.mHandler;} else {// Assume that post will succeed laterViewRoot.getRunQueue().post(action);return true;}return handler.post(action);} 其實都是通過handler機制來實現的。
最后,一些常見的異常的產生原因:
1.Can't create handler inside thread that has not called Looper.prepare();
產生的原因:
是因為在子線程中創建Handler對象的時候沒為創建的Handler指定Looper,
源碼解析:
<span style="color:#333333;"> public Handler() {if (FIND_POTENTIAL_LEAKS) {final Class<? extends Handler> klass = getClass();if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());}}</span><span style="color:#ff0000;">mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}</span><span style="color:#333333;">mQueue = mLooper.mQueue;mCallback = null;} </span> 2. android.view.ViewRootImpl$CalledFromWrongThreadException:?
Only the original thread that created a view hierarchy can touch its views.
產生的原因:是因為在非UI線程中更新UI
總結
以上是生活随笔為你收集整理的Android中handler的使用及原理---学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: eclipse中经常用到的快捷键
- 下一篇: Android中Intent传递Obje