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,但是呢打印出来的结果却是 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有几种构造方法, 看下第二种构造方法有啥作用?

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怎么来防止上面引发的空指针呢? 查看源代码也不能看出

细心的人会注意到前面提到过,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"); } });

同时调用四种方法,几个用时改变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中MaterialDesign使用 (五)自定义Behavior实现UC顶栏效果
  3. iphone Android(安卓)JS的使用
  4. Android(安卓)多线程:线程池理解和使用总结
  5. Android:控件GridView的使用
  6. 开始开发Android的使用Eclipse
  7. Android(安卓)SDK Manager更新不了的解决办法
  8. Android:控件GridView的使用
  9. Android中ProgressDialog的简单使用.

随机推荐

  1. android 系统属性 之 自定义属性
  2. Android:读取本地相册与相机获取图片上传
  3. Android(安卓)studio打包报错:Lint found
  4. ubuntu的android studio调试小米手机的方
  5. 一个日期选择对话框
  6. FormatDateTime Java ,Android 常用的日
  7. Android(安卓)MQTT使用详解
  8. android fastboot 刷机 指令
  9. android SDK 无法更新
  10. Android 最大程度从App回到桌面 不被杀死