Handler使用总结

一、最常见的子线程通过Handler发送Msg来刷新主界面:

    private final static int TAG1 = 1;
    private Button button;
    private TextView text;
    MyHandler myHandler;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        myHandler = new MyHandler();
        // 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据
        // Handler有两个作用,
        // (1) : 定时执行Message和Runnalbe 对象
        // (2): 让一个动作,在不同的线程中执行.
        // 它安排消息,用以下方法
        // post(Runnable)
        // postAtTime(Runnable,long)
        // postDelayed(Runnable,long)
        // sendEmptyMessage(int)
        // sendMessage(Message);
        // sendMessageAtTime(Message,long)
        // sendMessageDelayed(Message,long)
        // 以上方法以 post开头的可以处理Runnable对象
        // sendMessage()可以处理Message对象(Message里可以包含数据,)
    }

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
        case R.id.bu1:
            MyThread m = new MyThread();
            new Thread(m).start();
            break;

        default:
            break;
        }
    }

    /**
     * 用以接受消息,处理消息 ,此Handler会与当前主线程一块运行
     * */
    private class MyHandler extends Handler {

        // 默认构造函数
        public MyHandler() {
            super();
        }

        // 带有Looper参数的构造函数
        @SuppressWarnings("unused")
        public MyHandler(Looper L) {
            super(L);
        }

        // 子类必须重写此方法,处理消息
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 根据msg的类型来处理数据
            switch (msg.what) {
            caseTAG1:
                // 此处可以更新UI
                Bundle b = msg.getData();
                String color = b.getString("color");
                String btn = msg.obj.toString();
                text.append(color + btn);
                break;

            default:
                break;
            }
        }
    }

    /** Thread用以处理耗时的后台操作,并实时将数据通过Handler添加到MessageQueue中
     *  Looper再从MessageQueue中取出,disptch给相应的处理函数*/
    class MyThread implements Runnable {
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            /** 使用Message能够传递多种数据具体可以参考Message的源码
                最常用的可以使用Message的obj对象在线程之间传递对象数据*/
            // Message msg  =  new Message();
            // 宜采用从Message池中获取msg,这样能够节省内存
            Message msg = myHandler.obtainMessage();
            msg.what = TAG1;
            Bundle b = new Bundle();// 存放数据
            b.putString("color", "Red");
            msg.setData(b);
            msg.obj = new String("Button"); // Data用于存放大数据,obj存放小数据
            myHandler.sendMessage(msg); // 向Handler发送消息,更新UI
        }
    }

    private void initView(){
        button = (Button) findViewById(R.id.bu1);
        button.setOnClickListener(this);
        text = (TextView) findViewById(R.id.text);
    }

1、引申:如果使用Handler设计到对Activity本身的引用,比如想在handleMessage中使用Toast,就需要Activity的Context
如果Acivity销毁,而Handler却依然保持对该Activity的引用,则会造成内存泄露;在代码中也会提示HandlerLeak
解决方法可以有如下两种,最根本的解决方法是第二种使用弱引用;
1)使用全局变量getApplicationContext()的方法
2)使用弱引用方式:

 /*** 使用此方法来避免内存泄露HandlerLeak **/
    private static class MyContextHandler extends Handler{
        /* 建立弱引用 */
        private WeakReference<MyHandlerActivity> mOuter;

        /* 构造函数 */
        public MyContextHandler(MyHandlerActivity activity) {
            mOuter = new WeakReference<MyHandlerActivity>(activity);
        }

        /** 处理函数*/
        public void handleMessage(Message msg) {
            // 防止内存泄露
            MyHandlerActivity outer = mOuter.get();
            if (outer != null) {
                outer.handleservice(msg);
            }
        }
    }

    /** 消息处理函数 */
    private void handleservice(Message msg) {
        // 根据msg的类型来处理数据
        switch (msg.what) {
        case TAG1:
            // 此处可以更新UI
            Bundle b = msg.getData();
            String color = b.getString("color");
            String btn = msg.obj.toString();
            Toast.makeText(MyHandlerActivity.this, color + btn, Toast.LENGTH_SHORT).show();
            break;
        default:
            break;
        }
    }

2、post、postDelayed、removeCallBacks:的简单使用:
可用于延时更新主界面,或者设置定时器

 private Button bu1, bu2;
    private TextView text , text2;
    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        mHandler = new Handler();    
        bu1.setOnClickListener(this);
        bu2.setOnClickListener(this);
    }

    //Runnable线程
    Runnable updateRunnable = new Runnable() {
        @Override
        public void run() {
            text.append("a");
        }
    };

    Runnable cicleRunnable = new Runnable() {
        @Override
        public void run() {
            text2.append("b");
            // 实现效果会是每隔一秒循环添加
            mHandler.postDelayed(cicleRunnable, 1000);
        }
    };

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.bu1:
            // 直接添加到MessageQueue中
//          mHandler.post(updateRunnable);
            // 延迟1s添加到MessageQueue中
            mHandler.postDelayed(updateRunnable, 1000);
            mHandler.post(cicleRunnable);
            break;
        case R.id.bu2:
            // 取消Runnable的执行
            mHandler.removeCallbacks(updateRunnable);
            mHandler.removeCallbacks(cicleRunnable);
            break;

        default:
            break;
        }
    }

二、Handler使用的一些变种:
1、runOnUiThread,更新主界面;
可以用于一些能够获取到Activity的引用,对其主界面进行直接操作的场景:

    activity.runOnUiThread(new Runnable() {   
        @Override
        publicvoid run() {
            Activity.getSomeTextView().setText("aaaaa");
        }
    });

2、View的post和postDelayed方法
和Handler的post与postDelayed用法相同,方法中Runnable运行在主线程中:

 text.post(new Runnable() {
        @Override
        publicvoid run() {
            button.setText("aaaa");
        }
    });

    text.postDelayed(new Runnable() {
        @Override
        publicvoid run() {
            button.setText("bbbb");
        }
    }, 2000);

3、HandlerThread:
在我们的应用程序当中为了实现同时完成多个任务,所以我们会在应用程序当中创建多个线程。为了让多个线程之间能够方便的通信,我们会使用Handler实现线程间的通信。
下面我们看看如何在线程当中实例化Handler。在线程中实例化Handler我们需要保证线程当中包含Looper(注意:UI-Thread默认包含Looper)。
为线程创建Looper的方法如下:在线程run()方法当中先调用Looper.prepare()初始化Looper,然后再run()方法最后调用Looper.loop(),这样我们就在该线程当中创建好Looper。(注意:Looper.loop()方法默认是死循环)
我们实现Looper有没有更加简单的方法呢?当然有,这就是我们的HandlerThread。我们来看下Android对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要注意start,start之后,是一个含有一个looper的新线程。这个looper可以用来创建其他的Handler对象;

三种Handler创建方式及简单使用对比如下:

 private Handler uiHandler;  // 主UI线程
    private Handler mainHandler;// HandlerThread线程Handler
    private Handler subHandler; // 普通线程Handler

    protectedvoid onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        // 启动HandlerThread,这里简化了Handler的创建流程
        HandlerThread handlerThread = new HandlerThread("mHandlerThread");
        handlerThread.start();

        mainHandler = new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg){
                switch (msg.what) {
                case 1:
                    // 注:这里已经不是主UI线程,不能操作主界面
//                  text.setText("11111");
                    Log.i("TAG", "11111");
                    break;
                }
            }
        };

        // 标准的子线程Handler创建流程
        SubThread subThread = new SubThread();
        subThread.start();

        uiHandler = new Handler(getMainLooper()){
            @Override
            public void handleMessage(Message msg){
                switch (msg.what) {
                case 3:
                    text.setText("33333");
                    Log.i("TAG", "3333");
                    break;
                }
            }
        };
    }

    // 普通线程创建Handler流程(这里书写得并不标准,会造成内存泄露)
    private class SubThread extends Thread{
        @Override
        public void run(){
            Looper.prepare();

            subHandler = new Handler(){
                @Override
                public void handleMessage(Message msg){
                    switch (msg.what) {
                    case 2:
                        // 这里是子线程,无法更新UI界面
//                      text.setText("22222");
                        Log.i("TAG", "2222");
                        break;
                    }
                }
            };

            Looper.loop();
        }
    }

    @Override
    publicvoid onClick(View v) {

        switch (v.getId()) {
        case R.id.bu1:         
            mainHandler.sendEmptyMessage(1);
            break;
        case R.id.bu2:         
            subHandler.sendEmptyMessage(2);
            break;
        case R.id.bu3:         
            uiHandler.sendEmptyMessage(3);
            break;
        default:
            break;
        }
    }

由之前分析过得handler与Looper之间的关系也可以看出,Handler处理事件及其所在线程与其相关的Looper相关;Looper使用TLS机制保存,是线程唯一的;

4、异步事务处理AsyncTask:
AsyncTask类为将耗时的操作移到后台线程,并在操作中同步更新UI线程实现了最佳的实践模式及封装。
在Android中实现异步任务机制有两种方式,Handler和AsyncTask。
Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制。
AsyncTask的声明如下:

public abstract class AsyncTask<Params, Progress, Result>

泛型函数,明显看出后面三个数据类型分别对应于参数类型,返回进度数据类型,以及最终返回结果数据类型;使用实例为下:

private class myAsyncTask extends AsyncTask<String, Integer, String>
{
    //执行前先进性的初始化操作(可选)
    @Override
    protected void onPreExecute() {
    }

    //Task操作主体(abstract函数,必须重写)
    //参数(String类型),由execute传递过来
    @Override
    protected String doInBackground(String... params) {
        return null;
    }

    //运行在UI线程(可选)
    //实时更新进度条等UI元素
    @Override
    protected void onProgressUpdate(Integer... progress){
    }

    //doInBackground完成后,返回值会传入事件处理程序中(可选)
    @Override
    protected void onPostExecute(String result){
    }   
}

代码中使用得:

    new myAsyncTask().execute(params);

5、IdlerHandler:
使用IdlerHandler可以向当前线程的消息队列里发送操作,这些操作之后在空闲的时候才会执行,它们的优先级非常低。需要注意的是queueIdle的方法可以返回一个布尔值,如果返回false,说明IdleHandler执行一次之后就会被删除,如果返回true,说明IdleHandler一直是有效的,只要系统处于空闲状态就执行IdleHandler的queueIdle方法。使用方式如下:

    Looper.myQueue().addIdleHandler(new IdleHandler() {
        @Override
        public boolean queueIdle() {
            text.setText("4444");
            return false;
        }
    });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值