Android消息机制之Handler
2016-03-08 22:18
537 查看
Android为什么要提供Handler
Android建议我们不要在UI线程中执行耗时操作,因为这很容易导致ANR异常(在Android源码中我们可以看到,UI如果对用户的操作超过5秒无响应,就会报ANR异常)。因此,一些耗时操作都会在子线程中完成。当我们在子线程中获取了数据,要将其显示到UI中,如果没有Handler,这将很难完成。因此,Android之所以提供Handler,就是为了解决子线程访问UI的问题。为什么Android不允许在子线程中访问UI呢?显然这样做不安全,多线程访问UI是不安全的(学过操作系统的盆友应该都了解线程互斥,这里我就不详细介绍了)。有人就会说了,可以通过设置信号量来解决啊。这中方法不是不可以,因为这种方法会使访问UI的逻辑变得复杂;其次这会降低UI的访问效率。而使用Handler就比较简单高效。Handler是同个Message来通讯的。
Handler的用法
使用Handler时,需要重写handleMessage方法,在handleMessage中接受新线程发来的Message,并做相应的处理。在新线程中则是通过Message来传递消息,Message中往往也携带着需要传递的数据以及消息的类型。还要强调一点,如果当前线程有Looper就不需要执行Looper.prepare(),如果没有,就需要在新线程内执行Looper.prepare(),否则会报错。具体使用代码如下:public class MainActivity extends AppCompatActivity { private Handler mHandler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: //执行需要修改的UI操作 break; default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() {//在新线程中执行耗时操作 //如果当前线程有Looper就不需要执行Looper.prepare(); Looper.prepare(); try { Thread.sleep(1000);//睡眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } //操作完成之后通过发送Message,来通知Handler进行UI操作 Message msg=new Message(); msg.what=1; /*这部分是伪代码,value 是想通过Message传递的值 Bundle data=new Bundle(); data.putSerializable("key",value); msg.setData(data); */ //设置好数据后,发送消息 mHandler.sendMessage(msg); } }).start(); } }
当然,handler也可以在子线程中创建,代码如下:
private TextView tv_test; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.handler_test_layout); tv_test= (TextView) findViewById(R.id.tv_test); } //button点击的函数 public void click(View v) { new Thread(new Runnable() { @Override public void run() { Looper.prepare(); Handler handler=new Handler(Looper.getMainLooper()){ @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: tv_test.setText("receive msg"); } } }; Message msg=new Message(); msg.what=1; handler.sendMessage(msg); } }).start(); }
上面的代码是,当点击按钮后,就会创建一个新的线程,在新线程中创建handler,并发送消息、接受消息。这里需要注意的是,在新线程中创建handler需要使用Handler handler=new Handler(Looper.getMainLooper())这样的写法,Looper.getMainLooper()将主线程中的Looper传过去,这样handler才能访问UI。运行效果如下:
Handler的内部机制
Handler创建时会采用Looper来建立消息循环。所以,当前线程必须要有Looper。当Handler创建完成后,其内部的Looper以及MessageQueue既可以和Handler一起协同工作了。Handler通过sendMessage将消息发送给内部的MessageQueue,而MessageQueue会调用queue.enqueueMessage(msg, uptimeMillis)方法,它的源码如下:boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
通过源码,我们发现,queue.enqueueMessage(msg, uptimeMillis)将消息放入了MessageQueue里。Looper则会一直处理MessageQueue中的消息。
相关文章推荐
- Android FragmentPagerAdapter.notifyDataSetChanged() 并不能更新其 Fragment解决方案
- Android错误
- Android消息机制之Handler
- Android jni 常见错误
- Android中的属性动画(Property Animation)——Android开发艺术探索笔记
- Android基础——TabHost使用(自定义按钮菜单)
- 以动态列表配置选项(ListActivity与Menu整合技巧)
- 4.4.4 Android animation List 实现充能塔的效果
- android使用AIDL实现跨进程通讯(IPC)
- GridView视图(BaseAdapter)
- Android Context简介
- Android开发规范
- Android studio 导入xutils3报错
- Android:分页下载示例(PullToRefresh)
- Android Studio git
- Android瀑布流照片墙实现,体验不规则排列的美感
- android:layout_weight的真实含义
- android 数据库(一)
- java/android基础总结2
- [android] 数据库的事务