在TV实际开发中需要使用到监听,来实时更新UI,下面以ATV搜台为例子,搜台时,底层C++发送消息,app中来更新UI。下面具体来分析。
Activity中代码如下:
oncreate中:
private OnAtvPlayerEventListener mAtvPlayerEventListener = new AtvPlayerEventListener();
TvChannelManager.getInstance().registerOnAtvPlayerEventListener(mAtvPlayerEventListener);//进行注册
onpause中:
TvChannelManager.getInstance().unregisterOnAtvPlayerEventListener(mAtvPlayerEventListener);//进行解注册
mAtvPlayerEventListener = null;
private class AtvPlayerEventListener implements OnAtvPlayerEventListener {
@Override
public boolean onAtvAutoTuningScanInfo(int what, AtvEventScan extra) {
Message msg = mAtvUiEventHandler.obtainMessage(what, extra);
mAtvUiEventHandler.sendMessage(msg); //使用handle去更新UI 具体代码不展示了
return true;
} }
OnAtvPlayerEventListener 代表监听器,实现监听中的方法:onAtvAutoTuningScanInfo
activity中的代码显示需要onAtvAutoTuningScanInfo方法被调用,从而达到刷新UI的目的。
那我们需要在TvChannelManager这个类中进行方法的调用,代码如下:
private ArrayList<OnAtvPlayerEventListener> mAtvListeners = new ArrayList<OnAtvPlayerEventListener>();
@Deprecated
public boolean registerOnAtvPlayerEventListener(OnAtvPlayerEventListener listener) {
Log.d(TAG, "registerOnAtvPlayerEventListener ");
synchronized (mAtvListeners) {
mAtvListeners.add(listener);//注册
}
return true;
} @Deprecated
public synchronized boolean unregisterOnAtvPlayerEventListener(OnAtvPlayerEventListener listener) {
synchronized (mAtvListeners) {
mAtvListeners.remove(listener);//解注册
Log.d(TAG, "unregisterOnAtvPlayerEventListener size: " + mAtvListeners.size());
}
return true;
}
代码比较简单,创建一个接口的ArrayList,进行注册与解注册,作用是:任意一个activity中进行注册了此监听,就都能够接收到该监听。
下面来看监听方法被调用的代码:
private class EventHandler extends Handler {
EventHandler(Looper looper) {
super(looper);
} @Override
public void handleMessage(Message msg) { switch (msg.what) {
case TVPLAYER_ATV_AUTO_TUNING_SCAN_INFO: {
synchronized (mAtvListeners) {
for (OnAtvPlayerEventListener l : mAtvListeners) {
Bundle data = msg.peekData();
data.setClassLoader(AtvEventScan.class.getClassLoader());
Object msgObj = data.getParcelable("messageObj");
l.onAtvAutoTuningScanInfo(msg.what, (AtvEventScan)msgObj);
}
}
}
handle中,只要是ArrayList中的OnAtvPlayerEventListener,都会调用方法onAtvAutoTuningScanInfo
那么继续看这个handle在哪里被调用:
private EventHandler mHandler = null;
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mHandler = new EventHandler(looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mHandler = new EventHandler(looper);
} else {
mHandler = null;
} @Override
public boolean onEvent(Message msg) throws RemoteException {
if (mHandler != null) {
Message msgTmp = mHandler.obtainMessage();
msgTmp.copyFrom(msg);
mHandler.sendMessage(msgTmp);
}
return true;
}
在TvChannelManager的构造函数中,进行handle的实例化。如果是主线程中,那么就会有Looper,若是在子线程中则需要获取Looper。
在TvChannelManager中重写的onEvent方法调用handler去发消息
那么看一下该类继承会实现了什么:下面可以看见继承了IEventClient.aidl。
public class TvChannelManager extends IEventClient.Stub
aidl中如下:
interface IEventClient {
boolean onEvent(in Message msg);
}
那么是不是到此处就完了呢? 还早呢,上面代码只是继承了aidl,并实现了aidl中的方法,那么哪里调用这个onEvent呢?
既然TvChannelManager是继承IEventClient.aidl,那么属于一个binder,而binder大多数时候可以作为方法的参数,猜想一定有方法的使用了TvChannelManager的实例进行参数传递。
TvChannelManager的构造方法中:
IBinder b = ServiceManager.getService("tv");
ITvChannel mService = ITvService.Stub.asInterface(b).getTvChannel();
mService.addClient(this);
三行简单代码,主要的含义就是IPC跨进程通信,具体不深入了,主要代码就是获取到一个ITvChannel binder(如下),然后调用addClient方法。
public class TvChannelService extends ITvChannel.Stub{
@Override
public void addClient(IBinder client) throws RemoteException {
DeskTvCommonEventListener.getInstance().setClient(client);
}}
addClient的参数为IBinder,而恰巧TvChannelManager是继承于aidl,自然也是一个binder。
DeskTvCommonEventListener中的代码如下:
public class DeskTvCommonEventListener implements OnEventListener {
private static DeskTvCommonEventListener mListener = null;
private static final String TAG = "DeskTvCommonEventListener";
private RemoteCallbackList<IEventClient> mCallbackList = null;
private HashMap<IBinder, IEventClient> mMap = null;
private DeskTvCommonEventListener() {
mCallbackList = new RemoteCallbackList<IEventClient>() { @Override
public void onCallbackDied(IEventClient client) {
mMap.remove(client.asBinder());
}
};
mMap = new HashMap<IBinder, IEventClient>();
} public static DeskTvCommonEventListener getInstance() {
if (mListener == null) {
mListener = new DeskTvCommonEventListener();
} return mListener;
} public void setClient(IBinder client) {
if (client != null) {
IEventClient TvCommonEventClient = IEventClient.Stub.asInterface(client);
mCallbackList.register(TvCommonEventClient);
mMap.put(client, TvCommonEventClient);
}
} public void releaseClient(IBinder client) {
if (client != null) {
IEventClient tvCommonEventClient = mMap.get(client);
if (null != tvCommonEventClient) {
mCallbackList.unregister(tvCommonEventClient);
}
mMap.remove(client);
}
} @Override
synchronized public boolean onEvent(Message msg) {
if (mCallbackList == null) {
return false;
} int count = mCallbackList.beginBroadcast();
while (count > 0) {
count--;
try {
mCallbackList.getBroadcastItem(count).onEvent(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
mCallbackList.finishBroadcast();
return true;
}
}
DeskTvCommonEventListener中的主要的是RemoteCallbackList这个类,这个可以自行百度,或者查看android开发艺术与探索书籍,主要的就是可以删除远程接口的意思。
mCallbackList.getBroadcastItem(count).onEvent(msg);调用onEvent。
那么DeskTvCommonEventListener哪里调用呢?DeskTvCommonEventListener是实现于接口OnEventListener,加上该类也单例模式设计,那么也会有 把DeskTvCommonEventListener作为方法的参数进行传递。
private DeskTvCommonEventListener mTvCommonEventListener = null;
mTvCommonEventListener = DeskTvCommonEventListener.getInstance();
TvManager.getInstance().getPlayerManager().setOnEventListener(mTvCommonEventListener);
果不其然,那么我们就找到setOnEventListener该方法。
private OnEventListener mOnEventListener;
public void setOnEventListener(OnEventListener listener) {
mOnEventListener = listener;
} private class EventHandler extends Handler {
private PlayerImpl mMSrv; public EventHandler(PlayerImpl srv, Looper looper) {
super(looper);
mMSrv = srv;
} @Override
public void handleMessage(Message msg) {
TvOsType.EnumInputSource inputSource = null; if (mMSrv.mNativeContext == 0) {
return;
} if (mOnEventListener != null) {
final String MESSAGE_OBJ = "messageObj";
Bundle bundle = new Bundle();
bundle.putParcelable(MESSAGE_OBJ, (Parcelable) msg.obj);
switch (msg.what) {
case TVPLAYER_DTV_AUTO_TUNING_SCAN_INFO:
msg.setData(bundle);
break;
case TVPLAYER_ATV_MANUAL_TUNING_SCAN_INFO:
msg.setData(bundle);
break;
case TVPLAYER_ATV_AUTO_TUNING_SCAN_INFO:
msg.setData(bundle);
break;
case TVPLAYER_HBBTV_UI_EVENT:
msg.setData(bundle);
break;
}
msg.obj = null;
mOnEventListener.onEvent(msg);//总算找到调用的地方了!
msg.obj = bundle.getParcelable(MESSAGE_OBJ);
}}
代码就是一个handle中调用了DeskTvCommonEventListener的onEvent方法。
private static void postEventFromNative(Object srv_ref, int what, int arg1, int arg2, Object obj) {
PlayerImpl srv = (PlayerImpl)((WeakReference) srv_ref).get();
if (srv == null) {
return;
} if (srv.mEventHandler != null) {
Message m = srv.mEventHandler.obtainMessage(what, arg1, arg2, obj);
srv.mEventHandler.sendMessage(m);
}
return;
}
postEventFromNative该方法为静态方法,该类中有很多native方法,所以很显然postEventFromNative会被JNI那边调用。
继续查看JNI代码:
void com_mstar_android_tvapi_impl_PlayerImpl_native_init
(JNIEnv *env) {
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");}
void JNIMSrvListener::notifyCB(int32_t event, int32_t ext1, int32_t ext2, const Parcel* pacelObj) {
ALOGE("notifyCB event = %d\n", event);
jint msg;
jobject jobj = NULL;
JNIEnv *env = NULL;
if(mJvm == NULL || mJvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
ALOGE("Error! %s(%d) AttachCurrentThread \n", __FUNCTION__, __LINE__);
} switch (event) {//只列出一个event,作为示例
case EV_INPUT_SOURCE_LOCK:
msg = fields.TVPLAYER_INPUT_SOURCE_LOCK;
break;
}
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, jobj);
}
notifyCB中调用了静态方法postEventFromNative。
void JNIMSrvListener::PostEvent_PvrRecordTime(int32_t ext1, int32_t ext2) {//举个例子
ALOGD("PostEvent_PvrRecordTime");
notifyCB(EV_PVR_NOTIFY_RECORD_TIME, ext1, ext2, NULL);
}
PostEvent是主要疑惑点,由于作者也没有找到PostEvent的相关代码,也不好分析,究竟是怎样从C++到JNI中来的!这里面也会涉及到HIDL、binder比较难!也请高手指教下!
C++中都是直接这么使用PostEvent,暂时也没有找到相关PostEvent的源码(如下)。所以如果现在系统代码中有PostEvent找个函数的话,我们就可以参照此流程进行添加自定义的监听事件!
GetMSrvPlayer(eMapiSrcType)->PostEvent(NULL, EV_INPUT_SOURCE_LOCK, E_INPUT_SOURCE_LOCK_EVENT_ON, E_INPUT_SOURCE_LOCK_EVENT_UNDEFINED);