一、什麼是AIDL
AIDL 的全名是 Android Interface definition language,按照字面理解,就是一種在安卓的接口定義語言,而它的作用就是主要用於不同進程之間的通信
二、應用場景
Android中不同進程是不共享同一片內存區域的,通常情況下,一個App就是一個進程。而我們也可以在AndroidManifest裏進行配置,讓某一個組件獨立出去,最常見的就是將Service獨立的運行在一個進程裏。例如支付服務,音樂服務,導航服務,守護進程等,這樣可以保證服務不被程序的其它部分所幹擾,或是保證2個進程不會同時被系統殺死,或是一定程度上提高服務的安全性。但是,要想與這些不在同一個進程中的服務進行通信的話,傳統的單例啦什麼的都是不可用的,於是就需要使用AIDL了
三、使用
在Eclipse裏AIDL的教程很多,但是現在都開始逐步轉變成Android Studio了,我們就看一看Studio裏如何使用AIDL與Service進行通信
1.建立Activity
首先我們建立一個Activity,裏面有3個按鈕,分別是BindService、ControlService、ChangeData,分別用來 鏈接服務、模擬對服務的控制、和與服務進行傳值
activity _main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/btn_bind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="綁定服務" />
<Button
android:id="@+id/btn_control"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="控制服務" />
<Button
android:id="@+id/btn_change"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="傳值" />
</LinearLayout>
在Activity裏綁定佈局,並設施監聽,看以下 MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button bindBtn,controlBtn,changeDataBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindBtn = (Button) findViewById(R.id.btn_bind);
controlBtn = (Button) findViewById(R.id.btn_control);
changeDataBtn = (Button) findViewById(R.id.btn_change);
bindBtn.setOnClickListener(this);
controlBtn.setOnClickListener(this);
changeDataBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_bind://綁定服務
break;
case R.id.btn_control://控制服務
break;
case R.id.btn_change://傳遞數據
break;
}
}
}
着就是簡單的佈局
2.我們去寫一TestService
public class TestService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
然後去AndroidManifest把我們剛寫的服務註冊一下
<service android:name=".TestService"
android:process=":remote">
<intent-filter>
<action android:name="com.example.chenfengyao.aidldemo.TestService"/>
</intent-filter>
</service>
我們的服務是要用隱式Intent啓動的,所以要有action屬性,而android:process=":remote"
着一句的意思就是,有該屬性的服務將在一個獨立的進程中運行了,加上“:remote”則,進程名字將是這個App的進程名加:remot,另外,這裏的“:remote”是可以寫別的的,不過大家都習慣於寫它了。
3.生成AIDL文件
// MyTestAIDL.aidl
package com.example.chenfengyao.aidldemo;
// Declare any non-default types here with import statements
interface MyTestAIDL {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
可以看到系統幫我們生成的AIDL文件裏已經寫好了一個方法,而且通過讀註釋,我們知道,利用AIDL傳遞int,long,boolean,float,double,String這些數據的時候是不需要別的工作的,直接傳就好了。我們先把系統的這個方法刪除了,寫一個自己的方法。interface MyTestAIDL {
void controlService(int flag);
}
寫完了之後,我們編譯一下,只有編譯了,系統纔會將這個AIDL文件給我們生成對應的JAVA代碼來使用,不然是用不了的。4.與服務綁定
public class TestService extends Service {
MyTestAIDL.Stub stub = new MyTestAIDL.Stub() {
@Override
public void controlService(int flag) throws RemoteException {
Log.i("TestService","flag:"+flag);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return stub;
}
}
這裏我們首先 創建一個Stub對象,在這個Stub對象裏,會要求我們重寫剛剛在AIDL裏寫的方法的,然後將這個Stub對象通過onBind方法返回,這樣服務端就寫好了。5.與Activity的綁定
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button bindBtn,controlBtn,changeDataBtn;
private MyTestAIDL myTestAIDL;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myTestAIDL = MyTestAIDL.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
myTestAIDL = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindBtn = (Button) findViewById(R.id.btn_bind);
controlBtn = (Button) findViewById(R.id.btn_control);
changeDataBtn = (Button) findViewById(R.id.btn_change);
bindBtn.setOnClickListener(this);
controlBtn.setOnClickListener(this);
changeDataBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_bind://綁定服務
Intent intent = new Intent("com.example.chenfengyao.aidldemo.TestService");
intent.setPackage("com.example.chenfengyao.aidldemo");
bindService(intent,connection,BIND_AUTO_CREATE);
break;
case R.id.btn_control://控制服務
if(myTestAIDL!=null){
try {
myTestAIDL.controlService(0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
case R.id.btn_change://傳遞數據
break;
}
}
}
首先,我們創建一個AIDL對象,然後在ServiceConnection的連接成功方法裏,將IBinder通過Stub的asInterface來給AIDL賦值,然後在連接服務的按鈕裏建立一個Intent對象,因爲是利用隱式Intent啓動的,所以需要給它action標籤裏的內容,另外需要注意的是,Android5.0之後Intent還必須加上目標的包名,不然會報錯的。最後通過bindService方法將Activity也連接上。再在controlService按鈕的回調裏,調用AIDL對象裏controlService方法,就可以調用服務,並將值傳給Service6.傳遞複雜數據
public class TestBean implements Parcelable{
String testString;
int testInt;
public TestBean(){}
protected TestBean(Parcel in) {
testString = in.readString();
testInt = in.readInt();
}
public static final Creator<TestBean> CREATOR = new Creator<TestBean>() {
@Override
public TestBean createFromParcel(Parcel in) {
return new TestBean(in);
}
@Override
public TestBean[] newArray(int size) {
return new TestBean[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(testString);
dest.writeInt(testInt);
}
}
// TestBean.aidl
package com.example.chenfengyao.aidldemo;
parcelable TestBean;
然後在我們最初的AIDL文件裏引包,並加入方法// MyTestAIDL.aidl
package com.example.chenfengyao.aidldemo;
// Declare any non-default types here with import statements
import com.example.chenfengyao.aidldemo.TestBean;
interface MyTestAIDL {
void controlService(int flag);
void changeData(in TestBean testBean);
}
之後在app的build.gradle加上以下代碼,把我們的AIDL文件夾也變成資源文件夾sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/aidl']
resources.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
}
}
編譯,回到我們的Service裏修改一下,實現我們新加入的方法public class TestService extends Service {
MyTestAIDL.Stub stub = new MyTestAIDL.Stub() {
@Override
public void controlService(int flag) throws RemoteException {
Log.i("TestService","flag:"+flag);
}
@Override
public void changeData(TestBean testBean) throws RemoteException {
Log.i("TestService","testBean:"+testBean.testString);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return stub;
}
}
最後再在Activity裏去測試一下 case R.id.btn_change://傳遞數據
TestBean testBean= new TestBean();
testBean.testString = "Test";
try {
myTestAIDL.changeData(testBean);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
至此利用AIDL傳值就實現啦。