1.基於前面寫的aidl使用,這段時間準備研究ActivityManager框架,對aidl進行了更深入的研究,因爲android框架大量使用了進程通信機制,所以,在研究android framework前認真研究一下AIDL的實現機制十分有必要的
2.前面講了aidl是 Android Interface definition language的縮寫,它是一種進程通信接口的描述,通過sdk解釋器對器進行編譯,會把它編譯成java代碼在gen目錄下,類路徑與aidl文件的類路徑相同。
3.aidl接口
package com.cao.android.demos.binder.aidl;
import com.cao.android.demos.binder.aidl.AIDLActivity;
interface AIDLService {
void registerTestCall(AIDLActivity cb);
void invokCallBack();
}
它編譯後生成的java文件如下
AIDLService.java詳細描述了aidl接口的實現,看上面圖示,AIDLActivity.aidl編譯成了一個接口AIDLActivity,一個存根類Stub,一個代理類Proxy
public interface AIDLService extends android.os.IInterface//與AIDLActivity.aidl中定義的接口對應的java接口實現
public static abstract class Stub extends android.os.Binder implements com.cao.android.demos.binder.aidl.AIDLService
//繼承android.os.Binder,在onTransact完成對通信數據的接收,通過不同通信參數code調用AIDLService接口方法,並回寫調用返回結果AIDLService接口方法需要在
//服務端實現
private static class Proxy implements com.cao.android.demos.binder.aidl.AIDLService
//實現AIDLService接口方法,但是方法只是執行代理遠程調用操作,具體方法操作在遠端的Stub存根類中實現
總的來說,AIDLActivity.aidl編譯會生成一個AIDLActivity接口,一個stub存根抽像類,一個proxy代理類,這個實現其實根axis的wsdl文件編譯生成思路是一致的,
stub存根抽像類需要在服務端實現,proxy代理類被客戶端使用,通過stub,proxy的封裝,屏蔽了進程通信的細節,對使用者來說就只是一個AIDLActivity接口的調用
4.根據以上思路使用aidl再看一下AIDLService調用實現代碼
--1.在服務端實現AIDLService.Stub抽象類,在服務端onBind方法中返回該實現類
--2.客戶端綁定service時在ServiceConnection.onServiceConnected獲取onBind返回的IBinder對象
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
Log("connect service");
mService = AIDLService.Stub.asInterface(service);
try {
mService.registerTestCall(mCallback);
} catch (RemoteException e) {
}
}
注意mConnection在bindservice作爲調用參數:bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
--3.AIDLService.Stub.asInterface(service);
public static com.cao.android.demos.binder.aidl.AIDLService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
//如果bindService綁定的是同一進程的service,返回的是服務端Stub對象本省,那麼在客戶端是直接操作Stub對象,並不進行進程通信了
if (((iin!=null)&&(iin instanceof com.cao.android.demos.binder.aidl.AIDLService))) {
return ((com.cao.android.demos.binder.aidl.AIDLService)iin);
}
//bindService綁定的不是同一進程的service,返回的是代理對象,obj==android.os.BinderProxy對象,被包裝成一個AIDLService.Stub.Proxy代理對象
//不過AIDLService.Stub.Proxy進程間通信通過android.os.BinderProxy實現
return new com.cao.android.demos.binder.aidl.AIDLService.Stub.Proxy(obj);
}
--4.調用AIDLService接口方法,如果是同一進程,AIDLService就是service的Stub對象,等同直接調用Stub對象實現的AIDLService接口方法
如果是一個proxy對象,那就是在進程間調用了,我們看一個客戶端調用的例子:
public void onClick(View v) {
Log("AIDLTestActivity.btnCallBack");
try {
mService.invokCallBack();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
--mService.invokCallBack()等同調用Proxy.invokCallBack,這個時候是進程間調用,我們看代理方法的實現
public void invokCallBack() throws android.os.RemoteException
{
//構造一個Parcel對象,該對象可在進程間傳輸
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
//DESCRIPTOR = "com.cao.android.demos.binder.aidl.AIDLService",描述了調用哪個Stub對象
_data.writeInterfaceToken(DESCRIPTOR);
//Stub.TRANSACTION_invokCallBack 標識調用Stub中哪個接口方法,mRemote在是構造Proxy對象的參數obj,也就是public void onServiceConnected(ComponentName className, IBinder service)
//中的service參數,它是一個BinderProxy對象,負責傳輸進程間數據。
mRemote.transact(Stub.TRANSACTION_invokCallBack, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
--5.BinderProxy.transact 該方法本地化實現
public native boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
//對應實現的本地化代碼 /frameworks/base/core/jni/android_util_Binder.cpp->static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj,
jobject replyObj, jint flags)
//具體進程通信在c代碼中如何實現,以後再深入研究。
--6.服務端進程數據接收
--調用堆棧
##AIDLService.Stub.onTransact
##AIDLService.Stub(Binder).execTransact
##NativeStart.run
--AIDLService.Stub.onTransact
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_registerTestCall:
{
data.enforceInterface(DESCRIPTOR);
com.cao.android.demos.binder.aidl.AIDLActivity _arg0;
_arg0 = com.cao.android.demos.binder.aidl.AIDLActivity.Stub.asInterface(data.readStrongBinder());
this.registerTestCall(_arg0);
reply.writeNoException();
return true;
}
//TRANSACTION_invokCallBack由前面客戶端調用的時候transact方法參數決定,code==TRANSACTION_invokCallBack,執行
//invokCallBack方法,方法由繼承Stud的服務端存根類實現。
case TRANSACTION_invokCallBack:
{
data.enforceInterface(DESCRIPTOR);
this.invokCallBack();
reply.writeNoException();
return true;
}
5.裏面設置本地C代碼的調用,我沒有深入研究,隨着後面我對android框架的深入,我會發blog進一步說民底層C代碼是如何實現進程通信的,關於AIDL進程通信,暫時研究到這裏。
原文:http://blog.csdn.net/stonecao/article/details/6579333
通過代碼來實現這個數據傳輸過程是冗長乏味的,Android提供了AIDL工具來處理這項工作。
AIDL IPC機制是面向接口的,像COM或Corba一樣,但是更加輕量級。它是使用代理類在客戶端和實現端傳遞數據。
- Calls made from the local process are executed in the same thread that is making the call. If this is your main UI thread, that thread continues to execute in the AIDL interface. If it is another thread, that is the one that executes your code in the service. Thus, if only local threads are accessing the service, you can completely control which threads are executing in it (but if that is the case, then you shouldn't be using AIDL at all, but should instead create the interface byimplementing a Binder).
- Calls from a remote process are dispatched from a thread pool the platform maintains inside of your own process. You must be prepared for incoming calls from unknown threads, with multiple calls happening at the same time. In other words, an implementation of an AIDL interface must be completely thread-safe.
- The
oneway
keyword modifies the behavior of remote calls. When used, a remote call does not block; it simply sends the transaction data and immediately returns. The implementation of the interface eventually receives this as a regular call from theBinder
thread pool as a normal remote call. Ifoneway
is used with a local call, there is no impact and the call is still synchronous.
AIDL使用簡單的語法來聲明接口,描述其方法以及方法的參數和返回值。這些參數和返回值可以是任何類型,甚至是其他AIDL生成的接口。
需要特別注意的是,對於非基本數據類型,也不是String和CharSequence類型的,需要有方向指示,包括in、out和inout,in表示由客戶端設置,out表示由服務端設置,inout是兩者均可設置。
import com.demo.Person;
interface IMyService {
void savePersonInfo(in Person person);
List<Person> getAllPerson();
}
private LinkedList<Person> personList =new LinkedList<Person>();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
privatefinal IMyService.Stub mBinder = new IMyService.Stub(){
@Override
public void savePersonInfo(Person person) throws RemoteException {
if (person != null){
personList.add(person);
}
}
@Override
public List<Person> getAllPerson()throws RemoteException {
return personList;
}
};
}
public staticabstract class Stubextends android.os.Binder implements com.demo.IMyService
1) void writeToParcel(Parcel dest, int flags) 將需要序列化存儲的數據寫入外部提供的Parcel對象dest。而看了網上的代碼例子,個人猜測,讀取Parcel數據的次序要和這裏的write次序一致,否則可能會讀錯數據。具體情況我沒試驗過!
2) describeContents() 沒搞懂有什麼用,反正直接返回0也可以
3) static final Parcelable.Creator對象CREATOR 這個CREATOR命名是固定的,而它對應的接口有兩個方法:
createFromParcel(Parcel source) 實現從source創建出JavaBean實例的功能
newArray(int size) 創建一個類型爲T,長度爲size的數組,僅一句話(return new T[size])即可。估計本方法是供外部類反序列化本類數組使用。
private String name;
private String telNumber;
private int age;
public Person() {}
public Person(Parcel pl){
name = pl.readString();
telNumber = pl.readString();
age = pl.readInt();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTelNumber() {
return telNumber;
}
public void setTelNumber(String telNumber) {
this.telNumber = telNumber;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest,int flags) {
dest.writeString(name);
dest.writeString(telNumber);
dest.writeInt(age);
}
publicstatic final Parcelable.Creator<Person> CREATOR =new Parcelable.Creator<Person>() {
@Override
public Person createFromParcel(Parcel source) {
returnnew Person(source);
}
@Override
public Person[] newArray(int size) {
returnnew Person[size];
}
};
}
然後創建Person.aidl文件,注意這裏的parcelable和原來實現的Parcelable 接口,開頭的字母p一個小寫一個大寫:
parcelable Person;
3. 拋出的異常是不能返回給調用者(跨進程拋異常處理是不可取的)。
3. 客戶端獲取接口
private ServiceConnection mRemoteConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mRemoteService = IMyService.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName className) {
mRemoteService = null;
}
};
* Cast an IBinder object into an com.demo.IMyService interface,
* generating a proxy if needed.
*/
public static com.demo.IMyService asInterface(android.os.IBinder obj) {...}
unbindService(mRemoteConnection);
}else{
bindService(new Intent("com.demo.IMyService"),
mRemoteConnection, Context.BIND_AUTO_CREATE);
}
mIsRemoteBound = !mIsRemoteBound;
new View.OnClickListener(){
private int index = 0;
@Override
public void onClick(View view) {
Person person = new Person();
index = index + 1;
person.setName("Person" + index);
person.setAge(20);
person.setTelNumber("123456");
try {
mRemoteService.savePersonInfo(person);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
listPersonButton.setOnClickListener(
new View.OnClickListener(){
@Override
public void onClick(View view) {
List<Person> list = null;
try {
list = mRemoteService.getAllPerson();
} catch (RemoteException e) {
e.printStackTrace();
}
if (list != null){
StringBuilder text = new StringBuilder();
for(Person person : list){
text.append("\nPerson name:");
text.append(person.getName());
text.append("\n age :");
text.append(person.getAge());
text.append("\n tel number:");
text.append(person.getTelNumber());
}
inputPersonEdit.setText(text);
}else {
Toast.makeText(ServiceActivity.this,"get data error",
Toast.LENGTH_SHORT).show();
}
}
});
Permission權限
如果Service在AndroidManifest.xml中聲明瞭全局的強制的訪問權限,其他引用必須聲明權限才能來start,stop或bind這個service.
另外,service可以通過權限來保護她的IPC方法調用,通過調用checkCallingPermission(String)方法來確保可以執行這個操作。
AndroidManifest.xml的Service元素
<intent-filter>
<actionandroid:name="com.demo.IMyService"/>
</intent-filter>
</service>
The name of the process where the service is to run. Normally, all components of an application run in the default process created for the application. It has the same name as the application package. The <application> element's process attribute can set a different default for all components. But component can override the default with its own process attribute, allowing you to spread your application across multiple processes.