- 浏览: 36496 次
- 性别:
- 来自: 济南
文章分类
最新评论
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; @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; //创建主线程的handler private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { messageMain = new Message(); Log.i("tag", "主线程中的Handler"); //向子线程发送消息 threadHandler.sendMessageDelayed(messageMain, 1000); }; }; @Override protected 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(); //创建子线程的handler threadHandler = new Handler(handlerThread.getLooper()){ @Override public void handleMessage(Message msg) { messageThread = new Message(); Log.i("tag", "子线程中的Handler"); //向主线程发送消息 mHandler.sendMessageDelayed(messageThread, 1000); } }; } @Override public 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"); }; }; @Override protected 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(){ // 在子线程中更新UI new Thread() { @Override public void run() { try { Thread.sleep(2000); switch (index) { case 1: // 方式1 handler1(); 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() { @Override public 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() { @Override public void run() { textview.setText("runOnUiThread方式更新UI"); Log.i("tag", "runOnUiThread方式更新UI"); } }); } /** * 方式四:view post方式 */ private void viewUI() { textview.post(new Runnable() { @Override public void run() { textview.setText("view post方式更新UI"); Log.i("tag", "view post方式更新UI"); } }); } @Override public 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(); } }viewpost方式:
public boolean post(Runnable action) { Handler handler; if (mAttachInfo != null) { handler = mAttachInfo.mHandler; } else { // Assume that post will succeed later ViewRoot.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的使用玩转Android---组件篇---Handler的使用玩转Android---组件篇---Handler的使用
Android中handler的使用,处理多线程的使用
Android中多线程的Handler的工作原理,其中涉及到MessageQueue和Looper。详情可以参见博客:http://www.cnblogs.com/plokmju/p/android_Looper.html
赠送jar包:netty-handler-4.1.24.Final.jar; 赠送原API文档:netty-handler-4.1.24.Final-javadoc.jar; 赠送源代码:netty-handler-4.1.24.Final-sources.jar; 赠送Maven依赖信息文件:netty-handler-4.1.24....
Handler在android里负责发送和处理消息。它的主要用途有(或者是干什么的): 1)执行计划任务,按计划(定时)发送消息或执行某个Runnanble(使用POST方法); 2)线程间通信,把从其他线程中发送来的消息放入消息...
Android Handler Message 多线程实例 - Intel- Developer Zon
android 中Handler 的几种写法,很简单的demo,大神简单修改下,用的是Handler.Callback,的方法
Android Weak Handler Memory safer implementation of android.os.Handler Problem Original implementation of Handler always keeps hard reference to handler in queue of execution. Any object in Message or...
android Handler的使用,我也刚开始学习,从别处下载了给大家分享
赠送jar包:netty-handler-proxy-4.1.68.Final.jar; 赠送原API文档:netty-handler-proxy-4.1.68.Final-javadoc.jar; 赠送源代码:netty-handler-proxy-4.1.68.Final-sources.jar; 赠送Maven依赖信息文件:netty-...
赠送jar包:netty-handler-proxy-4.1.73.Final.jar; 赠送原API文档:netty-handler-proxy-4.1.73.Final-javadoc.jar; 赠送源代码:netty-handler-proxy-4.1.73.Final-sources.jar; 赠送Maven依赖信息文件:netty-...
赠送jar包:netty-handler-proxy-4.1.73.Final.jar; 赠送原API文档:netty-handler-proxy-4.1.73.Final-javadoc.jar; 赠送源代码:netty-handler-proxy-4.1.73.Final-sources.jar; 赠送Maven依赖信息文件:netty-...
Android的Handler使用方法总结,不错的文档,跟大家分享分享
实战HTTP Handler (4) -- 与Web程序共享Session 源码
实战HTTP Handler (1) -- 创建一个最简单的HTTP Handler 源码
实战HTTP Handler (3) -- 动态生成图片 源码
实战HTTP Handler (6) -- 条码随意打 源码
资源分类:Python库 所属语言:Python 使用前提:需要解压 资源全名:concurrent_log_handler-0.9.4-py2.py3-none-any.whl 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
Android高手进阶教程之----Android中万能的BaseAdapter(Spinner,ListView,GridView)的使用!.doc Android高手进阶教程之----通过Location获取Address的使用.doc Android基础教程之----Android ProgressBar的使用.doc...
3.android学习笔记--activity生命周期&handler使用 4.android学习笔记--HandlerThread和Bundle 5.android学习笔记--SQLite 6.android学习笔记--下载文件 7.android学习笔记--Content Provider 8.android学习笔记--...