1. 程式人生 > >android進階3step2:Android App通訊——AIDL實現遠端服務的通訊

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實現同一個程序間啟動、停止、繫結、解綁服務等操作

依次點選

  1. 啟動服務
  2. 繫結服務
  3. 解綁服務
  4. 停止服務
/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,可以在一個程序中獲取另一個程序的資料和呼叫其暴露出來的方法,從而滿足程序間通訊的需求

  1. 建立AIDL檔案
  2. 自動生成AIDL的java檔案
  3. 使用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();

    @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;
        }
    }
修改後的onBind 
    @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中

  1. 同樣建立一樣的aidl檔案
  2. 生成aidl的java檔案
  3. 使用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