Android中handler的使用及原理---学习笔记
2015-07-19 10:33
676 查看
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; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageview = (ImageView) findViewById(R.id.id_images); final Handler mHandler = new Handler(){ @Override public 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(){ @Override public 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>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); setListener(); mHandler = new Handler() { @Override public 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); } @Override public 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; @Override protected 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); } @Override public 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>(); @Override public void run() { Looper.prepare(); mHandler = new Handler(){ @Override public 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{ @Override protected 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()){ @Override public 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.runOnUiThread2.handler post3.handler sendMessage4.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的使用及原理---学习笔记
- Day04-SmartImageView
- MPAndroidChart开源图表库之折线图
- Android View深入学习(三),View的绘制(Draw)过程
- Android中实现版本更新(一)使用第三方sdk更新
- Android之——AsyncTask和Handler对比
- android中ProgressBar和ListView
- Android Binder- 一次完整的通信过程
- Android 四大组件学习之Activity一
- Android Graphics - Architecture
- android如何提高加载布局页面速度
- Android Fragment
- Android之——Handler随笔
- android颜色值的表示方法android:background="#FFFFFFFF"的意思
- Android GridView一些错误
- Android应用程序启动过程
- Android:TextView属性大全
- Android SnackBar:你值得拥有的信息提示控件
- Android SnackBar:你值得拥有的信息提示控件
- android 无标题栏的设置方法