1. 铺垫
正常情况下,一个apk启动后只会运行在一个进程中,其进程名为apk的包名,所有的组件都会在这个进程中运行。
若要将某些组件(如Service,Activity等)运行在单独的进程中,就要在清单文件中对该组件设置android:process属性。
2. 代码
1) 清单文件:Service 和 Activity在同一个apk,但所处不同进程中
<service android:name=".MyService" android:process=":remote"/>
2) 实现了Parcelable接口的自定义类Student
public class Student implements Parcelable {
private static final String TAG = "Student lyl123";
private int id;
public Student(int id) {
this.id = id;
}
public static final Creator<Student> CREATOR = new Creator<Student>() {
@Override
public Student createFromParcel(Parcel in) {
int arg0 = in.readInt();
Log.e(TAG, "解包 - createFromParcel: arg0 = " + arg0);
//log中调用一次in.readInt(),值为id的值,参数中再调用一次in.readInt(),值就不是id的值了!
return new Student(arg0);
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
Log.e(TAG, "打包 - writeToParcel : id = " + id);
parcel.writeInt(id);
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
3) Student类对应的Student.aidl文件
package com.lyl.dialservice;
parcelable Student;
4) IStudentInterface.aidl文件
package com.lyl.dialservice;
import com.lyl.dialservice.Student;
interface IStudentInterface {
Student getStudentById(int id);
}
5) 对应的服务MyService(服务端)
public class MyService extends Service {
private static final String TAG = "MyService lyl123";
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private IBinder mBinder = new IStudentInterface.Stub(){
@Override
public Student getStudentById(int id) throws RemoteException {
Log.e(TAG, "getStudentById: " + id);
return new Student(id);//此处可能要操作数据库,查询数据等 -- 此处的数据要返回给客户端
}
};
}
6) Activity(客户端)
public class MainActivity extends Activity {
private static final String TAG = "MainActivity lyl123";
IStudentInterface iStudentInterface = null;
private ServiceConnection conn = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindMyService();
}
private void bindMyService() {
if (null == conn) {
Log.e(TAG, "bind service!");
conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iStudentInterface = IStudentInterface.Stub.asInterface(service);
Log.e(TAG, "iStudentInterface = " + iStudentInterface);
try {
Log.e(TAG, "student = " + iStudentInterface.getStudentById(2));
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
//intent要显性声明,负责会报错Service Intent must be explicit: Intent {}
Intent service = new Intent(this, MyService.class);
bindService(service, conn, BIND_AUTO_CREATE);
} else {
Log.e(TAG, "have bind service!");
}
}
@Override
protected void onDestroy() {
unbindMyService();
super.onDestroy();
}
private void unbindMyService() {
if (null != conn){
Log.e(TAG, "unbind service!");
unbindService(conn);
conn = null;
} else {
Log.e(TAG, "have unbind service!");
}
}
}
7) 各个文件的关系
8) 运行结果:
08-17 16:53:55.514 18381 18381 E MainActivity lyl123: bind service!
08-17 16:53:56.156 18381 18381 E MainActivity lyl123: iStudentInterface = com.lyl.dialservice.IStudentInterface$Stub$Proxy@1cf22e7
08-17 16:53:56.158 18432 18445 E MyService lyl123: getStudentById: 2
08-17 16:53:56.161 18432 18445 E Student lyl123: 打包 - writeToParcel : id = 2
08-17 16:53:56.166 18381 18381 E Student lyl123: 解包 - createFromParcel: arg0 = 2
08-17 16:53:56.166 18381 18381 E MainActivity lyl123: student = Student{id=2}
9) IStudentInterface.aidl文件生成的java文件
public interface IStudentInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.lyl.dialservice.IStudentInterface
{
private static final java.lang.String DESCRIPTOR = "com.lyl.dialservice.IStudentInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lyl.dialservice.IStudentInterface interface,
* generating a proxy if needed.
*/
public static com.lyl.dialservice.IStudentInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.lyl.dialservice.IStudentInterface))) {
return ((com.lyl.dialservice.IStudentInterface)iin);
}
return new com.lyl.dialservice.IStudentInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getStudentById:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
com.lyl.dialservice.Student _result = this.getStudentById(_arg0);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.lyl.dialservice.IStudentInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public com.lyl.dialservice.Student getStudentById(int id) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.lyl.dialservice.Student _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);
mRemote.transact(Stub.TRANSACTION_getStudentById, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.lyl.dialservice.Student.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getStudentById = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public com.lyl.dialservice.Student getStudentById(int id) throws android.os.RemoteException;
}
3. IStudentInterface.java文件分析
1) 它包含了两个静态内部类:
(a) 抽象类Stub,继承自Binder,其实就是一个Binder类,实现了IStudentInterface,但没有实现IStudentInterface的两个方法
(b) Stub的内部代理类Proxy,实现了IStudentInterface,且实现了IStudentInterface的两个方法。
2) IStudentInterface.Stub.asInterface(service);
客户端onServiceConnected()方法绑定的IBinder类型的service,是服务端返回的mBinder,二者是同一个对象。
/**
* Cast an IBinder object into an com.lyl.dialservice.IStudentInterface interface,
* generating a proxy if needed.
*/
public static com.lyl.dialservice.IStudentInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.lyl.dialservice.IStudentInterface))) {
return ((com.lyl.dialservice.IStudentInterface)iin);
}
return new com.lyl.dialservice.IStudentInterface.Stub.Proxy(obj);
}
通过以上代码可以看出,服务端客户端若在同一个进程中,则返回服务端的Stub对象本身。
若在不同进程,则是进程间通信,会调用return new com.lyl.dialservice.IStudentInterface.Stub.Proxy(obj);
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
即会把mBinder赋值给mRemote。
4. 生成文件简化后,可以看出是静态代理模式。
public interface ISubjectInterface extends android.os.IInterface{
public Subject getSubjectById(int id);//(1)
public static abstract class Stub extends android.os.Binder implements ISubjectInterface{
@Override public boolean onTransact(){
switch (code){
case TRANSACTION_getSubjectById:{//(3)
Subject _result = this.getSubjectById(_arg0);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
}
}
private static class Proxy implements ISubjectInterface{
@Override public Subject getSubjectById(int id) {//(2)
mRemote.transact(Stub.TRANSACTION_getStudentById, _data, _reply, 0);
_result = Subject.CREATOR.createFromParcel(_reply);
return _result;
}
}
}
}
客户端发起RPC调用,执行getSubjectById方法,会跳转到(1)处,
Proxy继承了ISubjectInterface,为ISubjectInterface的代理类,紧接着就会跳转到(2)处,
真正的被代理类就是Stub,真正执行就会在Stub类内,即在(3)处。
调用transact方法发起RPC(远程过程调用)请求后,当前线程会挂起;然后服务端的onTransact方法会被调用,直到RPC过程返回,当前线程继续执行。
@Override public com.lyl.dialservice.Student getStudentById(int id) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.lyl.dialservice.Student _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);
mRemote.transact(Stub.TRANSACTION_getStudentById, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.lyl.dialservice.Student.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getStudentById:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
com.lyl.dialservice.Student _result = this.getStudentById(_arg0);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
其中getStudentById方法、onTransact方法都运行在服务端的Binder线程池中。
在getStudentById方法中,首先声明两个Parcel对象,一个用于传递数据给服务端,一个用于接收服务端返回的数据。
其中Parcel类是一个容器,它主要用于存储序列化数据。可以把看成是一个流,依次往流里写数据,依次从流中读数据,并且写和读的顺序必须一致。
_data写入DESCRIPTOR和id后,作为参数传入transact方法,进入onTransact方法的case TRANSACTION_getStudentById中,
首先读处DESCRIPTOR和id,并把id赋给_arg0,同时调用服务端mBinder的getStudentById方法,构建student对象,同时把_arg0(id)的值赋给其成员变量。
com.lyl.dialservice.Student _result = this.getStudentById(_arg0);
private IBinder mbinder = new IStudentInterface.Stub(){//MyService.java
@Override
public Student getStudentById(int id) throws RemoteException {
Log.e("lyl", "getStudentById: " + id);
return new Student(id);//此处可能要操作数据库,查询数据等 -- 此处的数据要返回给客户端
}
};
public Student(int id) {//Student.java
this.id = id;
}
构建student对象不为空时,则向reply中写入1,之后执行writeToParcel方法,即student的writeToParcel方法。此时再次向reply中写值(id写入)。
@Override
public void writeToParcel(Parcel parcel, int i) {
Log.e("lyl", "service : 打包 - writeToParcel");
parcel.writeInt(id);
}
服务端的onTransact方法执行完,远程过程调用返回,_reply先从数据流中读取数值1,而后执行
_result = com.lyl.dialservice.Student.CREATOR.createFromParcel(_reply);
即调用Student的CREATOR.createFromParcel方法。
public static final Creator<Student> CREATOR = new Creator<Student>() {//Student.java
@Override
public Student createFromParcel(Parcel in) {
Log.e("lyl", "service : 解包 - createFromParcel");
return new Student(in.readInt());
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
_reply再次从数据流中读取id,构建Student对象,并把对象返回。
此时getStudentById方法获取对象成功!aidl流程分析也就分析完毕了。
5. 注意事项
1).客户端调用远程服务的方法getStudentById,且getStudentById方法运行在服务端的Binder线程池中,同时,客户端线程会被挂起,直到服务端方法执行完。若此时服务端方法执行耗时操作,会导致客户端线程长时间阻塞,当这个客户端线程是UI线程的话,会导致客户端ANR。当我们明确知道某个远程方法是耗时的,就要避免在客户端的UI线程中去访问它。
2).如1)所述,服务端的方法getStudentById是运行在服务端的Binder线程池中的,getStudentById本身可以执行耗时操作,此时切记不要在服务端方法中开启线程去执行异步任务,除非明确知道要干什么,负责不建议这样做。
3).客户端的onServiceConnected/onServiceDisconnected方法是运行在UI线程中的,不可以在它里面直接调用服务端的耗时方法。
4).service的生命周期方法:onCreate()/onBind()/onUnbind()/onDestroy()是运行在主线程中,onCreate()/onStrarCommand()/onDestroy()方法也运行在主线程中。
5).bindService时,要调用unbindService服务才会销毁,此时会调用服务的onDestroy方法;startService时,要调用stopService或自身的stopSelf方法服务才会销毁,此时也会调用服务的onDestroy方法。
6. 参考:
《Android开发艺术探索》