Handler使用总结

原创 2015年07月07日 20:53:23

一、最常见的子线程通过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;
        }
    });
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Handler 使用学习总结

总结handler的使用,为小挑做知识储备。为什么要使用handlerHandler的实现离不开Looper MessageQueue 三者紧密的联系在一个,Looper 相当于一个读取Message...

Handler使用总结

一、Handler的定义:           主要接受子线程发送的数据, 并用此数据配合主线程更新UI.           解释: 当应用程序启动时,Android首先会开启一个主线程...

Android中Handler使用总结

一、Handler的定义:     Handler主要接收子线程发送的数据, 并用此数据配合主线程更新UI,用来跟UI主线程交互用。比如可以用handler发送一个message,然后在hand...

android中的通信机制总结1:使用handler来进行通信

使用handler来进行通信    handler 大家可以把它想象成主线程(UI线程)的一个子线程,它可以给主线程(UI线程)发送数据从而更新主线程(UI线程)的UI与逻辑,handler 是一个...

Handler,Looper,Message,MessageQueue,HandlerThread使用总结(上)

我们都知道,android的UI操作并不是线程安全的,因此如果多个线程同时操作UI的话,会导致线程安全问题,因此android制订了一条规则,只允许UI线程(即主线程)进行UI操作,而且由于一些耗时操...

Android中使用Handler造成内存泄露的分析总结

内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收...

android handler使用总结

Handler主要接受子线程发送的数据, 并用此数据配合主线程更新UI。 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) ,主线程为管理界面中的UI控件,进行事件分发, 比如...

Handler使用方法总结

在看这篇博客之前,最好看看《Anroid异步消息机制(Handler、Looper、Message、MessageQueue)以及ThreadLocal运用》了解Handler原理。 Handler...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)