標籤:
在Android系統中,進程間傳遞的資料包括Java語言支援的基礎資料型別 (Elementary Data Type)和使用者自訂的資料類型,為了使資料能夠穿越進程邊界,所有資料都必須是“可打包”。對於Java語言的基礎資料型別 (Elementary Data Type),打包過程是自動完成的。但對於自訂的資料類型,使用者需要實現Parcelable介面,使自訂的資料類型能夠轉換為系統級原語儲存在Parcel對象中,穿越進程邊界後可再轉換為初始格式。
AIDL支援的資料類型如下表:
| 類型 |
說明 |
需要引入 |
| 基礎資料型別 (Elementary Data Type) |
boolean、byte、short、int、 long、char、float、double |
否 |
| String |
java.lang.String |
否 |
| CharSequence |
java.lang.CharSequence |
否 |
| List |
其中元素都必須是AIDL支援的資料類型 |
否 |
| Map |
其中ket和value都必須是AIDL支援的資料類型 |
否 |
| 其他AIDL介面 |
任何其他使用AIDL語言產生的介面類型 |
是 |
| Parcelable對象 |
實現Parcelable介面的對象 |
是 |
下面以ParcelMathServiceDemo樣本為參考,說明如何在遠程服務中使用自訂類型。這個樣本是RemoteMathServiceDemo樣本的延續,查看我的RemoteMathServiceDemo樣本文章 ,也定義了MathService服務,同樣可以為遠程調用者提供加法服務,而且同樣也是沒有啟動介面。
不同之處在於MathService服務涉及到了自訂資料類型,在接受到輸入參數後,將不再只向調用者返回long類型的資料,而是返回一個包含“加、減、乘、除”全部運算結果的對象。這個對象是一個自訂的類,為了能夠使其他AIDL檔案可以使用這個自訂的類,需要使用AIDL語言聲明這個類。
首先建立AllResult.aidl檔案
然後在AllResult.aidl檔案中聲明AllResult類,AllResult.aidl檔案中的代碼如下:
// AllResult.aidlpackage com.example.remotemathservicedemo;//在這裡聲明任何非預設類型parcelable AllResult;
(這個簡陋的aidl檔案可不會自動產生對應的java介面檔案哦)
然後在IMathService.aidl中為全運算增加新函數,傳回型別就是在AllResult.aidl中定義的AllResult,代碼如下:
// IMathService.aidlpackage com.example.remotemathservicedemo;// Declare any non-default types here with import statementsinterface IMathService { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ long Add(long a,long b); AllResult ComputeAll(long a,long b);}
然後Build->Make Project 重建介面檔案,使增加的新函數生效。這時候在新產生的IMathService.java中會提示錯誤,因為其中需要關於AllResult的資訊都找不到。
接下來手動構造AllResult類,可以放在和自動產生的IMathService.java同目錄下。下面先把AllResult.java的完整代碼貼出來:
package com.example.remotemathservicedemo;import android.os.Parcel;import android.os.Parcelable;/** * Created by yinghao on 2016/5/7. */public class AllResult implements Parcelable { public long addResult; public long subResult; public long mulResult; public double divResult; public AllResult(long addResult, long subResult, long mulResult, double divResult) { this.addResult = addResult; this.subResult = subResult; this.mulResult = mulResult; this.divResult = divResult; } //從Parcel對象得到資料,拆包函數 public AllResult(Parcel parcel) { addResult = parcel.readLong(); subResult = parcel.readLong(); mulResult = parcel.readLong(); divResult = parcel.readDouble(); } @Override public int describeContents() { return 0; } //顧名思義 wiiteToParcel 打包函數 //將AllResult類內部的資料按照特定順序寫入Parcel對象 //寫入順序必須與建構函式讀取順序一致 @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(addResult); dest.writeLong(subResult); dest.writeLong(mulResult); dest.writeDouble(divResult); } //實現靜態公用欄位Creator,用來使用Parcel物件建構AllResult對象 public static final Parcelable.Creator<AllResult> CREATOR = new Creator<AllResult>() { @Override public AllResult createFromParcel(Parcel parcel) { return new AllResult(parcel); } @Override public AllResult[] newArray(int size) { return new AllResult[size]; } };}
AllResult繼承於Parcelable,其中的資料就是全運算的運算結果。
AllResult類除了基本的建構函式以外,還需要以Parcel對象為輸入的建構函式,並且要重載打包函數writeToParcel()。 把這個類寫完後,你就會發現IMathService.java中的錯誤提示消失了。
到這裡,關於自訂資料類型的工作就完成了,然後在MathService.java檔案中,增加用來進行全運算的ComputAll()函數,並將運算結果儲存在AllResult對象中。MathService.java中的完整代碼如下:
package com.example.remotemathservicedemo;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import android.support.annotation.Nullable;import android.widget.Toast;/** * Created by yinghao on 2016/5/7. */public class MathService extends Service { /* 1. 建立 IMathService.Stub的執行個體mBinder並實現AIDL檔案定義的遠程服務介面 2. 在onBind()方法中將mBinder返回給遠程調用者 */ private final IMathService.Stub mBinder = new IMathService.Stub(){ @Override public long Add(long a, long b) throws RemoteException { return a + b; } @Override public AllResult ComputeAll(long a, long b) throws RemoteException { long addResult = a + b; long subResult = a - b; long mulResult = a * b; double divResult = a / b; AllResult allResult = new AllResult(addResult, subResult, mulResult, divResult); return allResult; } }; @Nullable @Override public IBinder onBind(Intent intent) { Toast.makeText(MathService.this, "遠程綁定:MathService", Toast.LENGTH_SHORT).show(); return mBinder; } //Return true if you would like to have the service‘s onRebind method later called when new clients bind to it. @Override public boolean onUnbind(Intent intent) { Toast.makeText(MathService.this, "取消遠程綁定", Toast.LENGTH_SHORT).show(); return false; }}
到這裡,服務端的工作就全部完成了,當然如果你沒看過我的上篇關於遠程服務傳遞基礎資料型別 (Elementary Data Type)的話,那還缺少一步,註冊Service。
然後就是調用者如何去綁定和調用服務,首先需要將服務端的Module中的兩個aidl檔案以及對應的java介面檔案和自己構造的AllResult.java類全部拷貝到調用端Module中。(關於原因可在上篇文章中查看)
然後將原來的“加法運算”功能改為“全運算”功能,關於remoteMathCallerDemo中MainActivity.java中的完整代碼如下:
package com.example.remotemathcallerdemo;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Button;import android.widget.TextView;import com.example.remotemathservicedemo.AllResult;import com.example.remotemathservicedemo.IMathService;public class MainActivity extends AppCompatActivity { private TextView textView; private Button bind; private Button unbind; private Button add; private boolean isBound = false; private IMathService mathService; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mathService = IMathService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { mathService = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.textView); bind = (Button) findViewById(R.id.bind); unbind = (Button) findViewById(R.id.unbind); add = (Button) findViewById(R.id.add); bind.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!isBound) { final Intent serviceIntent = new Intent(); serviceIntent.setAction("com.example.remote.MathService"); bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE); isBound = true; } } }); unbind.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (isBound) { unbindService(mConnection); isBound = false; mathService = null; } } }); add.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mathService == null) { textView.setText("未綁定遠程服務"); return; } long a = Math.round(Math.random() * 100); long b = Math.round(Math.random() * 100); AllResult result = null; try { result = mathService.ComputeAll(a, b); } catch (RemoteException e) { e.printStackTrace(); } String msg = ""; if (result != null) { msg += String.valueOf(a) + "+" + String.valueOf(b) + "=" + String.valueOf(result.addResult) + "\n"; msg += String.valueOf(a) + "-" + String.valueOf(b) + "=" + String.valueOf(result.subResult) + "\n"; msg += String.valueOf(a) + "*" + String.valueOf(b) + "=" + String.valueOf(result.mulResult) + "\n"; msg += String.valueOf(a) + "/" + String.valueOf(b) + "=" + String.valueOf(result.divResult) + "\n"; } textView.setText(msg); } }); }}
運行效果如下:
對於遠程服務的一些基礎知識總結以及遠程服務傳遞基礎資料型別 (Elementary Data Type)的Demo在Android Service 遠程服務(點擊查看),歡迎大家指出差錯或交流。
android 遠程服務傳遞自訂資料類型