1. 程式人生 > 其它 >Android:遠端 Service 和 AIDL 跨程序通訊

Android:遠端 Service 和 AIDL 跨程序通訊

目錄

遠端 Service

Service 是執行在主執行緒裡的,如果直接在 Service 中處理一些耗時的邏輯,就會導致程式 ANR。可以在 Service 中開啟執行緒去執行耗時任務,也可以索性將原來的 Service 轉換成一個遠端 Service
將 Service 轉換成遠端 Service 只需要在註冊 Service 的時候,在 AndroidManifest.xml 檔案中將 android:process 屬性指定成“:remote”。

<service android:name=".MyService"
    android:process=":remote">
</service>

AIDL 跨程序通訊

此時 Bind Service 讓 Activity 和 Service 建立關聯時,由於 Activity 和 Service 執行在兩個不同的程序當中,不能再使用傳統方式建立關聯。
可以使用 AIDL(Android Interface Definition Language)Android 介面定義語言來進行跨程序通訊(IPC),它可以讓某個 Service 與多個應用程式元件之間進行跨程序通訊,幫助 IPC 之間介面的建立,從而實現多個應用程式共享同一個 Service 的功能。使用的步驟如下:

  1. 定義 AIDL 介面,為 Service 建立介面 IService;
  2. Client 連線 Service 獲得 Stub 物件;
  3. 在 IService 中具體實現 IService.Stub;
  4. 直接呼叫 IService.Stub 裡面的方法。


客戶端呼叫遠端 Service 時,Android 不是直接返回 Service 物件到客戶端,而是通過 onBind() 方法把 Service 的代理物件 IBinder 傳送給客戶端,所以 AIDL 遠端介面的實現類就是 IBinder 的實現類。

遠端 Service 樣例

程式需求

基於 Service 元件技術,程式設計實現一個可以完成計算任意的兩數的加、減、乘運算的遠端服務。

功能設計

首先需要新建一個 aidl,裡面寫一個介面作為我們要提供的服務,接著寫一個該介面的實現類。最後完成 Activity,實現對遠端服務的繫結,並且測試提供的加減乘的操作。

程式碼編寫

IMyAidlInterface.aidl

// IMyAidlInterface.aidl
package com.example.test5;

// Declare any non-default types here with import statements
interface IMyAidlInterface {
    double add(double a, double b);
    double subtract(double a, double b);
    double multiply(double a, double b);
}

注意我們是把介面寫在 .aidl 檔案中,此時如果是 eclipse 可以自動生成 IService 檔案,但是如果是用 Android studio 的話就需要寫完程式碼後建立一下:

MyService.java

package com.example.test5;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class MyService extends Service {

    //定義內部類MyServiceImpl繼承AIDL檔案自動生成的內部類,並且實現定義的介面方法
    private class MyServiceImpl extends IMyAidlInterface.Stub{
        @Override
        public double add(double a, double b) throws RemoteException {
            // TODO Auto-generated method stub
            return a + b;
        }
        @Override
        public double subtract(double a, double b) throws RemoteException {
            // TODO Auto-generated method stub
            return a - b;
        }
        @Override
        public double multiply(double a, double b) throws RemoteException {
            // TODO Auto-generated method stub
            return a * b;
        }
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return new MyServiceImpl();    //返回AIDL實現
    }

    @Override
    public void onDestroy(){
        super.onDestroy();
    }
}

MainActivity

注意不能在沒有繫結服務的時候解綁服務,否則呼叫方法會導致系統崩潰。此時應該設定一個訊號量來確定狀態,只有當服務綁定了才可以解綁。

package com.example.test5;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity {

    static final String TAG = "IMyAidlInterface";
    IMyAidlInterface iService = null;
    EditText num1Text, num2Text;
    TextView textView;
    double num1, num2;
    boolean flag = false;    //訊號量,防止在沒有繫結服務的情況下解綁

    private ServiceConnection conn = new ServiceConnection(){
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 返回AIDL介面物件,然後可以呼叫AIDL方法
            iService = IMyAidlInterface.Stub.asInterface(service);
            textView.setText("繫結 Service 成功!");
            flag = true;    //狀態設定為已繫結服務
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "釋放Service");
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //定位xml中的2個運算元和輸出文字的位置
        num1Text = (EditText) findViewById(R.id.etnum1);
        num2Text = (EditText) findViewById(R.id.etnum2);
        textView = (TextView) findViewById(R.id.textView);
    }

    public void doClick(View v) {
        double result = 0.0;    //運算結果
        switch (v.getId()) {
            // 繫結服務
            case R.id.button1:
                if(!flag){
                    Intent bindIntent = new Intent(this, MyService.class);
                    startService(bindIntent);
                    bindService(bindIntent, conn, BIND_AUTO_CREATE);
                }
                break;

             // 解綁服務
            case R.id.button5:
                if(flag) {
                    unbindService(conn);
                    textView.setText("解除繫結 Service 成功!");
                    flag = false;
                }
                break;

            // 執行服務提供的操作
            default:
                if(getTwoNum()){
                    try {
                        switch (v.getId()) {
                            case R.id.button2:
                                result = iService.add(num1, num2);
                                break;
                            case R.id.button3:
                                result = iService.subtract(num1, num2);
                                break;
                            case R.id.button4:
                                result = iService.multiply(num1, num2);
                                break;
                        }
                    }
                    catch (RemoteException e) {
                        Log.e(TAG,"調用出錯!");
                        e.printStackTrace();
                    }
                    textView.setText("" + result);
                }
                else{
                    // 提示使用者正確輸入
                    textView.setText("非法輸入,請重新輸入!");
                }
                break;
        }
    }

    public boolean getTwoNum(){
        try{
            //獲取兩個運算元
            num1 = Double.parseDouble(num1Text.getText().toString());
            num2 = Double.parseDouble(num2Text.getText().toString());
        }catch (Exception e) {
            //資料非法或者沒輸入,返回false
            return false;
        }
        return true;
    }
}

Activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/tvnum1"
            android:textSize="20dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="運算元1:"
            android:layout_weight="1"/>

        <EditText
            android:id="@+id/etnum1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBaseline="@+id/tvhigh"
            android:layout_alignParentRight="true"
            android:hint="請輸入數字"
            android:gravity="center"
            android:layout_weight="2"/>

    </LinearLayout>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/tvnum2"
            android:textSize="20dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignRight="@+id/tvhigh"
            android:layout_below="@+id/tvhigh"
            android:text="運算元2:"
            android:layout_weight="1"/>

        <EditText
            android:id="@+id/etnum2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBaseline="@+id/tvweight"
            android:layout_alignLeft="@+id/ethigh"
            android:hint="請輸入數字"
            android:gravity="center"
            android:layout_weight="2"/>

    </LinearLayout>

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Bind Service"
        android:textSize="20dp"
        android:onClick="doClick" />

    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="add"
        android:textSize="20dp"
        android:onClick="doClick"/>

    <Button
        android:id="@+id/button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="subtract"
        android:textSize="20dp"
        android:onClick="doClick"/>

    <Button
        android:id="@+id/button4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="multiply"
        android:textSize="20dp"
        android:onClick="doClick"/>

    <Button
        android:id="@+id/button5"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Unbind Service"
        android:textSize="20dp"
        android:onClick="doClick"/>

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:gravity="center"
        android:text="result" />

</LinearLayout>

執行效果

繫結服務。

非法輸入。


加減乘操作。



解除繫結。

參考資料

《Android 移動應用開發》,楊誼 主編、喻德曠 副主編,人民郵電出版社