1. 程式人生 > >Android:遠端服務Service(含AIDL & IPC講解)

Android:遠端服務Service(含AIDL & IPC講解)

前言


  • Service作為Android四大元件之一,應用非常廣泛
  • 本文將介紹Service其中一種常見用法:遠端Service

目錄

目錄

1. 遠端服務與本地服務的區別

  • 遠端服務與本地服務最大的區別是:遠端Service與呼叫者不在同一個程序裡(即遠端Service是執行在另外一個程序);而本地服務則是與呼叫者執行在同一個程序裡
  • 二者區別的詳細區別如下圖:

按執行地點分類

2. 使用場景

多個應用程式共享同一個後臺服務(遠端服務)

即一個遠端Service與多個應用程式的元件(四大元件)進行跨程序通訊

使用場景

3. 具體使用

  • 為了讓遠端Service與多個應用程式的元件(四大元件)進行跨程序通訊(IPC),需要使用AIDL

    1. IPC:Inter-Process Communication,即跨程序通訊
    2. AIDL:Android Interface Definition Language,即Android介面定義語言;用於讓某個Service與多個應用程式元件之間進行跨程序通訊,從而可以實現多個應用程式共享同一個Service的功能。
  • 在多程序通訊中,存在兩個程序角色(以最簡單的為例):伺服器端和客戶端

  • 以下是兩個程序角色的具體使用步驟:
    伺服器端(Service)
    步驟1:新建定義AIDL檔案,並宣告該服務需要向客戶端提供的介面
    步驟2:在Service子類中實現AIDL中定義的介面方法,並定義生命週期的方法(onCreat、onBind()、blabla)
    步驟3:在AndroidMainfest.xml中註冊服務 & 宣告為遠端服務

    客戶端(Client)
    步驟1:拷貝服務端的AIDL檔案到目錄下
    步驟2:使用Stub.asInterface介面獲取伺服器的Binder,根據需要呼叫服務提供的介面方法
    步驟3:通過Intent指定服務端的服務名稱和所在包,繫結遠端Service

接下來,我將用一個具體例項來介紹遠端Service的使用

4. 具體例項

  • 例項描述:客戶端遠端呼叫伺服器端的遠端Service
  • 具體使用:

4.1 伺服器端(Service)

新建一個伺服器端的工程:Service - server

步驟1. 新建一個AIDL檔案

新建AIDL檔案

步驟2. 在新建AIDL檔案裡定義Service需要與Activity進行通訊的內容(方法),並進行編譯(Make Project)

// 在新建的AIDL_Service1.aidl裡宣告需要與Activity進行通訊的方法
package scut.carson_ho.demo_service;

interface AIDL_Service1 {
    void AIDL_Service();
}
//AIDL中支援以下的資料型別
//1. 基本資料型別
//2. String 和CharSequence
//3. List 和 Map ,List和Map 物件的元素必須是AIDL支援的資料型別;
//4. AIDL自動生成的介面(需要匯入-import)
//5. 實現android.os.Parcelable 介面的類(需要匯入-import)

編譯

步驟3:在Service子類中實現AIDL中定義的介面方法,並定義生命週期的方法(onCreat、onBind()、blabla)
MyService.java

public class MyService extends Service {

    // 例項化AIDL的Stub類(Binder的子類)
    AIDL_Service1.Stub mBinder = new AIDL_Service1.Stub() {

        //重寫接口裡定義的方法
        @Override
        public void AIDL_Service() throws RemoteException {
            System.out.println("客戶端通過AIDL與遠端後臺成功通訊");
        }
    };

//重寫與Service生命週期的相關方法
    @Override
    public void onCreate() {
        super.onCreate();

        System.out.println("執行了onCreat()");

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("執行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("執行了onDestory()");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {

        System.out.println("執行了onBind()");
    //在onBind()返回繼承自Binder的Stub型別的Binder,非常重要
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("執行了onUnbind()");
        return super.onUnbind(intent);
    }

}

步驟4:在AndroidMainfest.xml中註冊服務 & 宣告為遠端服務

<service
            android:name=".MyService"
            android:process=":remote"  //將本地服務設定成遠端服務
            android:exported="true"      //設定可被其他程序呼叫
            >
            //該Service可以響應帶有scut.carson_ho.service_server.AIDL_Service1這個action的Intent。
            //此處Intent的action必須寫成“伺服器端包名.aidl檔名”
            <intent-filter>
                <action android:name="scut.carson_ho.service_server.AIDL_Service1"/>
            </intent-filter>

        </service>

至此,伺服器端(遠端Service)已經完成了。

4.2 客戶端(Client)

新建一個客戶端的工程:Service - Client

步驟1:將服務端的AIDL檔案所在的包複製到客戶端目錄下(Project/app/src/main),並進行編譯

注:記得要原封不動地複製!!什麼都不要改!

複製後的目錄

步驟2:在主佈局檔案定義“繫結服務”的按鈕
MainActivity.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="scut.carson_ho.service_client.MainActivity">

    <Button
        android:layout_centerInParent="true"
        android:id="@+id/bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="繫結服務"
        />
</RelativeLayout>

步驟3:在MainActivity.java裡

  • 使用Stub.asInterface介面獲取伺服器的Binder;
  • 通過Intent指定服務端的服務名稱和所在包,進行Service繫結;
  • 根據需要呼叫服務提供的介面方法。

MainActivity.java

public class MainActivity extends AppCompatActivity {

        private Button bindService;

        //定義aidl介面變數
        private AIDL_Service1 mAIDL_Service;

        //建立ServiceConnection的匿名類
        private ServiceConnection connection = new ServiceConnection() {

            //重寫onServiceConnected()方法和onServiceDisconnected()方法
            //在Activity與Service建立關聯和解除關聯的時候呼叫
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }

            //在Activity與Service建立關聯時呼叫
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {

                //使用AIDLService1.Stub.asInterface()方法獲取伺服器端返回的IBinder物件
                //將IBinder物件傳換成了mAIDL_Service介面物件
                mAIDL_Service = AIDL_Service1.Stub.asInterface(service);

                try {

                    //通過該物件呼叫在MyAIDLService.aidl檔案中定義的介面方法,從而實現跨程序通訊
                    mAIDL_Service.AIDL_Service();

                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        };


        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            bindService = (Button) findViewById(R.id.bind_service);

            //設定繫結服務的按鈕
            bindService.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    //通過Intent指定服務端的服務名稱和所在包,與遠端Service進行繫結
                    //引數與伺服器端的action要一致,即"伺服器包名.aidl介面檔名"
                    Intent intent = new Intent("scut.carson_ho.service_server.AIDL_Service1");

                    //Android5.0後無法只通過隱式Intent繫結遠端Service
                    //需要通過setPackage()方法指定包名
                    intent.setPackage("scut.carson_ho.service_server");

                    //繫結服務,傳入intent和ServiceConnection物件
                    bindService(intent, connection, Context.BIND_AUTO_CREATE);

                }
            });
        }

    }

4.3 測試結果

點選繫結服務按鈕

從上面測試結果可以看出:

  • 列印的語句分別執行在不同程序(看語句前面的包名);
  • 客戶端呼叫了服務端Service的方法

客戶端和服務端進行了跨程序通訊

4.4 Demo地址

5. 總結

請點贊!因為你們的贊同/鼓勵是我寫作的最大動力!

不定期分享關於安卓開發的乾貨,追求短、平、快,但卻不缺深度