Android消息处理模型汇总

1. 概述

Android开发中,消息的处理是一项非常重要的事情,好的消息处理模型的建立对于系统稳定性和可维护性很重要,下面就日常开发中用到的消息处理模型做下汇总。

2.消息模型

2.1责任链消息处理模型

这种消息处理模型运用了责任链模式,使多个对象都有机会处理请求,从而避免了请求的发送者和接收者的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
责任链模式UML图如下所示
在这里插入图片描述
简单责任链模式UML图
在简单责任链模式下,消息可以理解为一个简单的字符串,将消息通过Client下发到责任链的一端,然后消息在这条链上流动起来,当某个Handler关心这条消息,那么它就去处理它,消息处理到此终止,当它不关心这条消息时,将消息传递到下一个Handler,直到被处理为止,当所有的Handler都不关心这条消息时,那么我们需要做默认处理或者直接不care。
在这里插入图片描述
复杂责任链模式UML图
复杂责任链模型设计中,与简单模型不同的是,消息变得复杂了,不再是简单的字符串消息,变成了AbstractRequest对象。虽然消息实体变的复杂了,但是处理流程还是一样的,将这个请求消息在链上流动起来,由关心它的Handler类去处理。

在gs 应用开发中,我们最常处理的两类消息,一类是dbus消息,一类是广播消息。当待处理的消息较多时,我们通常的做法是通过条件判定来界定消息类型,然后针对不同消息类型做处理,当过多的消息放到同一代码块中处理时,不免造成代码块过长,业务复杂,难以维护,这个时候责任链的设计就派上用场了,通过AbstractHandler类将业务拆分,将不同类型的消息放到不同类型的AbstractHandler类中处理。下面以dbus消息处理为例说明详细的代码设计流程:

  1. AbsHandler类及其子类
public abstract class AbsDbusHandler {
    protected final Context mContext;
    protected Handler mHandler = new Handler();
    protected AbsDbusHandler nextHandler;
    public AbsDbusHandler(Context context) {
        mContext = context;
    }
    public abstract boolean handle(String sigName, int argLength, List<DbusObj> argList);
    protected String getTag() {
        return getClass().getSimpleName();
    }
}

MetaSwitch Signal的处理

public class MetaSwitchSignalsHandler extends AbsDbusHandler {
    public MetaSwitchSignalsHandler(Context context) {
        super(context);
    }
    @Override
    public boolean handle(String sigName, int argLength, List<DbusObj> argList) {
        if (sigName.equals(DbusSignal.SIGNAL_METASWITCH_REQ_LOGIN)||
        sigName.equals(DbusSignal.SIGNAL_METASWITCH_LOGIN_ERROR)) {
            //do some thing
            return true;
        } else {
            if (nextHandler != null) {
                return nextHandler.handle(sigName, argLength, argList);
            } else {
                return false;
            }
        }
    }
}

Audio Signal消息的处理

public class AudioSignalsHandler extends AbsDbusHandler {
    public AudioSignalsHandler(Context context) {
        super(context);
    }
    @Override
    public boolean handle(String sigName, int argLength, List<DbusObj> argList) {
        if (sigName.equals(DbusSignal.SIGNAL_AUDIO_DEV)) {
            //do someting
            return true;
        } else {
            if (nextHandler != null) {
                return nextHandler.handle(sigName, argLength, argList);
            } else {
                return false;
            }
        }
    }
  1. HandlerChainFactory组装责任链
    通过简单工厂类将各个Handler串起来,组成一条链,特别注意不要将第一个Handler和最后一个Handler关联,否则会形成环状链,如果消息没有可以处理的Handler类,那么消息将一直在链上传递,所以最好有一个默认消息处理的Handler放到链的末端。
public class HandlerChainFactory {
    private static AbsDbusHandler dbusHandler;
    public static AbsDbusHandler productDbusChain(Context context) {
        if(dbusHandler == null){
            AbsDbusHandler metaSwitchSignalsHandler = new MetaSwitchSignalsHandler(context);
            AbsDbusHandler audioSignalsHandler = new AudioSignalsHandler(context);
            AbsDbusHandler upgradeSignalsHandler = new UpgradeSignalsHandler(context);
            metaSwitchSignalsHandler.nextHandler = audioSignalsHandler;
            audioSignalsHandler.nextHandler = upgradeSignalsHandler;
            dbusHandler = metaSwitchSignalsHandler;
        }
        return dbusHandler;
    }
 }
  1. 责任链的调用
protected AbsDbusHandler getDbusHandler(){
        return HandlerChainFactory.productDbusChain(mContext);
    }
private DbusCallback dbusCallback = new DbusCallback() {
        @Override
        public void onCallback(int sigId, String sigName, int argNum, List<DbusObj> argList) {
            if (!getDbusHandler().handle(sigName, argNum, argList)) {
            // do something, 未处理的dbus消息在这里处理
            }

至此,一个简单的责任链设计就完成,在开发过程中,大家可以参考实现类似场景的消息模型设计。
在Android框架中还有许多类似的责任链模式设计的例子,比如,在Android事件分发处理的流程中就使用到责任链的设计,
通过dispatchTouchEvent, onTouchEvent, onInterruptTouchEvent三个覆写方法完成事件的分发处理。再不如,OkHttp3框架的interceptor的设计也使用了责任链模式设计。

OkHttp3架构设计图
在这里插入图片描述
Android事件分发处理流程图
在这里插入图片描述

2.2 对象池消息处理模型

池的概念大家并不陌生,对象池,连接池,线程池等等,在程序设计中引入池的目的是实现对象的复用,用的时候从池中获取,用完再放回池子,对于消息的处理可以称为消息池,Android中消息处理采用的Handler+Looper+Message+MessageQueue的方式,具体的工作方式这里不在赘述了,我们重点关注它的内部的消息模型的设计,读过源码应该比较清楚,它内部消息管理采用的
是消息池+链表的方式,消息池的目的是实现消息的复用,链表引入是为了保证消息的顺序。

消息池使用的场景如下:
1.需要频繁大量创建对象
2.对象没有特定身份
3.需要缓冲池的场景
在设计模式中,与对象池概念相通的设计模式叫做享元模式,存放对象必然需要一个容器,这些容器可以是一个数组,链表,或者map, 在对象池的使用中,我们必然需要不停的从对象池中获取对象,用完后放回容器,那么多线程场景下需要考虑线程安全问题。

实际开发中,当我们需要使用到对象池时并不需要从头开发,只需要对Android Message机制做下简单封装就可达到目的。
例如,Android原生Telephony框架中,在处理rild AT命令时使用的就是这种对象池的设计,概括起来就是需要处理消息的发送和接收两种场景,发送又可以分为有回应发送以及无回应发送,具体的实现可以参考如下路径:
frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
内部类RILRequest类使用了对象池的设计,容器使用了链表的实现,代码如下:

class RILRequest {
    static final String LOG_TAG = "RilRequest";

    //***** Class Variables
    static Random sRandom = new Random();
    static AtomicInteger sNextSerial = new AtomicInteger(0);
    private static Object sPoolSync = new Object();
    private static RILRequest sPool = null;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 4;
    private Context mContext;

    //***** Instance Variables
    int mSerial;
    int mRequest;
    Message mResult;
    Parcel mParcel;
    RILRequest mNext;

    /**
     * Retrieves a new RILRequest instance from the pool.
     *
     * @param request RIL_REQUEST_*
     * @param result sent when operation completes
     * @return a RILRequest instance from the pool.
     */
    static RILRequest obtain(int request, Message result) {
        RILRequest rr = null;

        synchronized(sPoolSync) {
            if (sPool != null) {
                rr = sPool;
                sPool = rr.mNext;
                rr.mNext = null;
                sPoolSize--;
            }
        }

        if (rr == null) {
            rr = new RILRequest();
        }

        rr.mSerial = sNextSerial.getAndIncrement();

        rr.mRequest = request;
        rr.mResult = result;
        rr.mParcel = Parcel.obtain();

        if (result != null && result.getTarget() == null) {
            throw new NullPointerException("Message target must not be null");
        }

        // first elements in any RIL Parcel
        rr.mParcel.writeInt(request);
        rr.mParcel.writeInt(rr.mSerial);

        return rr;
    }

    /**
     * Returns a RILRequest instance to the pool.
     *
     * Note: This should only be called once per use.
     */
    void release() {
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                mNext = sPool;
                sPool = this;
                sPoolSize++;
                mResult = null;
            }
        }
    }

    private RILRequest() {
    }

    static void
    resetSerial() {
        // use a random so that on recovery we probably don't mix old requests
        // with new.
        sNextSerial.set(sRandom.nextInt());
    }

    String
    serialString() {
        //Cheesy way to do %04d
        StringBuilder sb = new StringBuilder(8);
        String sn;

        long adjustedSerial = (((long)mSerial) - Integer.MIN_VALUE)%10000;

        sn = Long.toString(adjustedSerial);

        //sb.append("J[");
        sb.append('[');
        for (int i = 0, s = sn.length() ; i < 4 - s; i++) {
            sb.append('0');
        }

        sb.append(sn);
        sb.append(']');
        return sb.toString();
    }

    void
    onError(int error, Object ret) {
        CommandException ex;

        ex = CommandException.fromRilErrno(error);

        if (RIL.RILJ_LOGD) Rlog.d(LOG_TAG, serialString() + "< "
            + RIL.requestToString(mRequest)
            + " error: " + ex + " ret=" + RIL.retToString(mRequest, ret));

        if (mResult != null) {
            AsyncResult.forMessage(mResult, ret, ex);
            mResult.sendToTarget();
        }

        if (mParcel != null) {
            mParcel.recycle();
            mParcel = null;
        }
    }
}

下面以获取IMS注册状态为例子说明发送消息流程

在这里插入图片描述
1请求对象获取

public void getImsRegistrationState(Message result) {
        //从对象池中获取一个请求对象,具体过程可以参考RILRequest的obtain函数
        RILRequest rr = RILRequest.obtain(RIL_REQUEST_IMS_REGISTRATION_STATE, result);
        send(rr);
    }

2.请求对象回收
该处理流程主要在RILSender.handleMessage中处理

 //回调异常结果
 rr.onError(RADIO_NOT_AVAILABLE, null);
 //回收请求对象
 rr.release();
 decrementWakeLock();

实际在Android源码中,google已经给我们提供了pool模型,具体源码在

android/frameworks/support/v4/java/android/support/v4/util/Pools.java

内部给我提供了SimplePool和SynchronizedPool两类对象池。

2.3 发布订阅式消息处理模型

发布订阅式的消息处理模型可以为组件以及模块间的通信提供解耦,使用观察者模式设计,包括注册事件监听,发送事件,取消事件监听等几个步骤, 你可以选择使用EventBus,但是EventBus选择使用Class作为事件的Tag,所以每监听一个类型
事件就需要为该事件创建一个新的类,我们也可以自己封装个事件分发框架,通过定义事件topic来区分不同的Event, 通过
对象池设计来复用事件对象。

  1. 注册事件监听
public static void registerEventListener(I_CEventListener listener, String[] topics) {
        if (null == listener || null == topics) {
            return;
        }

        synchronized (LISTENER_LOCK) {
            for (String topic : topics) {
                if (TextUtils.isEmpty(topic)) {
                    continue;
                }

                Object obj = LISTENER_MAP.get(topic);
                if (null == obj) {
                    // 还没有监听器,直接放到Map集合
                    LISTENER_MAP.put(topic, listener);
                } else if (obj instanceof I_CEventListener) {
                    // 有一个监听器
                    I_CEventListener oldListener = (I_CEventListener) obj;
                    if (listener == oldListener) {
                        // 去重
                        continue;
                    }
                    LinkedList<I_CEventListener> list = new LinkedList<I_CEventListener>();
                    list.add(oldListener);
                    list.add(listener);
                    LISTENER_MAP.put(topic, list);
                } else if (obj instanceof List) {
                    // 有多个监听器
                    LinkedList<I_CEventListener> listeners = (LinkedList<I_CEventListener>) obj;
                    if (listeners.indexOf(listener) >= 0) {
                        // 去重
                        continue;
                    }
                    listeners.add(listener);
                }
            }
        }
    }

监听器数据类型如下

/**
* 监听器列表,支持一对多存储, Object对象可以是List类型
*/
private static final HashMap<String, Object> LISTENER_MAP = new HashMap<String, Object>();

订阅关心的Topic,并在相应Listener中处理

//订阅的topic列表
private final String[] subscribeTopic = new String[]{Constants.EventTopic.SIP_DETECT_RESULT,
            Constants.EventTopic.AV_DETECT_RESULT, Constants.EventTopic.SIP_TRANS_PROTO,
            Constants.EventTopic.DETECT_STATE_CHANGED, Constants.EventTopic.AV_DETECT_GRADE};
//注册topic主题
CEventCenter.registerEventListener(this, subscribeTopic);

//根据topic类型处理不同的事件
@Override
    public void onCEvent(final String topic, int msgCode, int resultCode, Object obj) {
        Logger.d(TAG,"onCEvent, topic = "+topic+", object = "+obj.toString());
        //TODO something, 根据topic类型执行不同的订阅操作
   }
  1. 事件分发
   /**
     * 同步分发事件
     * @param topic      主题
     * @param msgCode    消息类型
     * @param resultCode 预留参数
     * @param obj        回调返回数据
     */
    public static void dispatchEvent(String topic, int msgCode, int resultCode, Object obj) {
        if (!TextUtils.isEmpty(topic)) {
           //从事件对象池中获取
            CEvent event = POOL.get();
            event.setTopic(topic);
            event.setMsgCode(msgCode);
            event.setResultCode(resultCode);
            event.setObj(obj);
            dispatchEvent(event);
        }
    }
    
    public static void dispatchEvent(CEvent event) {
        // 没有监听器,直接跳出代码,无需执行以下代码
        if (LISTENER_MAP.size() == 0) {
            return;
        }

        if (null != event && !TextUtils.isEmpty(event.getTopic())) {
            String topic = event.getTopic();
            // 通知事件监听器处理事件
            I_CEventListener listener = null;
            LinkedList<I_CEventListener> listeners = null;
            synchronized (LISTENER_LOCK) {
                Log.d(TAG, "dispatchEvent | topic = " + topic + "\tmsgCode = " + event.getMsgCode()
                        + "\tresultCode = " + event.getResultCode() + "\tobj = " + event.getObj());
                Object obj = LISTENER_MAP.get(topic);
                if (obj == null) {
                    return;
                }
                if (obj instanceof I_CEventListener) {
                    listener = (I_CEventListener) obj;
                } else if (obj instanceof LinkedList) {
                    listeners = (LinkedList<I_CEventListener>) ((LinkedList) obj).clone();
                }
            }
            // 分发事件
            if (null != listener) {
                listener.onCEvent(topic, event.getMsgCode(), event.getResultCode(), event.getObj());
            } else if (null != listeners && listeners.size() > 0) {
                for (I_CEventListener l : listeners) {
                    l.onCEvent(topic, event.getMsgCode(), event.getResultCode(), event.getObj());
                }
            }
            // 把对象放回池里面
            POOL.returnObj(event);
        }
    }

根据EventTopic类型分发事件

int grade = 0;
grade = ((ProbeTaskResult) event).getGrade();
dispatchEvent(Constants.EventTopic.AV_DETECT_GRADE, grade);

SipResult sipResult = ((ProbeTaskResult) event).getSipResult();
if (sipResult != null) {
    dispatchEvent(Constants.EventTopic.SIP_DETECT_RESULT, sipResult);
}

List<TasksResultItem> avTaskResults = ((ProbeTaskResult) event).getTasksResult();
if (avTaskResults != null) {
    dispatchEvent(Constants.EventTopic.AV_DETECT_RESULT, avTaskResults);
}

public void dispatchEvent(final String topic, final Object event) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                CEventCenter.dispatchEvent(topic, 0, 0, event);

            }
        });
    }
  1. 注销事件监听
/**
     * 注销监听器
     *
     * @param listener 监听器
     * @param topics   多个主题
     */
    public static void unregisterEventListener(I_CEventListener listener, String[] topics) {
        if (null == listener || null == topics) {
            return;
        }
        synchronized (LISTENER_LOCK) {
            for (String topic : topics) {
                if (TextUtils.isEmpty(topic)) {
                    continue;
                }
                Object obj = LISTENER_MAP.get(topic);
                if (null == obj) {
                    continue;
                } else if (obj instanceof I_CEventListener) {
                    // 有一个监听器
                    if (obj == listener) {
                        LISTENER_MAP.remove(topic);
                    }
                } else if (obj instanceof List) {
                    // 有多个监听器
                    LinkedList<I_CEventListener> listeners = (LinkedList<I_CEventListener>) obj;
                    listeners.remove(listener);
                }
            }
        }
    }

通常在OnDestory()方法中取消事件监听

@Override
    public void onDestroyView() {
        super.onDestroyView();
        //取消事件监听
        CEventCenter.unregisterEventListener(this, subscribeTopic);
        
    }

具体代码可以参考H60目录下
vendor/grandstream/apps/SoftProbe/app/src/main/java/com/grandstream/cmcc/softprobe/eventcenter

2.4 总结

实际开发中还有许多其他的消息处理模型可以选择,希望大家在认真思考实际场景,根据场景选择适当的消息模型进行处理,这对于系统的稳定性至关重要。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Calvin880828

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值