1、概述
AIDL在android系統中的作用
AIDL,Android Interface definition language的縮寫,它是一種android內部進程通信接口的描述語言,通過它我們可以定義進程間的通信接口。最近看了下AIDL在Android系統中的用法,在網上看到很多初學的朋友不太明白AIDL的實際作用,android提供了很多進程間通信的組件,像action、broadcast、contentprovide都可以實現進程間的通信,爲什麼還要用AIDL這個東西呢?我在android源碼中實現了一個自己寫的AIDL例子,用以簡單解釋下AIDL的作用。
有開發過藍牙或者WIFI應用的朋友肯定都知道,要去操作它必須先獲得一個管理類,比如WIFI的管理類是WifiManager,通過getSystemService(Context.WIFI_SERVICE)就可以得到wifi的管理權限,這個提供了很多的方法可以讓用戶去操作它,比如打開wifi可以調用setWifiEnabled(true)方法。那這個Manager到底做了什麼工作呢?是怎樣實現打開wifi的呢?其實這個Manager只是一個管理類,真正幹活的另有其人,是一個叫WifiService的系統服務。在Android系統中有很多的Manager,wifi的管理類叫WifiManager,藍牙的管理類叫BluetoothManager,但是,只要有xxxManager.java,就會有Ixxx.aidl,並且有xxxService.java。這個aidl類就是實現Manager和Service通信的橋樑。
Binder驅動,Binder客戶端、服務端這三者之間的關係:
binder通信的四個角色應該是client組件,server組件,serviceManager和binder驅動。client組件和server組件進行通信,要通過ServiceManager查詢server組件的引用。server向ServiceManager註冊一個binder屍體,client通過ServiceManager獲得binder實體的一個引用,這樣client和server就可以通信了
2、Android studio 中AIDL的使用
在eclipse裏面操作時aidl文件個java文件都放在一個包下, 客戶端直接將該包複製到自己的目錄下,然後可以另外建另外一個包放其他代碼。但在android studio下面這樣是不可以的,需要在src單獨建一個AIDL文件夾,將aidl文件放在裏面,java文件在另外的包下,這樣就導致服務端項目與客戶端項目的包名必須相同。在as中project相當於es的workspace,moudle相當於es的project,在eclipse裏面是兩個project在通信,so 我猜測在as中是兩個mould在通信,因此建了一個project(裏面自帶一個app module),讓app moulde作爲客戶端然後又另外加了一個服務端moudle 叫aidlserver。在aidlserver的project視圖下面的src下面右鍵new 選擇AIDL ,AIDL Folder ,然後將自己的aidl文件放入其中。
在Android Studio下創建AIDL Serivce。
建立ADIL Service的步驟比建立普通Service要多一些,主要有:
1、創建AIDL文件,在這裏面定義遠程接口。
2、生成Java接口文件。
3、建立一個Service的子類,並且記得在AndroidManifest.xml文件中配置。
在客戶端調用ADIL Servie:
1、拷貝服務器端的AIDL文件,並生成Java接口文件。
2、用BindService來調用Service,與調用普通Serivce相類似,只是獲取IBinder的方式有點不一樣。
下面是一個簡單的例子
一、建立服務端AIDL文件
在項目名稱上右鍵>NEW>AIDL>AIDL File,這樣就創建了一個ADIL文件,命名爲AIDLServerService.aidl
package com.test.huangxingli.aidlserver;
import com.test.huangxingli.aidlserver.Girl;
// Declare any non-default types here with import statements
interface AIDLServerService {
String sayHello();
Girl getGirl();
}
再創建Girl.aidlpackage com.test.huangxingli.aidlserver;
parcelable Girl;
建好這兩個文件後再編寫Girl.java .注意要先寫Girl.aidl然後再寫Girl.java
剛開始時 我先寫的Girl.java 然後再寫Girl.aidl時提示不能創建重名的文件。
Girl.java如下:
package com.test.huangxingli.aidlserver;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by huangxingli on 2015/3/27.
*/
public class Girl implements Parcelable{
String name;
int age;
public Girl() {
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
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.writeInt(age);
}
public static final Creator<Girl> CREATOR=new Creator<Girl>() {
@Override
public Girl createFromParcel(Parcel source) {
Girl girl=new Girl();
girl.setName(source.readString());
girl.setAge(source.readInt());
return girl;
}
@Override
public Girl[] newArray(int size) {
return new Girl[size];
}
};
}
如圖
接着寫Service類MAIDLServerService,如下:
package com.test.huangxingli.aidlserver;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class MAIDLServerService extends Service {
public MAIDLServerService() {
}
AIDLServerService.Stub binder=new AIDLServerService.Stub() {
@Override
public String sayHello() throws RemoteException {
return "hello, i am from AIDLServerService";
}
@Override
public Girl getGirl() throws RemoteException {
Girl girl=new Girl();
girl.setAge(25);
girl.setName("lily");
return girl;
}
};
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return binder;
}
}
然後在AndroidManifest.xml中裏面將該服務器端的Service註冊一下:如下<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.huangxingli.aidlserver" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<service
android:name="com.test.huangxingli.aidlserver.MAIDLServerService"
android:process=":remote"
>
<intent-filter>
<action android:name="com.test.huangxingli.aidlserver.MAIDLServerService"></action>
</intent-filter>
</service>
</application>
</manifest>
server端結構如圖
二、建立客戶端AIDL文件
首先要拷貝AIDL文件,這裏要保證文件的內容一模一樣,包括包的名稱,比如本例子中服務器端AIDL文件所在包的名稱是com.test.huangxingli.aidlserver,如何做到這一點,先新建一個項目,然後在:項目文件夾/app/src/main目錄下建立一個aidl文件夾,與java文件夾同級,在Android Studio中就可以看到這個目錄,在這個目錄上右鍵New>Package,建立一個com.test.huangxingli.aidlserver的包,再將aidl文件拷進去。這樣才能保證生成的java接口文件完全一樣,否則會提示找不到接口。參照下圖操作
Intent mIntent = new Intent();
mIntent.setAction("com.test.huangxingli.aidlserver.MAIDLServerService");
Intent eintent = new Intent(createExplicitFromImplicitIntent(MainActivity.this, mIntent));
bindService(eintent,connection,BIND_AUTO_CREATE);
MainActivity.this.startService(eintent);
package com.test.huangxingli.aidlserver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.List;
public class MainActivity extends ActionBarActivity {
TextView textView;
Button button;
AIDLServerService aidlServerService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button= (Button) findViewById(R.id.button);
textView= (TextView) findViewById(R.id.textView);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Intent intent=new Intent();
// intent.setAction("com.test.huangxingli.aidlserver.MAIDLServerService");//你定義的service的action
// intent.setPackage(getPackageName());
//
// bindService(intent,connection,BIND_AUTO_CREATE);
Intent mIntent = new Intent();
mIntent.setAction("com.test.huangxingli.aidlserver.MAIDLServerService");
Intent eintent = new Intent(createExplicitFromImplicitIntent(MainActivity.this, mIntent));
bindService(eintent,connection,BIND_AUTO_CREATE);
MainActivity.this.startService(eintent);
}
});
}
/***
* Android L (lollipop, API 21) introduced a new problem when trying to invoke implicit intent,
* "java.lang.IllegalArgumentException: Service Intent must be explicit"
*
* If you are using an implicit intent, and know only 1 target would answer this intent,
* This method will help you turn the implicit intent into the explicit form.
*
* Inspired from SO answer: http://stackoverflow.com/a/26318757/1446466
* @param context
* @param implicitIntent - The original implicit intent
* @return Explicit Intent created from the implicit original intent
*/
public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
ServiceConnection connection=new ServiceConnection() {
String content;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
aidlServerService=AIDLServerService.Stub.asInterface(service);
try {
content=aidlServerService.sayHello()+"\n";
Girl girl=aidlServerService.getGirl();
content +="my name is "+girl.getName();
textView.setText(content);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
aidlServerService=null;
}
};
}
好了,就到這裏就可以運行程序了
代碼先安裝server端的代碼,然後再安裝client端的。。。
參考文獻:
http://blog.csdn.net/huangxingli/article/details/44674751
http://blog.csdn.net/lmj623565791/article/details/38461079
http://blog.csdn.net/shenzhonglaoxu/article/details/42737195