Android的消息机制,handler多种用法

笔记。。。。。。。。。

Android的消息机制(Android消息机制是为了解决在子线程中无法访问UI的矛盾。)

    描述:Android应用程序是通过消息来驱动的。
Handler机制主要运用
1.)发送消息,在不同的线程间发送消息,使用的方法为sendXXX();
 handler.sendEmptyMessage(int);//发送一个空的消息bb 
      handler.sendMessage(Message);//发送消息,消息中可以携带参数
      handler.sendMessageAtTime(Message, long);//未来某一时间点发送消息
      handler.sendMessageDelayed(Message, long);//延时Nms发送消息
    2.)计划任务,在未来执行某任务,使用的方法为postXXX();
 handler.post(Runnable);//提交计划任务马上执行
      handler.postAtTime(Runnable, long);//提交计划任务在未来的时间点执行
      handler.postDelayed(Runnable, long);//提交计划任务延时Nms执行
    Handler机制扩展(推荐使用)
1.) Activity.runOnUiThread(Runnable)
2.)View.post(Runnable)
3.)使用AsyncTask代替Thread
   1.为什么不允许在非主线程访问UI呢?          这是因为Android的UI控件不是线程安全的。并且UI访问没有锁机制,并发访问会导致控件处于不可预期的状态。
   2.那为什么不对UI访问加上锁机制呢?
     (1)这显然会让UI访问的逻辑变得极其复杂;
     (2)锁机制自然会降低效率;
     (3)锁机制还会阻塞某些进程的执行。
 MessageQueue
   采用以单链表为数据存储结构的消息列表。对外提供插入(enqueueMessage)和读取(next)。读取本身附带删除操作。单链表结构在插入和删除上比较有优势。读取是一个无限循环,如果消息队列中没有消息就阻塞,当有新消息的时候就返回这条消息,并将其从单链表中删除。
 Looper
   loop()方法是一个死循环,会调用消息队列中的next()方法,next是一个阻塞操作,只有next返回了新消息,Looper才会处理这条消息。跳出looper死循环的唯一条件是MessageQueue的读取方法next()返回null。当looper的quit()方法被调用时,消息队列就会被标记为退出状态,她的next()方法也就返回了null。所以looper必须退出,否则loop()方法会一直执行。
 1.如何为一个线程创建Looper
   (1)UI线程默认就会初始化Looper.
   (2)子线程默认没有Looper,Handler创建钱必须手动创建Looper,否则会报错。
         Looper.prepare();创建一个Looper     Looper.loop();开启消息循环
   (3)使用HandlerThread继承自Thread,和普通的thread不同的是其内部已经封装了Looper.   
 Handler
 
    主要负责消息的发送和接收。
注:handler.post()和handler.sendMessage()原理都是封装成了Message。
 ##整个handler机制的原理
     这篇文章写的很详细可以多看几遍:http://blog.csdn.net/ly502541243/article/details/52062179/
特别说明:
1.handler造成内存泄漏的原因   (Handler mHandler=new Handler)          

  在java中非静态内部类和匿名内部类都会隐式持有当前类的外部引用,由于Handler是非静态内部类所以其持有当前Activity的隐式引用,如果Handler没有被释放,其所持有的外部引用也就是Activity也不可能被释放,当一个对象一句不需要再使用了,本来该被回收时,而有另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏OOM。

    2.解决方法

 /**
     * 主线程接收消息,更新UI
     */
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg != null) {
                String message = "";
                switch (msg.what) {
                    case 1:
                        message = (String) msg.obj;
                        //更新UI
                        Log.i("TAG", "请求成功--接收到的子线程消息=" + message);
                        break;
                    case 2:
                        Bundle bundle = msg.getData();//同Bundle bundle = (Bundle) msg.obj; 对应使用
                        String name = bundle.getString("name");
                        int age = bundle.getInt("age");
                        Log.i("TAG", "请求成功--接收到的子线程消息=" + "name=" + name + ",age=" + age);
                        break;
                    case 3:
                        message = (String) msg.obj;
                        Log.i("TAG", "请求成功--接收到的子线程消息=" + message);
                        break;
                }
            } else {
                Log.i("TAG", "空消息!!!");
            }
        }
    };

使用handler更新UI的几种常见方式:

方法一:

 new Thread(new Runnable() {
            @Override
            public void run() {
                //耗时操作,可以去请求网络。。。
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //发消息给主线程
//                Message message=new Message();
//                message.what=1;//此条消息的标记,同一个handler可能会发送很多条消息,使用此标记进行区分
//                message.arg1=10;//若仅仅传递一个整数,可以使用arg1
//                message.obj="这是来自子线程的handlerMessage消息";//message要携带的数据
//                handler.sendMessage(message);//发带有数据的消息给主线程
//                handler.sendEmptyMessage(1);//发空消息
//                handler.sendEmptyMessageDelayed(1,1000);//延迟1秒发消息
                //为了节省新new对象的开销,最好使用obtainMessage()
                Message msg = handler.obtainMessage(1);
                msg.obj = "这是来自子线程方法1的handlerMessage消息";
                msg.sendToTarget();
            }
        }).start(); 

方法二:

//法2,
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //耗时操作
                Message msg = handler.obtainMessage(2);
                Bundle bundle = new Bundle();
                bundle.putString("name", "yhy");
                bundle.putInt("age", 26);
                msg.setData(bundle);//同msg.obj=bundle;
                msg.sendToTarget();
            }
        }, 3000);

方法三:

  new Thread("thread2") {
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //View特有的方法,可以在子线程中更新UI
                text.post(new Runnable() {
                    @Override
                    public void run() {
                        text.setText("方法一更新UI啦》》》》");
                        Log.i("TAG", "方法一更新UI啦》》》》");
                    }
                });
            }
        }.start(); 

方法四:

 //方法二,注:1.用view.getContext()可以得到上下文。
        //2.跳过context直接用new Activity().runOnUiThread(Runnable action)来切换到主线程。
        CurrentCircleActivity.this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //此时已经在主线程中了,可以更新UI了
                text.setText("方法二更新UI啦。。。。。");
                Log.i("TAG", "方法二更新UI啦。。。。。");
            }
        }); 

方法五:

 new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //通过Looper.getMainLooper()方法,已经处在主线程中,可以更新UI了
                Handler mHandler = new Handler(Looper.getMainLooper());
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        text.setText("更新UI啦");
                        Log.i("TAG", "更新UI啦");
                    }
                });
            }
        }).start(); 

方法六:

 /**
     * 该类中方法的执行顺序依次为:onPreExecute,doInBackground,onPostExectue
     * 3秒更新进度条操作
     */
    private class MyAsyncTask extends AsyncTask<String, Integer, String> {

        /**
         * 在主线程中执行
         * 在execute被调用后首先执行
         * 一般用来在执行后台任务前对UI做一些标记
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Log.i("TAG", "onPreExecute...");
        }

        /**
         * 子线程中执行,执行一些耗时操作,关键方法
         * 在执行的过程哄可以调用publishprogress()来更新进度信息
         *
         * @param strings
         * @return
         */
        @Override
        protected String doInBackground(String... strings) {
            Log.i("TAG", "doInBackground....");
            int count = 0;
            for (int i = 0; i < 10; i++) {
                try {
                    count++;
                    publishProgress((count % 100) * 10);
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return "耗时任务执行完成";
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            progressbar.setProgress(values[0]);
            Log.i("TAG", "onProgressUpdate..." + values[0] + "%");
        }

        /**
         * 在主线程中,当后台操作结束时,此方法会被调用
         * 计算结果将作为参数传递到此方法中,直接将结果显示在UI上
         *
         * @param s
         */
        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            Log.i("TAG", "onPostExecute..." + "子线程执行结束,结果为=" + s);
        }

        /**
         * 在主线程中执行,当异步任务取消后,会回调该方法。在该方法内可以更新UI
         */
        @Override
        protected void onCancelled() {
            super.onCancelled();
            progressbar.setProgress(0);
            Log.i("TAG", "onCancelled...");
        }

        @Override
        protected void onCancelled(String s) {
            super.onCancelled(s);
            Log.i("TAG", "onCancelled..." + s);
        }
    }

原理:

1.android的消息机制主要指的是handler的运行机制,以及底层的messgeQueue和Lopper的工作过程。Handdler的主要作用是负责线程间通信。在网络请求完成后,通过handler告诉UI线程来更新UI,并传递数据。

 (1)主线程可以默认使用handler的原因是,因为主线程ActivityThread被创建时就会默认初始化Looper,而子线程没有,需要我们自己创建。 (2)系统为什么不允许在子线程中访问UI呢?这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问会导致UI控件处于不可预测的状态。那么为什么系统不对UI控件的访问加上锁机制呢?缺点有两个:首先,加上锁机制会让UI的访问逻辑变得复杂;其次,锁机制会减低UI的访问效率,因为锁机制会阻塞某些线程的执行。鉴于以上两点,最简单高效的方法就是使用单线程模型来处理UI操作。只需要通过Handler来切换一下UI访问的执行线程即可。
 2.ThreadLocal的工作原理
ThreadLocal是一个线程内部的数据存储类。(Looper的作用域是线程,并且不同的线程有不同的Looper,那么通过ThreadLocal就可以轻松的实现Looper在线程中的存取)从ThreadLocal的get和set方法中可以看出,它们操作的都是当前线程的localValues对象的table数组,因此不同线程中访问同一个LocalThread的get和set方法,它们对LocalThread的读写操作都位于各自线程的内部,所以ThreadLocal可以在多线程中互不干扰的存储和修改数据。
 3.消息队列MessageQueue的工作原理 MessaeQueue主要包含两个操作:插入enqueueMessage()和读取next()(注:其中读取的本身伴随着删除操作)。MessageQueue实际上是以单链表数据结构来维护消息列表的。
4.Looper的工作原理
   在Android的消息机制中,Looper主要负责不停的循环查看MessageQueue中是否有新消息,如果有则立刻处理,没有则一直阻塞在那里。

handler创建时会采用当前线程的Looper来构建内部消息循环系统,如果当前线程没有Looper,则需要我没收到去创建(创建方式:1.Looper.prepare();并调用Looper.loop();开启消息循环。只有调用了loop方法消息循环系统才真正的起作用。2.可以通过Looper.getMainLooper();在任何地方获取主线程的Looper。另外手动创建Looper后,在不需要的时候记得使用quitSafely()退出Looper),否则会报错。 

demo:代码

package com.yhy.testviewdemo.activity;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.yhy.testviewdemo.R;
import com.yhy.testviewdemo.bean.PieData;
import com.yhy.testviewdemo.view.PieView;

import java.util.ArrayList;

/**
 * 自定义圆状图
 */
public class CurrentCircleActivity extends AppCompatActivity implements View.OnClickListener{
    private TextView text;
    private ProgressBar progressbar;
    private MyAsyncTask myAsyncTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题
        setContentView(R.layout.activity_current_circle);
        text = (TextView) findViewById(R.id.text);
        text.setOnClickListener(this);
        progressbar = (ProgressBar) findViewById(R.id.progressbar);
        sendMeaasge();
        upteUI();
        myAsyncTask = new MyAsyncTask();
        myAsyncTask.execute();//执行异步请求
    }


    /**
     * 主线程接收消息,更新UI
     */
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg != null) {
                String message = "";
                switch (msg.what) {
                    case 1:
                        message = (String) msg.obj;
                        //更新UI
                        Log.i("TAG", "请求成功--接收到的子线程消息=" + message);
                        break;
                    case 2:
                        Bundle bundle = msg.getData();//同Bundle bundle = (Bundle) msg.obj; 对应使用
                        String name = bundle.getString("name");
                        int age = bundle.getInt("age");
                        Log.i("TAG", "请求成功--接收到的子线程消息=" + "name=" + name + ",age=" + age);
                        break;
                    case 3:
                        message = (String) msg.obj;
                        Log.i("TAG", "请求成功--接收到的子线程消息=" + message);
                        break;
                }
            } else {
                Log.i("TAG", "空消息!!!");
            }
        }
    };

    /**
     * 发送消息
     */
    public void sendMeaasge() {
        //法1,
        new Thread(new Runnable() {
            @Override
            public void run() {
                //耗时操作,可以去请求网络。。。
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //发消息给主线程
//                Message message=new Message();
//                message.what=1;//此条消息的标记,同一个handler可能会发送很多条消息,使用此标记进行区分
//                message.arg1=10;//若仅仅传递一个整数,可以使用arg1
//                message.obj="这是来自子线程的handlerMessage消息";//message要携带的数据
//                handler.sendMessage(message);//发带有数据的消息给主线程
//                handler.sendEmptyMessage(1);//发空消息
//                handler.sendEmptyMessageDelayed(1,1000);//延迟1秒发消息
                //为了节省新new对象的开销,最好使用obtainMessage()
                Message msg = handler.obtainMessage(1);
                msg.obj = "这是来自子线程方法1的handlerMessage消息";
                msg.sendToTarget();
            }
        }).start();
        //法2,
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //耗时操作
                Message msg = handler.obtainMessage(2);
                Bundle bundle = new Bundle();
                bundle.putString("name", "yhy");
                bundle.putInt("age", 26);
                msg.setData(bundle);//同msg.obj=bundle;
                msg.sendToTarget();
            }
        }, 3000);

        //发一条空消息
        new Thread("thread1") {
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Message message = Message.obtain();
                message.obj = "发一条空消息";
                handler.sendEmptyMessage(1);
            }
        }.start();
    }

    /**
     * 更新UI
     */
    private void upteUI() {
        //方法一
        new Thread("thread2") {
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //View特有的方法,可以在子线程中更新UI
                text.post(new Runnable() {
                    @Override
                    public void run() {
                        text.setText("方法一更新UI啦》》》》");
                        Log.i("TAG", "方法一更新UI啦》》》》");
                    }
                });
            }
        }.start();
        //方法二,注:1.用view.getContext()可以得到上下文。
        //2.跳过context直接用new Activity().runOnUiThread(Runnable action)来切换到主线程。
        CurrentCircleActivity.this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //此时已经在主线程中了,可以更新UI了
                text.setText("方法二更新UI啦。。。。。");
                Log.i("TAG", "方法二更新UI啦。。。。。");
            }
        });
        //方法三,
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //通过Looper.getMainLooper()方法,已经处在主线程中,可以更新UI了
                Handler mHandler = new Handler(Looper.getMainLooper());
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        text.setText("更新UI啦");
                        Log.i("TAG", "更新UI啦");
                    }
                });
            }
        }).start();
    }

    @Override
    public void onClick(View v) {
        //取消异步请求,回调AsyncTask中的onCancelled()方法
        myAsyncTask.cancel(true);
    }

    /**
     * 该类中方法的执行顺序依次为:onPreExecute,doInBackground,onPostExectue
     * 3秒更新进度条操作
     */
    private class MyAsyncTask extends AsyncTask<String, Integer, String> {

        /**
         * 在主线程中执行
         * 在execute被调用后首先执行
         * 一般用来在执行后台任务前对UI做一些标记
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Log.i("TAG", "onPreExecute...");
        }

        /**
         * 子线程中执行,执行一些耗时操作,关键方法
         * 在执行的过程哄可以调用publishprogress()来更新进度信息
         *
         * @param strings
         * @return
         */
        @Override
        protected String doInBackground(String... strings) {
            Log.i("TAG", "doInBackground....");
            int count = 0;
            for (int i = 0; i < 10; i++) {
                try {
                    count++;
                    publishProgress((count % 100) * 10);
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return "耗时任务执行完成";
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            progressbar.setProgress(values[0]);
            Log.i("TAG", "onProgressUpdate..." + values[0] + "%");
        }

        /**
         * 在主线程中,当后台操作结束时,此方法会被调用
         * 计算结果将作为参数传递到此方法中,直接将结果显示在UI上
         *
         * @param s
         */
        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            Log.i("TAG", "onPostExecute..." + "子线程执行结束,结果为=" + s);
        }

        /**
         * 在主线程中执行,当异步任务取消后,会回调该方法。在该方法内可以更新UI
         */
        @Override
        protected void onCancelled() {
            super.onCancelled();
            progressbar.setProgress(0);
            Log.i("TAG", "onCancelled...");
        }

        @Override
        protected void onCancelled(String s) {
            super.onCancelled(s);
            Log.i("TAG", "onCancelled..." + s);
        }
    }
}

运行结果:

06-12 16:09:29.071 9905-9905/com.yhy.testviewdemo I/TAG: 方法二更新UI啦。。。。。
06-12 16:09:29.071 9905-9905/com.yhy.testviewdemo I/TAG: onPreExecute...
06-12 16:09:29.071 9905-10358/com.yhy.testviewdemo I/TAG: doInBackground....
06-12 16:09:29.077 9905-9905/com.yhy.testviewdemo I/TAG: onProgressUpdate...10%
06-12 16:09:29.095 9905-9905/com.yhy.testviewdemo I/TAG: width=: 1080--oldw=0
06-12 16:09:29.095 9905-9905/com.yhy.testviewdemo I/TAG: height=: 1635--oldh=0
06-12 16:09:31.072 9905-9905/com.yhy.testviewdemo I/TAG: 请求成功--接收到的子线程消息=null
06-12 16:09:31.073 9905-9905/com.yhy.testviewdemo I/TAG: 更新UI啦
06-12 16:09:32.072 9905-9905/com.yhy.testviewdemo I/TAG: 请求成功--接收到的子线程消息=这是来自子线程方法1的handlerMessage消息
06-12 16:09:32.073 9905-9905/com.yhy.testviewdemo I/TAG: 方法一更新UI啦》》》》
06-12 16:09:32.073 9905-9905/com.yhy.testviewdemo I/TAG: onProgressUpdate...20%
06-12 16:09:32.073 9905-9905/com.yhy.testviewdemo I/TAG: 请求成功--接收到的子线程消息=name=yhy,age=26
06-12 16:09:35.074 9905-9905/com.yhy.testviewdemo I/TAG: onProgressUpdate...30%
06-12 16:09:38.074 9905-9905/com.yhy.testviewdemo I/TAG: onProgressUpdate...40%
06-12 16:09:41.075 9905-9905/com.yhy.testviewdemo I/TAG: onProgressUpdate...50%
06-12 16:09:44.075 9905-9905/com.yhy.testviewdemo I/TAG: onProgressUpdate...60%
06-12 16:09:47.075 9905-9905/com.yhy.testviewdemo I/TAG: onProgressUpdate...70%
06-12 16:09:50.076 9905-9905/com.yhy.testviewdemo I/TAG: onProgressUpdate...80%
06-12 16:09:53.076 9905-9905/com.yhy.testviewdemo I/TAG: onProgressUpdate...90%
06-12 16:09:56.076 9905-9905/com.yhy.testviewdemo I/TAG: onProgressUpdate...100%
06-12 16:09:56.852 22134-22134/? W/KeyguardUpdateMonitor: ChargingSpeed  Wattage: -1 ST: 5000000 --> 7500000
06-12 16:09:59.076 9905-9905/com.yhy.testviewdemo I/TAG: onPostExecute...子线程执行结束,结果为=耗时任务执行完成 

点击TextView执行onClick()方法后执行结果:(ps:取消异步请求操作)

06-12 16:14:10.388 10497-10497/com.yhy.testviewdemo I/TAG: 方法二更新UI啦。。。。。
06-12 16:14:10.389 10497-10497/com.yhy.testviewdemo I/TAG: onPreExecute...
06-12 16:14:10.389 10497-10539/com.yhy.testviewdemo I/TAG: doInBackground....
06-12 16:14:10.403 10497-10497/com.yhy.testviewdemo I/TAG: width=: 1080--oldw=0
06-12 16:14:10.403 10497-10497/com.yhy.testviewdemo I/TAG: height=: 1635--oldh=0
06-12 16:14:10.404 10497-10497/com.yhy.testviewdemo I/TAG: onProgressUpdate...10%
06-12 16:14:12.389 10497-10497/com.yhy.testviewdemo I/TAG: 请求成功--接收到的子线程消息=null
06-12 16:14:12.396 10497-10497/com.yhy.testviewdemo I/TAG: 更新UI啦
06-12 16:14:13.390 10497-10497/com.yhy.testviewdemo I/TAG: 请求成功--接收到的子线程消息=这是来自子线程方法1的handlerMessage消息
06-12 16:14:13.391 10497-10497/com.yhy.testviewdemo I/TAG: onProgressUpdate...20%
06-12 16:14:13.391 10497-10497/com.yhy.testviewdemo I/TAG: 请求成功--接收到的子线程消息=name=yhy,age=26
06-12 16:14:13.392 10497-10497/com.yhy.testviewdemo I/TAG: 方法一更新UI啦》》》》
06-12 16:14:38.392 10497-10497/com.yhy.testviewdemo I/TAG: onCancelled...
06-12 16:14:38.392 10497-10497/com.yhy.testviewdemo I/TAG: onCancelled...耗时任务执行完成

学习博客:点击打开链接https://blog.csdn.net/da_caoyuan/article/details/52931007


 

猜你喜欢

转载自blog.csdn.net/yhy123456q/article/details/80666730
今日推荐