android進階3step2:Android App通訊——AIDL實現遠端服務的通訊
安卓介面描述語言AIDL
全稱:Android Interface definition language
作用:程序間的通訊介面(實現兩個程序資料共享)
IBinder僅限於同一個程序間的資料共享
定義轉:https://www.jianshu.com/p/29999c1a93cd
AIDL 意思即 Android Interface Definition Language,翻譯過來就是Android介面定義語言,是用於定義伺服器和客戶端通訊介面的一種描述語言,可以拿來生成用於IPC的程式碼。從某種意義上說AIDL其實是一個模板,因為在使用過程中,實際起作用的並不是AIDL檔案,而是據此而生成的一個IInterface的例項程式碼,AIDL其實是為了避免我們重複編寫程式碼而出現的一個模板
設計AIDL這門語言的目的就是為了實現程序間通訊。在Android系統中,每個程序都執行在一塊獨立的記憶體中,在其中完成自己的各項活動,與其他程序都分隔開來。可是有時候我們又有應用間進行互動的需求,比較傳遞資料或者任務委託等,AIDL就是為了滿足這種需求而誕生的。通過AIDL,可以在一個程序中獲取另一個程序的資料和呼叫其暴露出來的方法,從而滿足程序間通訊的需求
通常,暴露方法給其他應用進行呼叫的應用稱為服務端,呼叫其他應用的方法的應用稱為客戶端,客戶端通過繫結服務端的Service來進行互動
一、遠端啟動服務
ServiceDemo實現同一個程序間啟動、停止、繫結、解綁服務等操作
依次點選
- 啟動服務
- 繫結服務
- 解綁服務
- 停止服務
/com.demo.servicedemo D/MyService-vv: onCreate: /com.demo.servicedemo D/MyService-vv: onStartCommand: /com.demo.servicedemo D/MyService-vv: onBind: /com.demo.servicedemo D/MainActivity-vv: onServiceConnected: /com.demo.servicedemo D/MainActivity-vv: 當前進度是: 2 /com.demo.servicedemo D/MyService-vv: onUnbind: /com.demo.servicedemo D/MyService-vv: onDestroy:
MyService.java
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
/**
* <p>檔案描述:<p>
* <p>作者:Mr-Donkey<p>
* <p>建立時間:2018/11/25 14:03<p>
* <p>更改時間:2018/11/25 14:03<p>
* <p>版本號:1<p>
*/
/**
* Service進行同一個程序的通訊
*/
public class MyService extends Service {
private static final String TAG = "MyService-vv";
private int i;
public static boolean IsBinder = false;
public MyService() {
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: ");
//開啟一個執行緒(從1數到100),用於模擬耗時的任務
new Thread() {
@Override
public void run() {
super.run();
try {
for (i = 1; i <= 100; i++) {
sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
/**
* 繫結服務後會在on
*
* @param intent
* @return
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
IsBinder = true;
return new MyBinder();
}
/**
* Binder是IBinder的空實現類
* 提供一個方法用於獲取當前計數的進度
*/
public class MyBinder extends Binder {
public int getProcess() {
return i;
}
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind: ");
IsBinder = false;
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
}
在AndroidManifest.xml中 註冊Service
android:exported="true" 表明當前Service對其他App開放
並且要設定action。
<application
....
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.demo.myservice" />
</intent-filter>
</service>
</application>
MainActivity.java
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity-vv";
private MyService.MyBinder mb;
private ServiceConnection conn = new ServiceConnection() {
//當客戶端正常連線著服務時,執行服務的繫結操作會被呼叫
//此時傳來的IBinder物件是onbinder的返回的物件
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: ");
//將IBinder物件轉成MyBinder
mb = (MyService.MyBinder) service;
//呼叫MyBinder中的獲取進度的方法:實現監控
int process = mb.getProcess();
Log.d(TAG, "當前進度是: " + process);
}
//當客戶端和服務的連線丟失了
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected: ");
}
};
//IBinder
//ServicerConnection:用於繫結客戶端和Service
//進度監控
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* 按鈕點選事件
*
* @param v
*/
public void operate(View v) {
switch (v.getId()) {
case R.id.start:
//啟動服務:建立-->啟動-->銷燬
//如果服務已經建立了,後續重複啟動,操作的都是同一個服務,不會再重新建立了,除非你先銷燬它
Intent it1 = new Intent(this, MyService.class);
startService(it1);
break;
case R.id.stop:
Intent it2 = new Intent(this, MyService.class);
stopService(it2);
break;
case R.id.bind:
//繫結服務:最大的 作用是用來實現對Service執行的任務進行進度監控
//如果服務不存在: onCreate-->onBind-->onUnbind-->onDestory
// (此時服務沒有再後臺執行,並且它會隨著Activity的摧毀而解綁並銷燬)
//服務已經存在:那麼bindService方法只能使onBind方法被呼叫,而unbindService方法只能使onUnbind被呼叫
Intent it3 = new Intent(this, MyService.class);
bindService(it3, conn, BIND_AUTO_CREATE);
break;
case R.id.unbind:
//解綁服務
if (MyService.IsBinder)
unbindService(conn);
break;
}
}
}
新建一個AIDLDemo來遠端啟動服務
主要是通過顯示宣告Intent來操作另外一個Servicedemo app
確保Servicedemo的AndroidManifest.xml中配置寫正確,還有兩個app同時執行在手機上
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* android5.0後要通過顯式宣告Intent來實現遠端啟動服務
*/
public void operate(View view) {
switch (view.getId()) {
case R.id.start:
Intent it1 = new Intent();
it1.setAction("com.demo.myservice");
it1.setPackage("com.demo.servicedemo");
startService(it1);
break;
case R.id.stop:
Intent it2 = new Intent();
it2.setAction("com.demo.myservice");
it2.setPackage("com.demo.servicedemo");
stopService(it2);
break;
case R.id.bind:
Intent it3 = new Intent();
it3.setAction("com.demo.myservice");
it3.setPackage("com.demo.servicedemo");
bindService(it3, conn, BIND_AUTO_CREATE);
break;
case R.id.unbind:
//解綁服務
unbindService(conn);
break;
}
}
}
這樣一來就可以遠端操作了。
二、遠端服務通訊
AIDL的使用
通過AIDL,可以在一個程序中獲取另一個程序的資料和呼叫其暴露出來的方法,從而滿足程序間通訊的需求
- 建立AIDL檔案
- 自動生成AIDL的java檔案
- 使用AIDL
在servicedemo中
一、建立AIDL檔案
main資料夾右鍵New——>AIDL——>AIDL File
然後就在main資料夾下生成一個aidl資料夾這個資料夾中又有一個和java中的包名一致的包,裡面生成自定義的.aidl檔案
IMyAidlInterface.aidl 裡面的內容
可以看見其實一個介面,可以寫你想要實現的介面方法,以下是預設的,那麼我新增一個獲取進度的方法getProcess()
// IMyAidlInterface.aidl
package com.demo.servicedemo;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* 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);
}
新增方法後(去不去掉預設的方法都可以) 我這裡去掉了
// IMyAidlInterface.aidl
package com.demo.servicedemo;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
void getProcess();
}
2.自動生成AIDL的java檔案
Builde——>Rebuild Project 就可以自動生成aidl的java檔案了
位於:build——>generated——>source——>aidl——>debug下
這是自動生成的aidl的java檔案,不用看得懂,其實就是實現程序間通訊的具體實現
注意:
public static abstract class Stub extends android.os.Binder implements com.demo.servicedemo.IMyAidlInterface
1.其實Stub抽象類是繼承了Binder(是IBinder的一個空實現類),那麼Stub也是IBinder的子類
就可以在 服務繫結的時候,返回這個Stub物件來替換之前的MyBinder();
修改後的onBind@Nullable @Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind: "); IsBinder = true; return new MyBinder(); } /** * Binder是IBinder的空實現類 * 提供一個方法用於獲取當前計數的進度 */ public class MyBinder extends Binder { public int getProcess() { return i; } }
@Nullable @Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind: "); IsBinder = true; return new IMyAidlInterface.Stub() { @Override public void getProcess() throws RemoteException { Log.d(TAG, "getProcess: " + i); } }; }
返回一個Stub物件
2.public static com.demo.servicedemo.IMyAidlInterface asInterface(android.os.IBinder obj)
asInterface是Stub特別重要的一個方法
可以通過傳入Stub物件來獲取com.demo.servicedemo.IMyAidlInterface的例項,進而呼叫在com.demo.servicedemo.IMyAidlInterface自定義的方法來獲取資料
在繫結服務成功後,onServiceConnected中收到onBind()返回的IBinder物件
此時可以通過
IMyAidlInterface im = IMyAidlInterface.Stub.asInterface(service); //獲取自定義方法 im.getProcess();
來獲取當前進度
完整程式碼:
private ServiceConnection conn = new ServiceConnection() { //當客戶端正常連線著服務時,執行服務的繫結操作會被呼叫 //此時傳來的IBinder物件是onbinder的返回的物件 @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG, "onServiceConnected: "); try { //拿到IMyAidlInterface物件例項 IMyAidlInterface im = IMyAidlInterface.Stub.asInterface(service); //獲取自定義方法 im.getProcess(); } catch (RemoteException e) { e.printStackTrace(); } } //當客戶端和服務的連線丟失了 @Override public void onServiceDisconnected(ComponentName name) { Log.d(TAG, "onServiceDisconnected: "); }
IMyAidlInterface.java
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/mac/Downloads/Android3Step1/servicedemo/src/main/aidl/com/demo/servicedemo/IMyAidlInterface.aidl
*/
package com.demo.servicedemo;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.demo.servicedemo.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.demo.servicedemo.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.demo.servicedemo.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.demo.servicedemo.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.demo.servicedemo.IMyAidlInterface))) {
return ((com.demo.servicedemo.IMyAidlInterface)iin);
}
return new com.demo.servicedemo.IMyAidlInterface.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_getProcess:
{
data.enforceInterface(DESCRIPTOR);
this.getProcess();
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.demo.servicedemo.IMyAidlInterface
{
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;
}
@Override public void getProcess() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getProcess, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getProcess = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public void getProcess() throws android.os.RemoteException;
}
在AIDLDemo中
- 同樣建立一樣的aidl檔案
- 生成aidl的java檔案
- 使用aidl
copy main資料夾下的aidl資料夾,然後rebuild project生成java檔案
這樣是保持aidl的匹配一致
那麼在AIDLDemo中的MainActivity中就可以獲取到servicedemo中的進度值了
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
IMyAidlInterface im = IMyAidlInterface.Stub.asInterface(service);
im.getProcess();
Log.d(TAG, "onServiceConnected: AIDLDemo拿到進度值");
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
在AIDLDemo中依次進行啟動、繫結、解綁、停止服務的log
servicedemo的log
/com.demo.servicedemo D/MyService-vv: onCreate:
/com.demo.servicedemo D/MyService-vv: onStartCommand:
/com.demo.servicedemo D/MyService-vv: onBind:
/com.demo.servicedemo D/MyService-vv: getProcess: 2
/com.demo.servicedemo D/MyService-vv: onUnbind:
/com.demo.servicedemo D/MyService-vv: onDestroy:
AIDLDemo的log
/com.demo.android3step2 D/MainActivity-vv: onServiceConnected: AIDLDemo拿到進度值
進行程序間物件的傳遞也是一樣的性質:
更多內容參考:
Android AIDL使用詳解
https://www.jianshu.com/p/29999c1a93cd