Handler学习小结

  写在前面的总结:

  Handler其实可以作为一个宏观的消息传递机制来看,平时我们在主线程中实例化一个handler,并重写handleMessage方法,在子线程中完成耗时操作,并拿着主线程中初始化的handler来发送消息给主线程,告诉主线程:你可以更新UI啦。这就是一个完整的消息传递流程。这个流程其实也就是最常用的子线程拿着主线程的handler发送消息给主线程,这个handler就像是一个信使。

  主线程handler,handleMessage()  <-- 子线程拿着主线程handler.sendMessage()就完成啦。

  明白了这个原理,其实反过来就可以完成主线程发送消息给子线程了。其实也就是主线程拿着子线程中初始化的handler发送消息给子线程,handler充当这个传话的中间人。流程如下:子线程中初始handler,子线程重写handleMessage方法,当主线程想要发送消息给子线程了就拿着子线程中初始化好的handler对象给子线程发消息就好了。

  子线程handler,handleMessage() <--  主线程拿着子线程的handler.sendMessage()就完成啦。

  只是子线程中初始化的这个handler需要我们自己获取looper罢了。当然也有Android为我们实现好的现成的HandlerThread这个类供我们使用。

  下面的具体用法是从网上找的其他人的博客,摘抄出来的例子。


一、Handler的两种使用场景,

其实就是子线程完成任务后发送消息给主线程,让主线程来进行界面的更新

(1)异步更新UI -- sendMessage和post两种方式

(2)延时任务


(1)异步更新UI操作的两种使用sendMessage或者post

private TextView tv_up;
    private String new_str = "";
    /*post方法解决UI更新问题handler创建方式*/
    private Handler handler_post = new Handler();
    /*sendMessage方法解决UI更新问题handler创建方式*/
    Handler handler_senM = new Handler() {
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                /*sendMessage方法更新UI的操作必须在handler的handleMessage回调中完成*/
                tv_up.setText(new_str);
            }
        };
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new Runnable() {

            @Override
            public void run() {
                new_str = "更新UI";
                /*sendMessage方法解决UI更新发送消息给handler(主线程中的handler)*/
                handler_senM.sendEmptyMessage(1);
                /*post方法解决UI更新,直接在runnable里面完成更新操作,这个任务会被添加到handler所在线程的消息队列中,即主线程的消息队列中*/
                handler_post.post(new Runnable() {

                    @Override
                    public void run() {
                        tv_up.setText(new_str);
                    }
                });
            }
        }).start();
    }
post和sendMessage只是用法上的区别,本质是没有区别的。

最终总结: 
1. post和sendMessage本质上是没有区别的,只是实际用法中有一点差别 
2. post也没有独特的作用,post本质上还是用sendMessage实现的,post只是一中更方便的用法而已


(2)延时执行

@Override
    public void onClick(View v) {
        DownloadThread downloadThread = new DownloadThread();
        downloadThread.start();
    }

    class DownloadThread extends Thread{
        @Override
        public void run() {
            try{
                System.out.println("DownloadThread id " + Thread.currentThread().getId());
                System.out.println("开始下载文件");
                //此处让线程DownloadThread休眠5秒中,模拟文件的耗时过程
                Thread.sleep(5000);
                System.out.println("文件下载完成");
                //文件下载完成后更新UI
                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("Runnable thread id " + Thread.currentThread().getId());
                        MainActivity.this.statusTextView.setText("文件下载完成");
                    }
                };
                uiHandler.post(runnable,1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

二、主线程向子线程发送消息,

在子线程中使用handler

  主线程向子线程发送消息的话,我们需要在子线程里初始化Looper,并在主线程里创建的Handler引用子线程的Looper(Handler中引用的是哪个线程的Looper,就在哪个线程里处理消息)。

  在子线程中接收消息,我们要用到Looper,主线程不用,因为主线程已经默认使用Looper了,我们会使用looper的两个主要方法,一个是prepare和loop,前一个是创建Looper对象,后一个是执行Looper循环功能。

public class ThreadHandlerActivity extends Activity{     
     //创建子线程
     class MyThread extends Thread{
         private Looper looper;//取出该子线程的Looper
         public void run() {

             Looper.prepare();//创建该子线程的Looper
             looper = Looper.myLooper();//取出该子线程的Looper
             Looper.loop();//只要调用了该方法才能不断循环取出消息
         }
     }

     private Handler mHandler;//将mHandler指定轮询的Looper

     protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.main);
             thread = new MyThread();
             thread.start();//千万别忘记开启这个线程
             //下面是主线程发送消息
             mHandler = new Handler(thread.looper){
                public void handleMessage(android.os.Message msg) {
                   Log.d("当前子线程是----->",Thread.currentThread()+"");
                };
             };
             mHandler.sendEmptyMessage(1);//主线程的handler发送消息给子线程
     }

 }

  其实这样就可以达到主线程向子线程发送消息了,然而当我们运行后发现程序会Crash掉,报了一个控制针,这是因为在Handler初始化的时候,thread.looper还没有初始化,所以会报控制针,这时我们可以让主线程等待一下子线程,也可以来一个while循环来判断thread.looper是否初始化完成。不过Android本身还提供了一个方法,那就是HandlerThread。

  HandlerThread本质上就是一个普通Thread,只不过内部建立了Looper.

 主线程向子线程发送消息的例子:

protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             tv = new TextView(this);
             tv.setText("Handler实验");
             setContentView(tv);

             //实例化一个特殊的线程HandlerThread,必须给其指定一个名字
            HandlerThread thread = new HandlerThread("handler thread");
             thread.start();//千万不要忘记开启这个线程
             //将mHandler与thread相关联
             mHandler = new Handler(thread.getLooper()){
                 public void handleMessage(android.os.Message msg) {
                     Log.d("当前子线程是----->", Thread.currentThread()+"");  //在这里进行耗时操作
                 };
             };
             mHandler.sendEmptyMessage(1);//拿着主线程的handler发送消息,给主线程处理
     }

HandlerThread主要还是使用在子线程向主线程发送消息,具体使用如下:

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView tvMain;

    private HandlerThread mHandlerThread;
    //子线程中的handler
    private Handler mThreadHandler;
    //UI线程中的handler
    private Handler mMainHandler = new Handler();

    //以防退出界面后Handler还在执行
    private boolean isUpdateInfo;
    //用以表示该handler的常熟
    private static final int MSG_UPDATE_INFO = 0x110;

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

        tvMain = (TextView) findViewById(R.id.tv_main);

        initThread();
    }


    private void initThread()
    {
        mHandlerThread = new HandlerThread("check-message-coming");
        mHandlerThread.start();

        mThreadHandler = new Handler(mHandlerThread.getLooper())
        {
            @Override
            public void handleMessage(Message msg) //进行耗时操作
            {
                update();//模拟数据更新,进行耗时操作

                if (isUpdateInfo)
                    mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
            }
        };

    }

    private void update()
    {
        try
        {
            //模拟耗时
            Thread.sleep(2000);
            mMainHandler.post(new Runnable()  //子线程完成耗时操作后,在主线程中更新UI
            {
                @Override
                public void run()
                {
                    String result = "每隔2秒更新一下数据:";
                    result += Math.random();
                    tvMain.setText(result);
                }
            });

        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }

    }

    @Override
    protected void onResume()
    {
        super.onResume();
        //开始查询
        isUpdateInfo = true;
        mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
    }

    @Override
    protected void onPause()
    {
        super.onPause();
        //停止查询
        //以防退出界面后Handler还在执行
        isUpdateInfo = false;
        mThreadHandler.removeMessages(MSG_UPDATE_INFO);
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        //释放资源
        mHandlerThread.quit();
    }
}

HandlerThread的特点

  • HandlerThread将loop转到子线程中处理,说白了就是将分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
  • 开启一个线程起到多个线程的作用。处理任务是串行执行,按消息发送顺序进行处理。
    相比多次使用new Thread(){…}.start()这样的方式节省系统资源。
    但是由于每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。
  • HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。
  • 通过设置优先级就可以同步工作顺序的执行,而又不影响UI的初始化;

总结

HandlerThread比较适用于单线程+异步队列的场景,比如IO读写操作,耗时不多而且也不会产生较大的阻塞。对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着。

三、子线程之间相互通信

  其实子线程向子线程之间通信,其实就是在一个子线程中创建一个Handler,它的回调自然就在此子线程中,然后在另一个子线程中调用此handler来发送消息就可以了,不过记得写上Looper哦,下面看代码:

new Thread(new Runnable() {

                      @Override
                      public void run() {
                          String msg;
                          Looper.prepare();

                          childHandler = new Handler() {
                              @Override
                    public void handleMessage(Message msg) {
                                 super.handleMessage(msg);

                                 System.out.println("这个消息是从-->>" + msg.obj+ "过来的,在" + "btn的子线程当中" + "中执行的");

                             }

                         };  
                      Looper.loop();//开始轮循

                     }
                 }).start();

然后我们创建第二个子线程:

new Thread(new Runnable() {

                      @Override
                      public void run() {
                         Looper loop = Looper.myLooper();
               Message msg = childHandler.obtainMessage();
                         msg.obj = "btn2当中子线程";
                         childHandler.sendMessage(msg);
                     }
                 }).start();

下面这个是上面的更加完整的代码:

public class CounterActivity extends AppCompatActivity {
    public Button pressBtn;
    public TextView textView;
    //在这里定义一个属于主界面的线程类,这个线程类必须包含一个Handler成员
    //而且这个Handler不能是私有的
    //而且重写了handleMessage方法
    public class mThread extends Thread
    {
        public Handler cHandler;
        @Override
        public void run()
        {
            //初始化Looper
            Looper.prepare();
            cHanler=new Handler()
            {
                @Override
                public void handleMessage(msg)
                {
                    //对msg进行处理,获得主线程传来的数据
                    //对数据进行处理,进行耗时操作
                }
            };
            Looper.loop();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_counter);
        //实例化一个上面定义的线程类,并启动它
        final mThread mthread=new mThread();
        mthread.start();
        pressBtn.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v) 
            {
                //当按钮被单击的时候,需要向已经启动的线程mthread
                //传递数据,这里就要使用Handler
                //将数据封装到Message对象中
                Message msg=new Message();
                //调用子线程的Handler,将msg发送给子线程
                mthread.cHandler.sendMessage(msg);      //主线程拿到子线程的handler发消息,就发送给了子线程的handleMessage方法执行
            }
        });
    }
}

上面的例子中,在实际开发中遇见过一次crash,就是执行mthread.cHandler.sendMessage(msg);时概率性地报空指针异常,因为原来代码中的cHandler的初始化写在了mthread.start();的后面,也就是当子线程运行时,有可能cHandler仍然没有初始化完成,因此,这个实例的初始化应该在启动线程执行之前进行。

  对比学习下,在子线程向主线程发送消息的过程:

public class MainActivity extends AppCompatActivity {
    //由上面的讲解,显然我们要在这个MainActivity中定义一个Handler
    public Handler mHandler;
    public Button pressBtn;
    public TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_counter);
        //在这里重写mHandler的handleMessage方法
        mHandler=new Handler()
        {
            @Override
            public void handleMessage(Message msg)
            {
                //对msg进行处理,获得其中的数据也就是计算结果
                //修改用户界面
                textView.setText("result");
            }
        };
        pressBtn.setOnClickListener(new View.OnClickListener() 
        {
            @Override
            public void onClick(View v) 
            {

                new Thread()
                {
                    @Override
                    public void run()
                    {
                    //在这里进行一段复杂计算
                    //可以用Thread.sleep(1000);代替
                        try 
                        {
                            Thread.sleep(1000);
                        } 
                        catch (InterruptedException e) 
                        {
                            e.printStackTrace();
                        }
                        //启动一个线程进行一个复杂计算
                        //当计算完成时,我们不能这样写:
                        //textView.setText("result");
                        //而是要通过主线程的Handler通知主线程
                        Message msg=new Message();
                        //应当对要传递的Message进行定制,
                        //将计算结果附在Message对象中
                        mHandler.sendMessage(msg);   //子线程拿着主线程的handler发送消息,就传送给了主线程的handleMessage方法执行
                    }
                }.start();
            }
        });
    }
}

  由上面的例子,我们就可以想到,在子线程中声明一个Handler对象,并且实现它的handleMessage(Message msg)方法;然后在主线程中调用子线程的Handler的sendMessage(Message msg)方法向子线程发送数据。就实现了主线程-->子线程的消息传递。

  这里就应该把Handler作为一个宏观的消息传递机制来看而不仅仅是一个类了。因为它和Looper,MessageQueue,Message是作为一个整体完成消息传递任务的。

猜你喜欢

转载自blog.csdn.net/cpcpcp123/article/details/79593121