Android中线程的通信机制:消息机制(一)

Android中线程的通信机制:消息机制(一)

众所周知在Android系统中,主线程是不可以执行耗时操作的,子线程可以执行耗时操作但是不可以直接更新UI,所以当子线程在执行耗时操作的过程中需要更新UI,可以发出一个“消息”给主线程,然后主线程接收到通知后就更新UI,从而完成协作。

因此在消息机制中,会使用到:

  • Handler  消息的发送者和处理者
  • Message  消息的载体
  • MessageQueue  消息队列,是消息的容器(下节讲到)
  • Looper  轮询者(下节讲到)

    运行的机制是,message需要发送消息的时候先封装,然后将消息发送到MessageQueue里面进行存放。消息在处理的时候需要进行依次排队,Looper就不停的循环队列(只要有就不停的拿),每取走一个就将其拿给handler

由此就衍生出几个问题:

  1. 在子线程中运行到那个步骤的时候需要发送消息
  2. 发送的消息需要携带哪些数据
  3. 在哪里接收消息并如何处理消息

我们就带着问题往下看

Handler  消息的发送者和处理者

通过调用Handler对象的sendMessage(Message)方法可以在子线程中向主线程发送消息。通过重写handleMessage(Message)方法可以决定如何处理消息,Handler默认是运行在主线程的,所以,可以直接更新UI。

Handler handler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message message) {
        //接收消息并对消息进行处理
        return false;
    }
});
@Override
public void run() {
    ***
    ***
    //根据自己的情况发送创建消息并发送
    Message msg = new Message();
    handler.sendMessage(msg)
}
  • 使用handler发送消息
  1. boolean sendMessage(Message msg)
  2. boolean sendMessageDelayed(Message msg, long delayMillis)   延迟delayMillis毫秒后发出消息
  3. boolean sendMessageAtTime(Message msg, long uptimeMillis)   在指定的时间点发出消息,其中,参数uptimeMillis表示从1970-01-01 00:00:00至今经历的毫秒数
  4. boolean sendEmptyMessage(int what)   发送空的消息,本质上并不是空消息,而是该方法可以帮助我们能够获取消息对象,使得我们在调用该方法之前,不用手动创建消息对象,参数int what表示消息对象的what属性
  5. boolean sendEmptyMessageDelayed(int what, long delayMillis)   延迟delayMillis毫秒后发出空消息
  6. boolean sendEmptyMessageAtTime(int what, long uptimeMillis)   在指定的时间点发出消息
  • 处理消息的方式
  1. 自定义类继承自Handler,重写void handleMessage(Message msg)方法,在该方法中决定如何处理消息
    static class InnerHandler extends Handler{
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        }

    注:这里要使用static修饰不然会出现内存溢出(数据该释放的时候没有释放,一直留在内存中)。因为在activity中运行时,如果直接finish() activity,此时子线程还在运行,然而Activity所有的成员也会被销毁,子线程在运行就需要通过handler发消息。但是handler这在处于使用状态,也就是此时的handler退不掉。所以会导致内存溢出(虽然所占内存是很小的,但是也是不可取的)

  2. 自定义类实现Handler.Callback,重写boolean handleMessage(Message msg)方法,在该方法中决定如何处理消息,然后创建出自定义的对象,用于new Handler(Handler.Callback callback)构造方法的参数
    class InnerHandlerCallback implements Handler.Callback{
            @Override
            public boolean handleMessage(Message message) {
                return false;
            }
    
        }

    注:这里的handlerMessage(Message msg)方法的返回值和上面1中的不同,这里返回值所指是否消费。

  3. Message类的静态方法obtain(Handler handler, Runnable callback),该方法的参数Runnable callback就是处理消息的对象

     消息的分发(处理消息的优先级),也就是以上3种处理方式,哪一种最优先处理。通过源码(自己点开即可查看)可以得知       处理顺序是3-->2-->1

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
  }

  【小结】:个人认为第三种比较麻烦而1和2比较简单推荐使用,

Message  消息的载体

message 的常用属性有:

  1. int  arg1  用于在消息中封装int类型的数据
  2. int  arg2  用于在消息中封装int类型的数据
  3. object obj  用于在消息中封装任意类型(包括对象)的数据
  4. int what   用于标识消息的类型,通常使用静态常量
private static final int UPLOAD_SUCCESS = 1;
private static final int UPLOADING = 2;
private boolean isUploadSuccess;
private Bena bean = new Bean();
Handler handler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message message) {
        //接收消息并对消息进行处理
        if(message.what = UPLOAD_SUCCESS){
           //执行成功的操作
        } else {
           //执行进度
           Bean bean = (Bean)msg.obj;
           bean.get***
           bean.get***
           ***
           ***
           //更新进度
        }
        return false;
    }
});
@Override
public void run() {
    ***
    ***
    isUploadSuccess = ***;
    Message msg = new Message();
    //根据自己的情况发送创建消息并发送
    //这里封装message的时候需要注意

    情况1.如果我们只需要一个或者两个int类型的数据直接可以
    //msg.arg1 = 1;
    //msg.arg2 = 2;

    情况2.如果我们需要使用String等类型的时候
    //String str = "***"
    //msg.obj = str ;

    情况3.当以上两种都不足以满足的情况需要携带的数据有很多的时候我们就可以通过对象的方式
    //bean.setName(***);
    //bean.setProgress(***);
    //bean.setTotalSize(***);
    //bean.setUploadSize(***)
    //msg.obj = bean;

    if(isUploadSuccess){
      msg.what = UPLOAD_SUCCESS;
    } else {
      msg.what = UPLOADING ;
    }
    handler.sendMessage(msg)
}

class  Bean{

  String name;
  int progress;
  int totalSize;
  int uploadSize;
  public int getTotalSize() {
      return totalSize;
  }
  public void setTotalSize(int totalSize) {
      this.totalSize = totalSize;
  }
  public String getName() {
      return name;
  }
  public void setName(String name) {
      this.name = name;
  }
  public int getProgress() {
      return progress;
  }
  public void setProgress(int progress) {
      this.progress = progress;
  }
  public int getUploadSize() {
      return uploadSize;
  }
  public void setUploadSize(int uploadSize) {
      this.uploadSize = uploadSize;
  }

}

在这里就感觉自己大功告成,但是这里还要注意的一个问题。请看上面的 run 方法,这里发送消息似乎是不对的。仔细看我们这里的message在不停的new ,这个地方似乎不是很合理吧!如果message被for包住然后循环一千次甚至一万次那这里的message也就会被 new 一千次甚至一万次,所以这里我们会用到一个Message的静态方法obtain()。因此Message 千万不要new出来。

Message类静态obtain()方法

Message类静态obtain()方法是从消息池中获取已有的或空闲的消息对象,如果消息中没有消息对象,或没有空闲的对象,则创建新的对象。

obtain()方法被重载过多次

  1. Message obtain()
  2. Message obtain(Message msg)  //用原有的去创建一个新的message
  3. Message obtain(Handler handler) //在获取的时候就指定是向谁去发消息是谁处理消息
  4. Message obtain(Handler handler, int what)
  5. Message obtain(Handler handler, Runnable callback)
  6. Message obtain(Handler handler, int what, Object obj) 
  7. Message obtain(Handler handler, int what, int arg1, int arg2)
  8. Message obtain(Handler handler, int what, int arg1, int arg2, Object obj)
     
@Override
public void run() {
    ***
    ***
    isUploadSuccess = ***;

    //替换为
    Message msg = Message.obtain(handler);
    //根据自己的情况发送创建消息并发送
    //这里封装message的时候需要注意

    情况1.如果我们只需要一个或者两个int类型的数据直接可以
    //msg.arg1 = 1;
    //msg.arg2 = 2;

    情况2.如果我们需要使用String等类型的时候
    //String str = "***"
    //msg.obj = str ;

    情况3.当以上两种都不足以满足的情况需要携带的数据有很多的时候我们就可以通过对象的方式
    //bean.setName(***);
    //bean.setProgress(***);
    //bean.setTotalSize(***);
    //bean.setUploadSize(***)
    //msg.obj = bean;

    if(isUploadSuccess){
      msg.what = UPLOAD_SUCCESS;
    } else {
      msg.what = UPLOADING ;
    }
    handler.sendMessage(msg)
}

注意如果我们使用了obtain()方法指定了Handler,那么在发消息的时候,就应该使用消息对象的sendToTarget()方法。即handler.sendMessage(msg) 替换为 msg.sendToTarget() (这里就直接是msg找自己的handler)。这里这样写的好处是防止项目里面有多个handler的时候会出现

        Message msg = Message.obtain(handler);

        handler2.sendMessage(msg)

所以最终的方式就是

@Override
public void run() {
    ***
    ***
    isUploadSuccess = ***;
    Message msg = Message.obtain(handler);
    //根据自己的情况发送创建消息并发送
    //这里封装message的时候需要注意

    情况1.如果我们只需要一个或者两个int类型的数据直接可以
    //msg.arg1 = 1;
    //msg.arg2 = 2;

    情况2.如果我们需要使用String等类型的时候
    //String str = "***"
    //msg.obj = str ;

    情况3.当以上两种都不足以满足的情况需要携带的数据有很多的时候我们就可以通过对象的方式
    //bean.setName(***);
    //bean.setProgress(***);
    //bean.setTotalSize(***);
    //bean.setUploadSize(***)
    //msg.obj = bean;

    if(isUploadSuccess){
      msg.what = UPLOAD_SUCCESS;
    } else {
      msg.what = UPLOADING ;
    }
    msg.sendToTarget();
}

还需要注意的问题就是其实Handler对象通过obtainMessage()方法也可以获得Message对象。你自己可以去查看一下源码,通过源码可以看出Handler的obtainMessage()方法的本质就是Message类的静态obtain()方法实现的。因此我个人推荐就使用Message的obtain()方法好一些。

以上就是我个人对Handler和Message的理解,所以写了这样一遍文章和同行们分享一下。这里我所讲到的是handler的send系列方法。如若有机会,可以和大家一起讨论post等系列方法。此篇文章里面有会有出处,如果有不对的地方希望大家能指出后面我将进行不断的完善。谢谢大家支持

猜你喜欢

转载自blog.csdn.net/m18180201066/article/details/89328810