Handler使用总结

来源:互联网 发布:藏绿阁三角梅淘宝 编辑:程序博客网 时间:2024/06/05 22:43

一、最常见的子线程通过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
原创粉丝点击