Handler由浅入深(一)--Handler的基本使用

为什么要使用Handler

1、UI更新是在主线程(即UI线程,非线程安全)
2、主线程不能执行耗时操作,耗时超过5秒会出现ANR现象
3、子线程无法更新主线程UI
由此产生了Handler消息传递异步机制,子线程负责耗时操作,主线程负责更新UI,Handler充当子线程和主线程之间的桥梁作用;

Handler的一些特点

1、Handler可分发Message对象和Runnable对象到主线程
2、可控制在主线程或者某个线程的某个地方执行
3、可控制在某个具体时间或者延迟时间执行

Handler中一些分发消息的方法

    post(Runnable)//立即执行
    postAtTime(Runnable,long)//定时执行
    postDelayed(Runnable long)//延时执行
    removeCallbacks(Runnable r)//从消息队列中移除一个Runnable对象
    sendEmptyMessage(int)//发送空消息
    sendMessage(Message)//发送Message对象
    sendMessageAtTime(Message,long)//定时发送Message对象
    sendMessageDelayed(Message,long)//延时发送Message对象

下面先介绍使用Handler的post实现UI更新

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private Handler mHandler;
    private TextView btnPost, btnPostDelay, textValue;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new Handler();
        btnPost = (TextView) findViewById(R.id.btn_post);
        btnPostDelay = (TextView) findViewById(R.id.btn_post_dey);
        textValue = (TextView) findViewById(R.id.text_value);
        btnPost.setOnClickListener(this);
        btnPostDelay.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.btn_post) {//立即执行
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            textValue.setText("来自于工作线程post(Runnable)发送到消息队列,在主线程中执行");
                        }
                    });
                }
            }).start();
        } else if (view.getId() == R.id.btn_post_dey) {//延迟执行
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            textValue.setText("来自于工作线程post(Runnable)发送到消息队列,在主线程中延迟三秒执行");
                        }
                    }, 3000);
                }
            }).start();
        }
    }
}

效果图如下:
Handler由浅入深(一)--Handler的基本使用_第1张图片

再介绍使用Message传递消息

传递Message对象需要使用sendMessage方法将对象入队到消息队列中,同时需要在UI线程中重写handleMessage()方法,用来获取从工作线程中传过来的Message对象。

  • 对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的,才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。我们也不必担心消息池中的消息过多,它是有上限的,上限为10个。Handler.obtainMessage()具有多个重载方法,如果查看源码,会发现其实Handler.obtainMessage()在内部也是调用的Message.obtain()。
  • Message有一下几个属性

    int arg1:参数一,用于传递不复杂的数据,复杂数据使用setData()传递。
    int arg2:参数二,用于传递不复杂的数据,复杂数据使用setData()传递。
    Object obj:传递一个任意的对象。
    int what:定义的消息码,一般用于设定消息的标志。

    使用Message类的属性可以直接携带int类型数据,如果要携带其他类型的数据,可以先将要携带的数据保存到Bundle中对象中,然后通过Message类的setData()方法将其添加到Message中,如:

    Message msg = Message.obtain();
    Bundle bundle = new Bundle();
    bundle.putInt("int", 1);
    bundle.putBoolean("boolean", false);
    bundle.putByte("byte", (Byte) null);
    bundle.putChar("char",'a');
    bundle.putCharSequence("Stirng","Hello World");
    bundle.putFloat("float", 12.3f);
    bundle.putString("String", "Hello World");
    bundle.putDouble("double", 12.3);
    msg.setData(bundle);
    

    下面是一个小Demo

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

        private TextView btnPost, btnPostDelay, btnSendMess, textValue;
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.arg1) {
                    case 0:
                        textValue.setText(msg.obj.toString());
                        break;
                }
            }
        };

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            btnPost = (TextView) findViewById(R.id.btn_post);
            btnPostDelay = (TextView) findViewById(R.id.btn_post_dey);
            btnSendMess = (TextView) findViewById(R.id.btn_send);
            textValue = (TextView) findViewById(R.id.text_value);
            btnPost.setOnClickListener(this);
            btnPostDelay.setOnClickListener(this);
            btnSendMess.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            if (view.getId() == R.id.btn_post) {//立即执行
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                textValue.setText("来自于工作线程post(Runnable)发送到消息队列,在主线程中执行");
                            }
                        });
                    }
                }).start();
            } else if (view.getId() == R.id.btn_post_dey) {//延迟执行
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        mHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                textValue.setText("来自于工作线程post(Runnable)发送到消息队列,在主线程中延迟三秒执行");
                            }
                        }, 3000);
                    }
                }).start();
            }else if (view.getId() == R.id.btn_send){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message msg = Message.obtain();
                        msg.arg1 = 0;
                        msg.obj = "来自于工作线程sendMessage发送到消息队列,在主线程中执行";
                        mHandler.sendMessage(msg);//还有其他几种发送空消息和定时、延迟就不一一列举了
                    }
                }).start();
            }
        }
    }

这里需要注意的是Message.obtain()有几种构造函数,可以参考一下引用案例

 public class MyThread implements Runnable{  

        @Override  
        public void run() {  
            /* 
             * 用obtain获取一个消息,会从消息池中取出一个消息(消息池不为null), 
             * 消息池的消息减1。如果消息池为空,则调用new Message新建一个 
             */  
            /*第一种方式 
               Message message = Message.obtain(); 
               message.what = 1; 
               message.arg1 = 2; 
               message.arg2 = 3; 
               message.obj = "lin"; 
               handler.sendMessage(message); 
            */  
            /*第二种方式 
               Message message = Message.obtain(handler); 
               message.what = 1; 
               message.arg1 = 2; 
               message.arg2 = 3; 
               message.obj = "lin"; 
             //把发送对象target置为当前传入的handler,故不能用sendMessage 
               message.sendToTarget(); 
            */  
            /* 
             * 第三种方式 
             * 第一个参数:Handler 
             * 第二个参数:what 
               Message message = Message.obtain(handler, 66); 
               message.arg1 = 1; 
               message.arg2 = 2; 
               message.obj = "monkey"; 
               message.sendToTarget(); 
            */  
            /*第四种方式 
             * 第一个参数:Handler 
             * 第二个参数:what 
             * 第三个参数:arg1 
             * 第四个参数:arg2 
             * 第五个参数:obj 
               Message message = Message.obtain(handler, 1, 2, 3, "Mary"); 
               message.sendToTarget(); 
            */  
            /*第五种方式 
             * 第一个参数:handler
             * 第二个参数:what 
             * 第三个参数:obj 
             */  
               Message message = Message.obtain(handler, 1, "Rose");  
               //除此之外还可以用setData传递一些比较复杂的数据,八种数据类型中的数据都可以传递  
               Bundle data = new Bundle();  
               data.putStringArray("str", new String[]{"Rose","Jackson"});  
               message.setData(data);  
               message.sendToTarget();  

        }  

    }  
}  

到这里Handler的一些基本使用就结束了!

再介绍一下更新Ui的四种方法

以btn_send的点击事件为例
- 方法1.使用handler的post方法(以上已实现)
- 方法2,使用handler发送消息来处理(以上已实现)
- 方法3,使用runOnUiThread

if (view.getId() == R.id.btn_send){
       new Thread(new Runnable() {
                @Override
                public void run() {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            textValue.setText("runOnUiThread(new Runnable())更新UI");
                        }
                    });
                }
            }).start();
}
  • 方法4,使用View.post()
if (view.getId() == R.id.btn_send){
    new Thread(new Runnable() {
                @Override
                public void run() {
                    btnSendMess.post(new Runnable() {
                        @Override
                        public void run() {
                            textValue.setText("View.post(new Runnable())更新UI");
                        }
                    });
                }
            }).start();
}

以上四个方法虽然使用方法不同,但查看源码可以知道其实现原理基本都是通过Handler实现的!

你可能感兴趣的:(Handler由浅入深(一)--Handler的基本使用)