Handler详解

Handler是什么

Handler是android提供给我们用来更新UI的一套机制
Handler是android提供给我们的一套消息处理机制:可以发送消息也可以处理消息
例如:Activity的生命周期回调方法都是通过Handler机制进行发送消息的,然后根据不同的Message进行分支处理

为什么要用Handler

Android在创建的时候就封装一个一套消息发送、传递、处理的机制,如果不遵循这样的机制就无法更新UI信息的,会抛出异常(不能再非UI线程中更新UI)

Handler怎么用

post(Runnable)
postDelayed(Runnable,long)
sendMessage()
sendMessageDelayed()

在子线程中更新UI
textView = (TextView) findViewById(R.id.textview);
new Thread() {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            textView.setText("在子线程中更新UI");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}.start();
这样的话程序会出错: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its
使用Handler(注意这里导入的包是os下的Handler) post(Runnable)
private Handler handler = new Handler();
在子线程中更新UI
textView = (TextView) findViewById(R.id.textview);
new Thread() {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            handler.post(new Runnable() {
                @Override
                public void run() {
                    textView.setText("在子线程中更新UI");
                }
            });
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}.start();
实例:
在ImageView中不断循环更新图片  postDelayed(Runnable,long)
public class MainActivity extends AppCompatActivity {

    private TextView textView;
    private Handler handler = new Handler();
    private ImageView imageView;
    //数组存储图片资源ID
    private int images[] = {R.drawable.image1,R.drawable.image2,R.drawable.image3};
    //图片的索引
    private int index = 0;
    //创建一个子线程
    private MyRunnable myRunnable = new MyRunnable();
    //匿名内部类
    class MyRunnable implements  Runnable{
        @Override
        public void run() {
            index++;
            index = index % images.length;
            imageView.setImageResource(images[index]);
            //延迟1秒后在调用线程myRunnable(调用自己),实现循环的功能不断的调用自己
            handler.postDelayed(myRunnable,1000);
        }
    }

    /**
     * ATTENTION: This was auto-generated to implement the App Indexing API.
     * See https://g.co/AppIndexing/AndroidStudio for more information.
     */
    private GoogleApiClient client;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        textView = (TextView) findViewById(R.id.textview);
        imageView = (ImageView) findViewById(R.id.imageview);
        //延迟1秒后调用线程myRunnable
        handler.postDelayed(myRunnable,1000);}
在这里我们还可以移除消息
button = (Button) findViewById(R.id.btn);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //移除消息
        handler.removeCallbacks(myRunnable);
    }
});
这样循环的子线程就不会再MessageQueue中了

sendMessage方法
private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        textView.setText(" "+msg.arg1+" "+msg.arg2+" "+msg.obj);
    }
};
自定义的类
class  Person{
    private int age;
    private String name;

    @Override
    public String toString() {
        return "  name="+name+"  age="+age;
    }
}
子线程更新UI
        new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    //获取Message对象的两种方法:第一种Message message = new Message();
                    // 第二种 Message message = handler.obtainMessage();
//                    Message message = new Message();
                    Message message = handler.obtainMessage();
                    message.arg1 = 1;
                    message.arg2 = 2;
                    //传递自定义对象 我们从网络中获取的Json解析的对象可以发送过去
                    Person person = new Person();
                    person.age = 21;
                    person.name = "xiaoxin";
                    message.obj = person;
                    //发送消息的两种方法:第一种handler.sendMessage(message);
//                    第二种: message.sendToTarget();
//                    handler.sendMessage(message)
                    message.sendToTarget();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();

其他用法:我们可以拦截Message:实现callback接口,true表示拦截,false表示不拦截
private Handler handler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        Toast.makeText(getApplicationContext(),"拦截消息",Toast.LENGTH_LONG).show();
        //return true表示下面的handleMessage不执行,被拦截了
        return true;
    }
}) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        textView.setText(" " + msg.arg1 + " " + msg.arg2 + " " + msg.obj);
    }
};

Handler用法小总结:可以用来发送、移除、截获消息等功能


Android为什么设置只能通过Handler更新UI

根本目的:解决多线程并发问题
假设在一个Activity中,有多个线程去更新UI,并且没有加锁机制,那么会出现界面混乱问题
如果我们对所有更新UI加锁的话那么会出现性能下降的问题
基于上面这种问题android给我们提供了Handler机制,我们只要遵循这套机制,不用去关心多线程问题,所有更新UI的操作都是在主线程的消息队列中去处理的

Handler的原理是什么

Handler封装了消息的发送
Looper:包含了一个消息队列MessageQueue,所有Handler发送的消息都是走向这个队列
Looper.loop方法是一个死循环,不断从MessagQueue中取出消息,如果有消息就处理消息没有消息就阻塞
MessageQueue:是一个消息队列可以添加消息也可以处理消息
在我们创建的一个Activity中都有一个ActivityThread里面有一个main线程 main下有一个Looper Looper中有MessageQueue

总结:Handler负责发送消息,Looper负责Handler发送的消息,并直接把消息回传给Handler自己 MessageQueue是一个存储消息的容器

实现与线程相关的Handler

在主线程中创建Handler,重新handlerMessage,不要做耗时处理不然主界面会卡死
解决办法我们可以再子线程中创建Handler
//在子线程中处理Handler
    class MyThread extends Thread{
        private Handler handler;
        @Override
        public void run() {
            //准备一个Looper
            Looper.prepare();
            handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
//要更新的UI 
                }
            };
            //死循环不断从MessageQueue中取出消息
            Looper.loop();
        }
    }

使用方法就是new一个MyThread线程然后开启

HandlerThread是什么

解决多线程导致的空指针问题
创建Handler的时候我们可以指定Looper,所以这个Looper对象是可以别的线程创建的,但是进行的时候我们要确保这个looper对象已经在别的线程中创建好了,否则会出现空指针异常,这个时候我们就可以通过HandlerThread创建这个Looper
为什么Handler要和Looper相关联,是因为Handler要往Looper中的MessageQueue插入Message
将主线程中的new出来的消息队列设置成HandlerThread对象thread的
handler = new Handler(thread.getLoop()),这样handler处理的执行的操作都会在thread中执行而不是在主线程中执行
用于主线程向子线程发送消息
来自别人的看法
创建Handler的时指定的looper,可以是别的线程创建的。所以Handler中MessageQueue的轮询不一定非要是创建Handler的线程进行,还可以在别的线程中进行。
这个时候我们就需要使用HandlerThread这个类来创建这个Looper了,这样消息的处理就在新创建的HandlerThread中进行。
mThread = new HandlerThread("Handler Thread");
mHandler = new Handler(mThread.getLooper()){
public void handleMessage(android.os.Message msg) {... };
};
我的看法:通过源码我们可以知道HandlerThre继承这Thread本身就是一个子线程,这个HandlerThread中通过wat()和notifyAll()的线程同步机制,确保主线程调用自己的Looper对象不为null

如何在主线程中向子线程发送消息


子线程向主线程发送消息,是通过调用主线程的Handler,发送信息给主线程的Looper,该Handler已经绑定主线程的Looper
主线程向子线程发送消息,是通过调用子线程的Handler,发送信息给子线程的Looper,因此必须确保子线程有Looper,为了防止子线程的Looper没有被初始化那么我们可以通过HandlerThread类确保子线程的Looper再被主线程调用的时候已经初始化了
package com.example.myapplication;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.google.android.gms.common.api.GoogleApiClient;

/**
 * Created by 小新 on 2016/4/11.
 */
public class SendStopHandler extends AppCompatActivity implements View.OnClickListener{

    private Button send, stop;
    /**
     * ATTENTION: This was auto-generated to implement the App Indexing API.
     * See https://g.co/AppIndexing/AndroidStudio for more information.
     */
    private GoogleApiClient client;

    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            Message message = new Message();
            Log.e("LOG","main Handler");
            //向子线程发送消息
            threadHandler.sendEmptyMessageDelayed(1,1000);
        }
    };

    private Handler threadHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout);
        send =(Button)findViewById(R.id.send);
        stop = (Button)findViewById(R.id.stop);
        send.setOnClickListener(this);
        stop.setOnClickListener(this);
        //创建子线程的Handler
        HandlerThread handlerThread = new HandlerThread("handlerThread");
        //这里的 handlerThread.start();一定要启动!!!!
        handlerThread.start();
        threadHandler = new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                Message  message = handler.obtainMessage();
                Log.e("LOG","thread Handler");
                //在主线程发送消息
                handler.sendEmptyMessageAtTime(1,1000);

            }
        };
    }
    
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.send :
                handler.sendEmptyMessage(1);
                break;
            case R.id.stop:
                handler.removeMessages(1);
                threadHandler.removeMessages(1);
                break;

        }

    }
}
按start按钮:运行结果每隔一秒打印

按stop按钮则停止发送消息

android中更新UI的方式

大概可以分为四种,其实原理都是一样的,到时Handler机制,只是进行了不一样的封装
1.handler.sendMessage(Message msg)
2.handler.post(Runnable run)
3.runOnUIThread(Runnable run)
4.控件.post(Runnable run)

非UI线程真的不能更新UI吗

答案是可以的,但我们还没有OnResume的时候,即在onCreate里面
ViewRootimp是在onResume方法中初始化的,在onCreate方法中创建线程并执行,这个时候是还没有初始化ViewRootimp的,所以没法判断更新UI的线程到底是不是主线程,ViewRootimp包含线程的判断
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值