Android的IPC有四种,详见:http://blog.youkuaiyun.com/habbyge/article/details/17796345
AIDL学习文档:http://developer.android.com/guide/components/aidl.html#PassingObjects
重点:AIDL应该注意的事项:
1. AIDL文件的inteface中的成员函数的参数
(1)参数是基本数据类型,则默认是in方向的,可以没有限制的直接使用(包括String);
(2)参数是Java支持的引用类型,必须指明in/out/inout方向,也可以直接使用;
(3)参数是Android中的引用类型,则必须是可以序列化的(Parcelable),且必须指明in/out/inout方向,也就是说,如果Android里的类对象时Parcelable的,才可以传入,不是Parcelable的,不能传入,需用其他方法解决(AIDL接口方法);
(4)对于自定义的类TestData.java,需要另外增加一个AIDL文件TestData.aidl,首先该自定义类TestData.java必须实现Parcelable,然后再写一个声明该类的声明文件(TestData.aidl),该声明文件(TestData.aidl)用于告诉引用该类的aidl文件,此类遵守Parcelable协议,可以在AIDL中使用(其实就是在TestData.aidl文件中声明如下:parcelable TestData,即可):
1) TestData.java
public class TestData implements Parcelable { private String name = ""; public TestData() { } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.name); } protected TestData(Parcel in) { this.name = in.readString(); } public static final Parcelable.Creator<TestData> CREATOR = new Parcelable.Creator<TestData>() { public TestData createFromParcel(Parcel source) { return new TestData(source); } public TestData[] newArray(int size) { return new TestData[size]; } }; }2) TestData.aidl
package com.tencent.qqpim.apps.photo.sdk.aidl; parcelable TestData;3) test.aidl
package com.tencent.qqpim.apps.photo.sdk.aidl; import com.tencent.qqpim.apps.photo.sdk.aidl.TestData; interface test { void fn1(int x, String y); List<String> fn2(in TestData td); }
必须为AIDL文件中的接口成员函数的引用参数类型指明读写方向,这样可以做到优化,因为传输类对象到AIDL的时候,AIDL需要对类对象进行Parcelable(序列化),指明in/out/inout可以做到只写或只读或读写对象,省时。所有的基本数据类型都是默认in的,可以不用显示指定,但是所有非基本类型(对象引用)都必须是Parcelable、且必须显示指定in/out/inout。
【AIDL文件是主进程和Service进程的接口】
【AIDL文件中的接口函数如果是在Service进程中实现,则是在Service进程中执行,并且是为了让主进程调用】
【AIDL文件中的接口函数如果是在主进程中实现,则是在主进程中执行,并且是为了让Service进程调用】
2. Service进程回调主进程中的代码(以更新Activity)
AIDL文件可以有多个,AIDL文件可以在Service进程中运行,也可以在主进程中运行,主要是看改AIDL接口是在哪里实现的,如果在Service进程中实现,则是在Service中运行的;如果是在主进程中实现的,则是在主进程中运行;AIDL全称是Andorid接口定义语言,也就是说只是在AIDL文件中定义进程间都可以使用的接口,具体是在哪个进程中运行,主要是看在哪个进程中实现这些接口;在主进程中实现的AIDL接口,主要是让Service进程来调用(回调)主进程中接口的,在Service进程中是实现的接口,主要是让主进程调用(回调)Service进程中接口的。
AIDL文件接口如果是主进程中实现,则表示该接口是在主进程中运行的,用来让Service进程来调用的;如果是在Service进程中实现,则表示是在Service线程中执行,用来让主进程来调用。
例子如下:
(1)MainActivity.class
package com.habby.test;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.widget.TextView;
import com.example.com.habby.test.R;
import com.habby.test.aidl.IRemoteCallback;
import com.habby.test.aidl.IRemoteService;
/**
* 主进程
* 让Service进程回调主进程中的修改界面的回调函数
*/
public class MainActivity extends Activity {
private final String TAG = "MainActivity";
private TextView mNameTv; // 被更改的View
private IRemoteService mRemoteService; // Service进程中执行的远程接口
private static final int sCHANGE_VIEW_SUCCESS = 1;
/**
* 主进程中执行的接口,更新主进程中的界面。
*/
public IRemoteCallback.Stub mCallback = new IRemoteCallback.Stub() {
@Override
public void changeView() throws RemoteException {
Log.d(TAG, "activity pid = " + Process.myPid());
Message.obtain(mHandler, sCHANGE_VIEW_SUCCESS).sendToTarget();
}
};
/**
* 当主进程的Activity连接上Service进程中的Service后,回调该对象
*/
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) { // 连接断开
Log.d(TAG, "onServiceDisconnected()");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) { // 连接成功
// 获得Service进程中的远程接口
mRemoteService = IRemoteService.Stub.asInterface(service);
if (mRemoteService != null) {
try {
/**
* 主进程获取Service进程中的数据、执行Service进程中的方法;
*/
mRemoteService.getPid();
mRemoteService.basicTypes(1, 2L, true, 2.0f, 2.0D, "I love Merry");
/**
* 主进程把主进程中的更改View的接口(IRemoteCallback.aidl)传入Service进程,
* 让Service进程可以更改主进程中的数据(View)。
*/
mRemoteService.setCallback(mCallback);
} catch (android.os.RemoteException e) {
e.printStackTrace();
}
}
}
};
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case sCHANGE_VIEW_SUCCESS:
mNameTv.setText(R.string.name);
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate()");
setContentView(R.layout.activity_main);
mNameTv = (TextView) findViewById(R.id.name);
Log.d(TAG, "pid = " + Process.myPid());
// 绑定服务
Intent intent = new Intent(this, AIDLService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy()");
unbindService(mConnection); // 取消服务
super.onDestroy();
}
}
(2)AIDLService.class
package com.habby.test;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
import android.util.Log;
import com.habby.test.aidl.IRemoteCallback;
import com.habby.test.aidl.IRemoteService;
// Service进程
public class AIDLService extends Service {
private final String TAG = "AIDLService";
private IRemoteCallback mViewCallback;
public class RemoteService extends IRemoteService.Stub {
@Override
public int getPid() {
int pid = Process.myPid();
Log.d(TAG, "AIDLService pid = " + pid);
return pid;
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
Log.d(TAG, "/" + anInt + "/" + aLong + "/" + aBoolean + "/" +
aFloat + "/" + aDouble + "/" + aString + "/");
}
@Override
public void setCallback(IRemoteCallback callback) {
Log.d(TAG, "setCallback()");
try {
callback.changeView();
} catch (android.os.RemoteException e) {
e.printStackTrace();
}
mViewCallback = callback;
}
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate()");
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind()");
return new RemoteService();
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy()");
super.onDestroy();
}
}
(3)IRemoteService.aidl,Service进程中的调用,在Service进程中实现
package com.habby.test.aidl;
import com.habby.test.aidl.IRemoteCallback;
interface IRemoteService {
int getPid();
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
void setCallback(IRemoteCallback callback);
}
(4)IRemoteCallback.aidl,主进程中的调用,在主进程中实现
package com.habby.test.aidl;
interface IRemoteCallback {
void changeView();
}
(5)AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.com.habby.test"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.habby.test.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.habby.test.AIDLService"
android:process="com.habby.remote" />
</application>
</manifest>
3. 主进程调用Service进程的代码(Service进程提供的服务)
例子如下:
(1)RemoteCallExampleActivity.java
package com.habby.remote;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import com.habby.remote.aidl.IRemoteCall;
public class RemoteCallExampleActivity extends Activity {
private IRemoteCall iRemoteCall; // aidl服务接口
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 启动Service
Intent remotecall = new Intent(this, RemoteCallService.class);
bindService(remotecall, new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iRemoteCall = IRemoteCall.Stub.asInterface(service);
try {
String args = "I love Merry !";
iRemoteCall.setListener(args);
} catch(RemoteException e) {
e.printStackTrace();
}
}
}, Context.BIND_AUTO_CREATE);
}
@Override
protected void onResume() {
super.onResume();
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
(2)RemoteCallService.java
package com.habby.remote;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import com.habby.remote.aidl.IRemoteCall;
public class RemoteCallService extends Service {
private final String TAG = "Habby";
/**
* Service实现的aidl策略
*/
public class RemoteCall extends IRemoteCall.Stub {
@Override
public void setListener(String listener) {
Log.i(TAG, "Service RemoteCall() setListener()" + listener);
}
}
@Override
public IBinder onBind(Intent arg0) {
return new RemoteCall();
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"Service onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,"Service onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.d(TAG,"Service onDestroy()");
super.onDestroy();
}
}
(3)IRemoteCall.aidl,在包com.habby.remote.aidl中:
package com.habby.remote.aidl;
interface IRemoteCall {
void setListener(String listener);
}
(4)AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.com.habby.remote"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.habby.remote.RemoteCallExampleActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.habby.remote.RemoteCallService"
android:process="com.habby.subprocess" > <!-- 把Service作为一个的进程 -->
<intent-filter>
<action android:name="com.habby.remote.RemoteCallService.action" />
</intent-filter>
</service>
</application>
</manifest>
注意:在做类似项目的过程中,遇到一个问题值得注意:
Service的onBind(),在没有调用unbindService()的时候,无论调用多少次bindService()该onBind()都只会被调用一次(但是ServiceConnection会每次都被调用),且是在第一次bindSerivce()的时候被调用,也就说只绑定一次(前提是没有调用unbindService())。