(一)项目介绍
Launcher上播放小视屏和独立的视频应用。小视屏是视频应用的裁剪版,只有播放的功能,两者使用相同的底层系统。
系统的初始化会占用比较长的时间,从Launcher上跳转进入app,如果先完全退出结束进程,再重新初始化底层系统,
这样就会耗费不少时间,用户体验会差很多。所以,为了改善这个情况,把底层的部分做成service形式,在Launcher上跳转到其他应用,只是停止了播放,而不结束底层系统。
(二)AIDL
ServiceAidl.aidl 文件定义提供客户端调用的接口
package com.demo.service;
import com.demo.service.Freq;
import com.demo.service.IServiceCallback;
interface ServiceAidl {
boolean isInited();
void registerCallback(IServiceCallback cb);
void unregisterCallback(IServiceCallback cb);
void playerInit();
int setVideoScale(int full, int x, int y, int width, int height);
void play();
/*自定义类*/
Freq getFreq();
}
|
aidl里面使用自定义类Freq,需要定义Freq.aidl,然后Freq类需要实现接口Parcelable。Freq(Parcel pl)里面的read的顺序,要和writeToParcel里面write的顺序一直。
public Freq(Parcel pl)
{
mFreq = pl.readInt();
mSymbol = pl.readInt();
mQam = pl.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeInt(this.mFreq);
dest.writeInt(this.mSymbol);
dest.writeInt(this.mQam);
}
//添加一个静态成员,名为CREATOR,该对象实现了Parcelable.Creator接口
public static final Parcelable.Creator<DvbFreq> CREATOR = new Parcelable.Creator<DvbFreq>(){
@Override
public DvbFreq createFromParcel(Parcel arg0) {
// TODO Auto-generated method stub
return new Freq(arg0);
}
@Override
public DvbFreq[] newArray(int arg0) {
// TODO Auto-generated method stub
return new Freq[arg0];
}
};
|
(三)Service
1、manifest 里面定义
<service
android:name="com.demo.service"
android:enabled="true"
android:exported="true" >
<intent-filter >
<action android:name="com.demo.service.START"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
|
2、定义Stub
private final ServiceAidl.Stub
mBinder = new ServiceAidl.Stub() {
@Override
public int setVideoScale(int full, int x, int y, int width, int height)
throws RemoteException {
// TODO Auto-generated method stub
return mService.setVideoScale(full, x, y, width, height);
}
@Override
public boolean isInited() throws RemoteException {
// TODO Auto-generated method stub
return mService.isInited();
}
@Override
public void registerCallback(IServiceCallback cb) throws RemoteException {
// TODO Auto-generated method stub
if (cb != null) mCallbacks.register(cb);
}
@Override
public void unregisterCallback(IDvbServiceCallback cb) throws RemoteException {
// TODO Auto-generated method stub
if (cb != null) mCallbacks.unregister(cb);
}
@Override
public void playerInit() throws RemoteException {
// TODO Auto-generated method stub
mService.playerInit();
}
@Override
public void play() throws RemoteException {
// TODO Auto-generated method stub
mService.play();
}
}
|
3、重载onBind(Intent intent),客户端bindservice的时候,返回上面定义的mBinder
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
}
|
(四) Client
1. 导入Service的jar包,或者拷贝service的aidl文件(ServiceAidl.aidl)和aidl里所用到的自定义类文件(Freq.java 和Freq.aidl)
2. 定义Service的aidl接口和connection,
我们调用
startDemoService ()之后,service connected,返回的Binder桩接口取得mServiceAidl接口,这样我们就可以调用Service的aidl文件里面定义的接口了。
注意:我们在startService()的时候,intent的action需要和service 的manifest里面定义的action一致。
private ServiceAidl mService; // service
private ServiceConnection mConnection;
// 连接service
private void initConnection() {
mConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
mService = DvbServiceAidl.Stub.asInterface(service);
try {
mInitFlag = mService.isInited();
if(mInitFlag) {
mService.playerInit();
mService.registerCallback(mCallback); // 注册消息回调
mService.setVideoScale(0, 272, 150, 930, 598); // 设置视频大小和位置
mService.play(); // 播放
}
else {
exitService(); // 初始化失败 退出
}
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
mService = null;
}
};
}
private void startDemoService() {
Intent intent = new Intent(
"com.demo.service.Service.START");
// remote service ,退出activity之后,也不停止service,所以这里bind之后再start
// 具体查看
bindService 、startService以及bindService和startService 同时调用的区别
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
startService (intent);
}
|
3. 因为Service会改变状态,需要去更新ui提示用户状态改变,目前我们已经使用了aidl的方式,所以没办法使用Messenger的方式来发送消息更新,这边使用了回调的方式去更新。
(1)、首先,来看下service端的实现,定义一个RemoteCallbackList和
IServiceCallback的aidl接口由客户端去实现
final static RemoteCallbackList<IServiceCallback> mCallbacks
= new RemoteCallbackList<IServiceCallback>();
|
package com.feiyue.dvbservice;
oneway interface IServiceCallback {
/*
* handler common message from service
*/
void handlerCommEvent(int msgID, int param);
/*
* handler search message from service
*/
void handlerSearchEvent(int msgID, in List<String> strList);
}
|
(2)、aidl 定义中有register和unregister两个接口供客户端注册和反注册回调。
void registerCallback(IServiceCallback cb);
void unregisterCallback(IServiceCallback cb);
@Override
public void registerCallback(IDvbServiceCallback cb) throws RemoteException {
// TODO Auto-generated method stub
if (cb != null) mCallbacks.register(cb);
}
@Override
public void unregisterCallback(IDvbServiceCallback cb) throws RemoteException {
// TODO Auto-generated method stub
if (cb != null) mCallbacks.unregister(cb);
}
|
(3)、service 状态改变,通知client更新ui,这边我们组了一个Message的形式转给客户端
final int N = mCallbacks.beginBroadcast();
try { switch(service.status) { case Msg.SHOW__MSG: case Msg.HIDE__MSG: Bundle b = new Bundle(); ArrayList<String> strList = new ArrayList<String>(); b = msg.getData(); strList.add(b.getString("MSGTIP")); // 提示框消息 mCallbacks.getBroadcastItem(i).handlerSearchEvent(msg.what, strList); break; default: break; } } catch (RemoteException e) { // The RemoteCallbackList will take care of removing // the dead object for us. } }
mCallbacks.finishBroadcast();
|
4. Client端实现
(1)、实现
IServiceCallback的接口, mHandler是客户端实现的消息处理。
private IServiceCallback mCallback = new IServiceCallback.Stub() {
@Override
public void handlerCommEvent(int msgID, int param) throws RemoteException {
// TODO Auto-generated method stub
Message msg = new Message();
msg.what = msgID;
msg.arg1 = param;
mHandler.sendMessage(msg);
}
@Override
public void handlerSearchEvent(int msgID, List<String> strList) throws RemoteException {
// TODO Auto-generated method stub
Message msg = new Message();
Bundle b = new Bundle();
msg.what = msgID;
switch (msgID) {
case Msg.SHOW_MSG:
case Msg.HIDE_MSG:
b.putString("MSGTIP",strList.get(0)); // ca提示框消息
break;
default:
break;
}
msg.setData(b);
mHandler.sendMessage(msg);
}
};
|
(2)、在service connected 的时候去注册这个回调,在退出activity反注册callback 和unbindservice
mService.unregisterCallback(mCallback);
if(mConnection != null) {
unbindService(mConnection);
mConnection = null;
}
|
到此,基本就已经完成了,service和client相互间的通信!
我的第一次写记录,勉之!
Demo: https://github.com/yqb178/AidlDemo