最近项目中碰到个问题,应用退出之后(通过调用System.exit(0)),弹出的Toast很久的不消失。
原因是项目中使用了环信的SDK,其中有个处理后台推送的Service环信专门为其加了个so文件,目的是为了防止Service被杀死。
同事将该Service设置为android:process = "remote" ,即使Service运行的进程与主进程分开,toast确实是消失了,但是后台就无法接受到推送了。
原因肯定是Service在其他进程了,而之前环信开发时又未做处理,所以已经无法与主进程通信了。
因为之前很少涉及到多进程的项目,所以就专门去研究了下AIDL的用法。
简单来说一下AIDL使用步骤:
1.编写一个AIDL文件。其实就是一个JAVA interface而已,只是文件名需要以.aidl结尾。
如果使用AndroidStudio进行开发,可直接建立AIDL文件,见下图:
建好之后,目录中会多一个名为aidl的文件夹,然后在.aidl文件中实现自己的方法,例如:
// IRemoteServiceStock.aidl
package k.javine.myremoteservice;
interface IRemoteServiceStock {
//自己实现的方法
int getPid();
int getCount();
}
2.建立一个Service类,并在其中实现aidl接口方法,以供客户端调用。
这个过程跟编写正常的Service差不多,只是多了一个Stub的实现,直接上代码吧。
public class MyRemoteService extends Service {
private static int bindCount = 0;
@Override
public void onCreate() {
super.onCreate();
Log.d("Javine", "Create Service pid = "+ Process.myPid());
}
//实现AIDL接口
private final IRemoteServiceStock.Stub mBinder = new IRemoteServiceStock.Stub(){
@Override
public int getPid() throws RemoteException {
return android.os.Process.myPid();
}
@Override
public int getCount() throws RemoteException {
return bindCount;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
bindCount++;
return mBinder;
}
@Override
public void onDestroy() {
Log.d("Javine", "Destroy Service");
super.onDestroy();
}
}
3.在主进程中调用远端进程的Service中的内容。
我们都知道,如果要调用Serivce中的函数,需要在Activity中实现ServiceConnection接口,并重写其中的方法。这里会将AIDL接口作为IBinder返回给Activity,这样就可以在Acitivy中调用RemoteService中的函数了。
代码如下:
class MyConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (progressDialog.isShowing())
progressDialog.dismiss();
remoteServiceStock = IRemoteServiceStock.Stub.asInterface(service);//获取远端进程Service返回的AIDL接口
String info = null;
try {
servicePid = remoteServiceStock.getPid();
info = "Process Id: "+remoteServiceStock.getPid()+"\nBind Count: "+remoteServiceStock.getCount();
} catch (RemoteException e) {
e.printStackTrace();
}
if (info!=null){
textInfo.setText(info);
unBindBtn.setEnabled(true);
killBtn.setEnabled(true);
}else{
textInfo.setText("获取失败");
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
remoteServiceStock = null;
}
}
好了,到此三步骤就走完了。
不过有几个地方需要重要注意一下:
1.在AndroidManifest.xml中声明Service,并且指定android:process = ":remote"属性。这里的remote前面加“:”代表应用内才能调用的远端进程,若没有":"代表其他应用也可调用。
2.如何启动远程service?
如果在同一个应用内,可通过Intent(this,xxxService.class)的方式,直接start或者onbind。
如果不在同一个应用内,则需通过给Service设置<action>属性,然后客户端通过intent.setAction()来隐式启动。
为此,我也编写了一个Demo,并且发现一个有趣的问题。
如果Activity已经绑定了remoteService,然后kill掉remoteService所在的远端进程,大概1s后系统会为remoteService新建一个进程,并且Service还是处于bind状态。
Demo截图
demo源码MyRemoteService