標籤:
前言
Android中誇處理序間通訊方式有很多種方式,比如:aidl,Messenger,檔案分享權限設定,廣播(BroadCast),ContentProvider,Socket(網路通訊)。每種方式都有自己的使用情境和優缺點,接下來幾篇部落格我們一一學習他們是怎麼使用的。這篇部落客要是介紹Android studio下實現aidl編程。
AIDL的使用步驟
aidl遠程調用傳遞的參數和傳回值支援Java的基本類型(int long booen char byte等)和String,List,Map等。當然也支援一個自訂對象的傳遞,不過此時就需要做一些特別處理了,後續會介紹aidl傳遞對象怎麼處理。
有關aidl的相關概念這裡就不詳細解釋了,我們來看看在Android Studio下怎麼來實現aidl編程的。
服務端產生aidl檔案
建立一個MyAidlDemoServer工程,然後在main目錄下右鍵建立一個aidl目錄,然後在該目錄下建立一個IMyAidlInterface.aidl檔案,代碼如下:
// IMyAidlInterface.aidlpackage com.example.xjp.aidla;interface IMyAidlInterface { int add(int arg1, int arg2);}
定義了一個IMyAidlInterface介面,介面裡面定義了一個add方法用於計算兩個數的和。然後Build當前工程,在app/build/generated/source/aidl/debug目錄下會產生一個與IMyAidlInterface.aidl檔案同樣包名的一個檔案,該檔案下面自動產生IMyAidlInterface檔案,該檔案裡面自動實現了一些方法用於遠程調用,IMyAidlInterface代碼如下:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\WorkPlace\\MyAidlDemoServer\\app\\src\\main\\aidl\\com\\example\\xjp\\aidla * \\IMyAidlInterface.aidl */package com.example.xjp.aidla;public interface IMyAidlInterface extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.xjp.aidla .IMyAidlInterface { private static final java.lang.String DESCRIPTOR = "com.example.xjp.aidla.IMyAidlInterface"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.xjp.aidla.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.example.xjp.aidla.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.xjp.aidla.IMyAidlInterface))) { return ((com.example.xjp.aidla.IMyAidlInterface) iin); } return new com.example.xjp.aidla.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_add: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); int _result = this.add(_arg0, _arg1); reply.writeNoException(); reply.writeInt(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.xjp.aidla.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 int add(int arg1, int arg2) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(arg1); _data.writeInt(arg2); mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public int add(int arg1, int arg2) throws android.os.RemoteException;}
以上代碼都是AS自動產生的,如此一來,開發人員更加容易來實現AIDL跨處理序間通訊。如有對以上代碼感興趣者,可自行學習。
編寫遠程服務
建立MyServer類實現遠程服務代碼如下:
public class MyServer extends Service { IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() { public int add(int arg1, int arg2) { return arg1 + arg2; } }; @Override public IBinder onBind(Intent intent) { return mStub; }}
服務代碼也很簡單,僅僅實現了IMyAidlInterface.Stub類中的 add方法,然後重寫了Service的onBind方法。
記得在AndroidManifest.xml中配置MyServer服務,代碼如下:
<?xml version="1.0" encoding="utf-8"?><manifest package="com.example.xjp.aidl" xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"> <service android:name="com.example.xjp.aidl.MyServer" android:process=":remote"> <intent-filter> <action android:name="com.xjp.myService"></action> </intent-filter> </service> </application></manifest>
以上代碼給當前service設定了 android:process屬性為“:remote”,這個屬性有兩種賦值,一是:”:remote”,一是:”remote”,區別在於字串前面帶有”:”,後面的字串不一定是remote,開發人員可以隨意設定。
兩者代表不同意思,帶有”:”的表示該服務所在的進程是私人的,即只要有用戶端去啟動該服務,系統就會建立一個新的進程來運行該服務。
不帶有”:”的表示該服務所在的進程是共用的,即當前系統不管有幾個用戶端去啟動該服務,系統中只有一個進程來運行該服務。
用戶端
用戶端代碼相對簡單些,建立MyAidlDemoCustomer工程,然後直接把服務端的aidl目錄直接拷貝到用戶端的main目錄下。這麼一來用戶端的aidl就無需編寫了,直接和服務端的一模一樣。包括路徑的包名等。雖然你可以自己在用戶端在寫一遍aidl代碼,為了不出錯,請直接將服務端的aidl代碼直接拷貝過來。
用戶端調用代碼如下
package com.example.xjp.myaidldemocustomer;import android.app.Activity;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.util.Log;import android.widget.ImageView;import android.widget.TextView;import com.example.xjp.aidla.IMyAidlInterface;public class MainActivity extends Activity { IMyAidlInterface mStub; TextView txt; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e("xjp", "the Connected====>" + System.currentTimeMillis()); mStub = IMyAidlInterface.Stub.asInterface(service); if (mStub == null) { Log.e("xjp", "the mStub is null"); } else { try { int value = mStub.add(1, 8); txt.setText(value + ""); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); txt = (TextView) findViewById(R.id.text); Intent intent = new Intent(); //android 5.0以後直設定action不能啟動相應的服務,需要設定packageName或者Component。 intent.setAction("com.xjp.myService"); intent.setComponent(new ComponentName("com.example.xjp.aidl", "com.example.xjp.aidl.MyServer")); //綁定服務 bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); Log.e("xjp", "the bindServer start..====>" + System.currentTimeMillis()); } @Override protected void onDestroy() { //解除綁定服務 unbindService(serviceConnection); super.onDestroy(); }}
如此一來,用戶端也寫好了。先安裝服務端,在安裝用戶端。進入用戶端之後實現了遠程調用Service。
AIDL跨進程傳遞Bitamp對象
以上是最簡單的 aidl跨進程傳遞java基本類型資料,最近在項目中遇到這麼個需求,跨進程傳遞bitmap圖片。當時看到這個需求就懵逼了,後來仔細分析其實是可以實現Android 利用Aidl實現跨進程傳遞 Bitmap對象。那麼該怎麼實現呢?
我們都知道Aidl支援跨進程傳遞 Java基礎資料型別 (Elementary Data Type)中包括byte這個類型,那麼思路是不是來了呢?我們可以在服務端將需要傳遞的圖片由bitmap轉換成 byte[]類型。代碼如下:
public byte[] getBitmap() { Bitmap bitmap = BitmapFactory.decodeResource(MyServer.this.getResources(), R.drawable.bg_top); ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 90, baos);//壓縮位元影像 return baos.toByteArray();//建立分配位元組數組 }
如此一來就將Bitmap對象轉換成byte[]類型了,現在aidl就可以跨進程傳遞byte[]類型資料。
在用戶端你只需要將遠程調用得到的byte[]類型轉換成Bitmap對象,然後顯示即可。用戶端代碼如下:
byte[] bytes = mStub.getBitmap();Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);img.setImageBitmap(bitmap);
是不是很簡單,其實Aidl傳遞圖片只是將Bitamp對象轉換成byte[]類型,然後由byte[]類型在轉換回Bitmap對象的一個過程。
- 注意:在親測的過程中發現,利用aidl傳遞Bitamp對象是有一定的限制的,當Bitmap達到一定大小時是Aidl跨進程傳遞會失敗。所以,利用Aidl跨進程可以傳遞較小的圖片,至於多大可以根據你需求測試以免出錯。這也是由於Aidl跨進程傳遞資料是有限的,資料過大就不適合利用aidl誇進程傳遞了,應該用Socket傳遞。因為Aidl跨處理序間通訊是一個即時的,同步的的一個過程,即Aidl的遠程調用方法不能被阻塞,因此不能傳遞大資料。
總結
這是一個在AS下最簡單的一個AIDL編程:
1.服務端建立一個aidl目錄,然後在該目錄下建立一個.aidl為尾碼的介面類,該類定義遠程調用的介面方法。
2.build編譯之後會在app/build/generated/source/aidl/debug目錄下會產生aidl遠程實作類別,該類是AS自動產生的。
3.在AndroidManifest.xml下配置Service的action和process屬性。
4.將服務端的aidl目錄拷貝到用戶端相應的目錄下,然後編寫用戶端調用代碼,AS下簡單的aidl編程就ok了。
5.利用Aidl實現跨進程傳遞較小的Bitmap對象。
Android studio 下的aidl編程實現Android的誇處理序間通訊