android 系列学习之 Handler

handler是啥?handler的定义:主要接受子线程发送的数据,并用此数据配合更新UI。 Handler的使用: 曾经学过Java的同学都知道,以前在Java当中,要不断的更新JFrame上面的信息,可以再一个子线程当中直接更新,但是在Android当中呢?有人会说,Android主要也是使用Java的,可以跟Java一样实现。但事实并不是。Android的机制处理中,处于线程安全,Android是出了名的单一线程实例。

Log.e("threadID", Thread.currentThread().getId() + ""); textView = (TextView) this.findViewById(R.id.textView); new Thread() { @Override public void run() { super.run(); try { sleep(3000); Log.e("ThreadID", Thread.currentThread().getId() + ""); textView.setText("newTHread"); } catch (InterruptedException e) { e.printStackTrace(); } } }.start();

看上面的代码,在Java当中运行时没有一点儿问题的。但是在Android的平台当中确是不行的(在有的机测试确实可以的,这个原因有有待研究),会抛出下列异常:android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 那么怎么解决这个问题呢?常用的方法就是使用handler处理。 代码改一下:

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.e("threadID——main", Thread.currentThread().getId() + ""); textView = (TextView) this.findViewById(R.id.textView); handler.post(runnable); } Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { Log.e("threadID--runnable",Thread.currentThread().getId()+""); textView.setText("TextViewSetText"); } };

问题是解决了,但是呢又有新的问题出来了,分别在main Runnable中都打印出来当前线程的ID,但是呢打印出来的结果却是 Android系列学习:handler,HandlerThread_第1张图片 id是一样的!是一样的!!是一样的!!!此刻你是不是也都凌乱了?或许这情况只是在main中调用的只是run()而已,并非启动runnable子线程。(原因后面继续跟进) 不少同学在使用一些APP时候,都会发现其中他们的图片会不定时的切换。怎么实现呢?其实使用handler也是挺简单的一件事情。 图片的前置条件

private int images[] = {R.drawable.gaosi1, R.drawable.gaosi2, R.drawable.gaosi}; private int index = 0;

Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { index++; index = index % 3; imageView.setImageResource(images[index]); handler.postDelayed(runnable, 1000); } };

最好别忘了post()一下。

handler.post(runnable);

这样就有三张南华大学的图片在不停的切换 在测试时候遇到了一点小插曲:图片使用*.png会出现找不到符号,可能是应为Android studio 1.3的bug缘顾吧,还有就是ImageView需拖进去,自己写的会显示不出图片。 Handler 与runnable搭配使用时候,最后别忘了removeCallbacks() 不然即使你的应用退出了,Handler还在继续,就像一个未被销毁的服务一样存在。 Handler发送消息: Handler不仅具有修改UI,post(),还发消息。

Handler handler1 = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.e("msg.arg1",msg.arg1+""); } };

 Message message = new Message(); message.arg1 = 15; handler1.sendMessage(message);

结果毫无疑问的打印出E/msg.ar1﹕ 15 细心的伙伴都会发现arg1、arg2都是int ,我如果要发的消息是其他类型或者是一个自定的对象呢? 发送的消息是好几个数据呢?那咋补? 放心,在message中有一参数 msg.obj;这个是可以传递一个Object的参数,问题解决也就那么简单。 还有一个方法message.setData(bundle);可以再bundle中封装多个数据多种类型都可。

Message message = handler1.obtainMessage(); message.sendToTarget();

消息也可以这样子发送,这一意思是将消息发送到目的Handler,使用这方法发送时的注意,message必须是目的Handler获取得到的,如果是new Message()会抛出一个空指针异常,原因就是没有目的Handler。 查看过谷歌官方提供的API会发现,Handler有几种构造方法, Android系列学习:handler,HandlerThread_第2张图片 看下第二种构造方法有啥作用?

Handler handler1 = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { Log.e("Callback_msg.arg1",msg.arg1+""); return false; } }){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.e("Handler_msg.arg1",msg.arg1+""); } };

Callback里面重写的handleMessage()返回值为false与true又有啥区别? 当放回值为true时候,它会拦截msg的信息,为false时候不拦截。 返回值为true时候的结果

返回值为false时候的结果

Looper:一种线程的消息循环 谷歌官方API提供的实现方法

class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here Log.e("threadID", Thread.currentThread().getId() + ""); } }; Looper.loop(); } }

handler必须在Looper.loop()之前,Looper.prepare()之后 只要调用下面的代码就可以运行起来了

threadLooper = new ThreadLooper(); threadLooper.start(); threadLooper.handler.sendEmptyMessage(1);

使用就是这么简单,但是呢,运行上面的代码并不能成功,为啥?在发送消息之前还得让线程休眠一下,缓冲一下。不然会抛空指针异常的。这就是在多线程下,UI线程执行到下一行时候handler还没创建成功。怎么解决这种多线程导致的空指针异常呢?一,使用线程锁,二,HandlerTHread代替。 HandlerThread handlerThread到底是啥?Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called handlerThread的使用:

HandlerThread handlerThread = new HandlerThread("handlerThread"); handlerThread.start(); Log.e("currentThread_UI", Thread.currentThread().toString()); Handler handler = new Handler(handlerThread.getLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.e("currentThread_Handler", Thread.currentThread().toString()); } }; handler.sendEmptyMessage(1);

HandlerThread怎么来防止上面引发的空指针呢? 查看源代码也不能看出

Android系列学习:handler,HandlerThread_第3张图片

Android系列学习:handler,HandlerThread_第4张图片

细心的人会注意到前面提到过,Handler+Runnable时候,他俩使用的是同时UI线程,在这里会不会也是一样呢?看运行的国果吧。

Android为我们封装了HandlerThread,不仅是我们更好的预防Looper+Handler引发的空指针异常,还能成为独立于UI线程的子线程,在HandlerThread中我们可以操作耗时操作:请求网络等等。 更新UI的几种方法:

handler post;

handler sendmessage ;

runOnUiThread;

view post

怎么实现呢?前两种方法前面有所提及不再做详细的介绍 第三种方法:

runOnUiThread(new Runnable() { @Override public void run() { textView3.setText("gaosi"); } });

第四种方法:

textView4.post(new Runnable() { @Override public void run() { textView4.setText("gaosi"); } });

Android系列学习:handler,HandlerThread_第5张图片 同时调用四种方法,几个用时改变UI,(上面是使用这四种方法同时改变textView1--4) 一定得要在UI线程才能更改UI吗?

new Thread(){ @Override public void run() { textView.setText("gaosi"); try { sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }.start();

这样的代码UI也能更新成功!!!你是否已经惊呆了?大多数的人都这样认为,更新UI都要在UI线程,但是这个例子是否让你的世界观瞬间巨变了呢?如果在更新UI前线sleep(),结果又会怎么怎么样子呢?这下可以让你找回一点自信了,抛出异常信息了!想要知道让你的世界观瞬间巨变的原因吗?欲知结果,请看Android源码。

更多相关文章

  1. Android页面去掉标题全屏的方法-第一次用安卓试试看
  2. android获取图片的RGB颜色值
  3. android 中使文本(TextView 、button等可参考)在屏幕正中心显示的
  4. android屏幕自适应4方法案例整合
  5. 打开SDK Manager检查Android SDK下载和更新失败的解决方法
  6. android中实现带图片和checkbox的listview
  7. 边缘图片android布局属性详解
  8. Android缩放图片Bitmap、Drawable
  9. android studio 3.6.0 绑定视图新特性的方法

随机推荐

  1. 为什么说枚举更占内存,枚举原理是什么?
  2. u3d android 优化
  3. Android使用SVG的一点小坑
  4. android 空调遥控器——简单发送内容
  5. Android系统惊现AKB48封面木马 日本警方
  6. Web和Android中的Reactive
  7. android ghost tricks(一)
  8. Android(安卓)7.0解析包时出现问题 的解
  9. Android高性能编码 - 第八篇 移动端安全
  10. 在Android上优雅的申请权限