Android 命令过滤
在工作中遇到这样一个问题,当滑动 App 中的色盘时,被控制灯的颜色也要跟随变化。
最开始的做法是在 View 的 onTouch().ACTION_CHANGE 中直接调用发送命令给固件,然而由于 onTouch() 方法在单次滑动触发的 ACTION_CHANGE 事件过多,而固件那边的处理速度和资源有限,无法处理这么快的命令。这时就需要在 App 端做处理了。
处理思路:
要点:App 发送给设备的命令要是均匀的,还要保证最后的一个指令不能丢。
处理代码如下:
public abstract class AdvanceStrategy {
public final static byte[] DEFAULT_SAMPLE_LIST = new byte[]{(byte) 0x01};
private final static AdvanceStrategy DEFAULT = new DefaultAdvanceStrategy();
private static AdvanceStrategy definition;
protected Callback mCallback;
protected int sampleRate = 200;
protected byte[] sampleOpcodes;
public static AdvanceStrategy getDefault() {
synchronized (AdvanceStrategy.class) {
if (definition != null)
return definition;
}
return DEFAULT;
}
public static void setDefault(AdvanceStrategy strategy) {
synchronized (AdvanceStrategy.class) {
if (strategy != null)
definition = strategy;
}
}
static public boolean isExists(byte opcode, byte[] opcodeList) {
for (byte opc : opcodeList) {
if ((opc & 0xFF) == (opcode & 0xFF))
return true;
}
return false;
}
final public int getSampleRate() {
return sampleRate;
}
/**
* 设置采样率,单位毫秒
*
* @param sampleRate
*/
final public void setSampleRate(int sampleRate) {
this.sampleRate = sampleRate;
}
/**
* 回调接口,采样到的命令交由回调接口处理
*
* @param mCallback
*/
public void setCallback(Callback mCallback) {
this.mCallback = mCallback;
}
public byte[] getSampleOpcodes() {
if (sampleOpcodes == null)
return DEFAULT_SAMPLE_LIST;
return sampleOpcodes;
}
/**
* 设置采样的Opcode数组
*
* @param sampleOpcodes
*/
public void setSampleOpcodes(byte[] sampleOpcodes) {
this.sampleOpcodes = sampleOpcodes;
}
/**
* 处理传进来的命令
*
* @param opcode 命令吗
* @param address 目标地址
* @param params 命令参数
* @param delay 命令延时
* @param tag 命令标签
* @param noResponse 命令发送方式
* @param immediate 是否立即写入底层FIFO
* @return 命令是否成功写入
*/
abstract public boolean postCommand(byte opcode, int address, byte[] params, int delay, Object tag, boolean noResponse, boolean immediate);
/**
* 启动,执行初始化
*/
abstract public void onStart();
/**
* 停止,做一些清理工作
*/
abstract public void onStop();
public interface Callback {
boolean onCommandSampled(byte opcode, int address, byte[] params, Object tag, int delay, boolean noResponse);
}
/**
* 默认的命令发送策略
*/
private static class DefaultAdvanceStrategy extends AdvanceStrategy {
private static final int ACTION_SEND_COMMAND = 1;
public final static String TAG = "AdvanceStrategy";
private long lastSampleTime;
/**
* 记录是否是最后一条命令,避免之前发送过一条合法的命令,之后再发送一次被丢弃的命令
*/
private boolean isLastCommand = true;
public DefaultAdvanceStrategy() {
}
@Override
public void onStart() {
this.lastSampleTime = 0;
}
@Override
public void onStop() {
}
@Override
public boolean postCommand(byte opcode, int address, byte[] params, int delay, Object tag, boolean noResponse, boolean immediate) {
long currentTime = System.currentTimeMillis();
boolean flag = false;
if (lastSampleTime == 0) {
//第一个命令,直接写入FIFO
lastSampleTime = currentTime;
flag = true;
} else if (immediate || !isExists(opcode, this.getSampleOpcodes())) {
//立即发送的命令,不在采样列表中的命令直接发送
flag = true;
} else {
//计算和最后一次采样时间的间隔,获取采样的命令
long interval = currentTime - this.lastSampleTime;
if (interval >= this.getSampleRate()) {
lastSampleTime = currentTime;
flag = true;
isLastCommand = true;
}
}
if (flag && this.mCallback != null) {
Log.d(TAG, "Sample Opcode : " + Integer.toHexString(opcode));
//所有采样到的命令立即交给回调接口处理
return this.mCallback.onCommandSampled(opcode, address, params, tag, delay, noResponse);
} else {
Log.d(TAG, "Miss Opcode : " + Integer.toHexString(opcode));
isLastCommand = false;
if (mCallback != null){
CommandBean commandBean = new CommandBean(opcode, address, params, tag, delay, noResponse);
sendMessageDelay(commandBean, ACTION_SEND_COMMAND);
}
}
return false;
}
public void sendMessageDelay(Object object, int what) {
mHandler.removeMessages(what);
Message message = Message.obtain();
message.what = what;
message.obj = object;
mHandler.sendMessageDelayed(message, getSampleRate());
}
class CommandBean {
byte opcode;
int address;
byte[] params;
Object tag;
int delay;
boolean noResponse;
public CommandBean(){}
public CommandBean(byte opcode, int address, byte[] params, Object tag, int delay, boolean noResponse) {
this.opcode = opcode;
this.address = address;
this.params = params;
this.tag = tag;
this.delay = delay;
this.noResponse = noResponse;
}
}
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case ACTION_SEND_COMMAND:
CommandBean commandBean = (CommandBean) msg.obj;
if (!isLastCommand && mCallback != null){
Log.i(TAG, "handleMessage() onCommandSampled code = " + commandBean.opcode);
mCallback.onCommandSampled(commandBean.opcode, commandBean.address, commandBean.params, commandBean.tag, commandBean.delay, commandBean.noResponse);
}
break;
}
}
};
}
}