Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 深入解讀Android的內部進程通信接口AIDL

深入解讀Android的內部進程通信接口AIDL

編輯:關於Android編程

意義:

由於每個應用進程都有自己的獨立進程空間,在android平台上,一個進程通常不能訪問另一個進程的內存空間,而我們經常需要誇進程傳遞對象,就需要把對象分解成操作對象可以理解的基本單元,並且有序的通過進程邊界。

定義:

AIDL(Android Interface Definition Language)是一種IDL語言,用於生成可以在Android設備上兩個進程之間進行進程間通信(interprocess communication, IPC)的代碼。如果在一個進程中(例如Activity)要調用另一個進程中(例如Service)對象的操作,就可以使用AIDL生成可序列化的參數。

說明以及實現流程:

AIDL接口和普通的java接口沒有什麼區別,只是擴展名為.aidl,保存在src目錄下,如果其他應用程序需要IPC,則也需要在src目錄下創建同樣的AIDL文件,創建完畢之後,通過ADT工具,會在工程的gen目錄下生成相對應的.java文件。

一般實現兩個進程之間的通信需要實現下面幾個步驟

(1)在Eclipse的android工程目錄下面創建一個.aidl擴展名的文件,語法和java定義接口的語法差不多,不過需要自己手動import對應的包名。(比如需要用到list集合,則需要import java.util.List;)

(2)如果aidl文件符合規范,ADT工具會幫助編譯器在gen目錄下生成相對應的.java文件。

(3)需要繼承實現一個服務類,跨進程調用的基礎。

(4)在service端實現AIDL接口,如果有回調則在client端實現callback的AIDL接口。

(5)在AndroidManifest.xml注冊service。

注意:

實現AIDL,我們需要注意以下五點

(1)AIDL只支持接口方法,不能公開static變量。

(2)AIDL接口方法如果有參數,則需要注意in、out、inout的使用規則,對於基本數據類型,默認是in類型,可以不需要添加聲明,非基本可變對象需要在變量名之前添加方法類型

in表示輸入參數,調用者把值傳遞給使用者使用。

out表示輸出參數,調用者把容器傳遞給使用者填充,然後自己使用處理。

inout標書輸入輸出參數,傳送相應的值並接收返回。

列舉一個out的使用例子:
服務端傳參數給客戶端,客戶端填充,服務端調用完之後,可以讀取到客戶端填寫的內容,具體的例子後面將給出。

(3)AIDL定義的接口名必須和文件名一致。

(4)oneway表示用戶請求相應功能時不需要等待響應可直接調用返回,非阻塞效果,該關鍵字可以用來聲明接口或者聲明方法,如果接口聲明中用到了oneway關鍵字,則該接口聲明的所有方法都采用oneway方式。

(5)AIDL傳遞非基本可變長度變量(非final對象),需要實現parcelable接口。
 parcel一般都用在Binder通信,通過read和write方法進行客戶端與服務端的數據傳遞(通信)。
比如:frameworks層服務端與hardware客戶端的Binder通信

reply->writeInt32(getCardReaderSize());
int mid = data.readInt32();

用來存放parcel數據的是內存(RAM),而不是永遠介質(Nand等)。

parcelable定義了把數據寫入parcel和從parcel讀出數據的接口,一個類的實例,如果需要封裝到消息中去,就必須實現這一接口,如果實現了這個接口,該類的實例就是可以“被打包”。
Parcelabel 的實現,需要在類中添加一個靜態成員變量 CREATOR,這個變量需要繼承 Parcelable.Creator 接口。

package com.zlc.provider;
 
import android.os.Parcel;
import android.os.Parcelable;
 
public class Students implements Parcelable{
  private int stu_id;
  private String stu_name;
  public Students(Parcel source){
    stu_id = source.readInt();
    stu_name = source.readString();
  }
  public int getStu_id() {
    return stu_id;
  }
  public void setStu_id(int stu_id) {
    this.stu_id = stu_id;
  }
  public String getStu_name() {
    return stu_name;
  }
  public void setStu_name(String stu_name) {
    this.stu_name = stu_name;
  }
  @Override
  public int describeContents() {
    // TODO Auto-generated method stub
    return 0;
  }
  @Override
  public void writeToParcel(Parcel dest, int flags) {
    // TODO Auto-generated method stub
    dest.writeInt(stu_id);
    dest.writeString(stu_name);
  }
  //Interface that must be implemented and provided as a public CREATOR field that generates instances of your Parcelable class from a Parcel. 
  public final static Parcelable.Creator<Students> CREATOR = new Parcelable.Creator<Students>() {
 
    @Override
    public Students createFromParcel(Parcel source) {
      // TODO Auto-generated method stub
      return new Students(source);
    }
 
    @Override
    public Students[] newArray(int size) {
      // TODO Auto-generated method stub
      return new Students[size];
    }
  };
}


實例:

下面列舉一個例子,主要實現客戶端調用服務端然後回調回來,具體實現功能改變客戶端的文字和圖片顯示,這個例子暫時效果是圖片的更改直接使用客戶端已經准備好的圖片,接下來幾篇博客會基於這個功能完善,到達服務端可以發送文字、圖片、文件句柄(I/O流),並且直接由服務端通過方法名稱直接調用客戶端方法,客戶端只需要注冊對應的view並且提供相應的方法給服務端使用,後面的兩部的完善主要用到反射和重寫MemoryFile(達到parcelable序列化效果)來實現。

(1)首先按照我們上面的步驟需要創建aidl文件,分別創建調用和回調的aidl文件,為了闡述更詳細一些,小編把parcelable對象也添加進去,僅僅作為測試。
IMyAidlService.aidl主要由服務端實現客戶端調用  

package com.zlc.aidl;
import com.zlc.aidl.DemoParcelable;
import com.zlc.aidl.AIDLCallback;
interface IMyAidlService{
  void registerClient(AIDLCallback cb);//注冊回調
  void saveDemoInfo(in DemoParcelable demo);//實際調用方法
}

AIDLCallback.aidl主要由客戶端實現,服務端調用

package com.zlc.aidl;
import com.zlc.aidl.DemoParcelable;
import java.util.List;
interface AIDLCallback {
  int returnResult(out List<DemoParcelable> list,int a);//回調給客戶端
  void testMethod(out Bundle params);//用來測試參數in/out的使用
}

DemoParcelable.aidl聲明傳遞對象:

package com.zlc.aidl;
parcelable DemoParcelable;

補充一點:out和in參數區別其實很明顯我們直接查看adt生成在gen目錄下對應的java文件就可以看出區別:當是out參數的時候是執行完之後從parcel對象讀取值,而in參數時是寫到parcel對象裡面傳過去。
我們看下當testMethod分別是out和in修飾時生成的文件
當時out的時候是從parcel對象裡面讀數據

mRemote.transact(Stub.TRANSACTION_testMethod, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
params.readFromParcel(_reply);
}

當時in的時候是從parcel對象裡面取數據

if ((params!=null)) {
_data.writeInt(1);
params.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_testMethod, _data, _reply, 0);
_reply.readException();

(2)實現一個服務類用來實現進程之間通信MyAidlService.java,貼出部分代碼,詳細代碼會在後面上傳。

@Override
  public IBinder onBind(Intent intent) {
    // TODO Auto-generated method stub
    Log.d(TAG, "MyAidlService onBind");
    return mBinder;
  }
 
  private final IMyAidlService.Stub mBinder = new IMyAidlService.Stub() {
    private AIDLCallback cb;
 
    @Override
    public void saveDemoInfo(DemoParcelable demo) throws RemoteException {
      if (demo != null) {
        if ("meinv1".equals(demo.getDemo_name())) {
          demo.setDemo_name("meinv2");
        }
        list.add(demo);
        Log.d(TAG, "saveDemoInfo list.size = " + list.size() + " list = " + list);
        cb.returnResult(list, 5);
        Bundle params = new Bundle();
        cb.testMethod(params);
        int width = params.getInt("width", 0);
        int height = params.getInt("height", 0);
        Log.d(TAG, "width = " + width + " height = "+height);
      }
    }
 
    @Override
    public void registerClient(AIDLCallback cb) throws RemoteException {
      cb.asBinder().linkToDeath(new DeathRecipient() {
        @Override
        public void binderDied() {
          try {
            Log.i(TAG, "[ServiceAIDLImpl]binderDied.");
          } catch (Throwable e) {
          }
        }
      }, 0);
    }
  };

   
(3)實現客戶端連接並且實現callback方法 

private ServiceConnection mRemoteConnection = new ServiceConnection() {
 
    @Override
    public void onServiceDisconnected(ComponentName name) {
      // TODO Auto-generated method stub
      Log.d(TAG, "onServiceDisconnected");
    }
 
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      // TODO Auto-generated method stub
      Log.d(TAG, "onServiceConnected");
      mRemoteService = (IMyAidlService) IMyAidlService.Stub
          .asInterface(service);
      if(mRemoteService != null)
        Log.d(TAG, "onServiceConnected success");
    }
  };
……
btn.setOnClickListener(new OnClickListener() {
 
      @Override
      public void onClick(View v) {
        // TODO Auto-generated method stub
        String actionName = "com.zlc.aidl.server.MyAidlService";
        Intent intent = new Intent(actionName);
        boolean ret = bindService(intent, mRemoteConnection,
            Context.BIND_AUTO_CREATE);
        Log.d(TAG, " ret ?=" + ret);
        if (ret) {
          new Thread(new Runnable() {
 
            @Override
            public void run() {
              // TODO Auto-generated method stub
              try {
                DemoParcelable demo = new DemoParcelable();
                List<String> list = new ArrayList<String>();
                list.add("like dance");
                demo.setDemo_id((Integer) img.getTag());
                demo.setDemo_name("meinv1");
                demo.setDemo_list(list);
                mRemoteService.registerClient(callback);
                mRemoteService.saveDemoInfo(demo);
              } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
              }
            }
          }).start();
 
        }
      }
    });
  }
……
private final AIDLCallback callback = new AIDLCallback.Stub() {
     
 
    @Override
    public int returnResult(List<DemoParcelable> list, int a)
        throws RemoteException {
      if (list != null)
        Log.d(TAG, "list.size = " + list.size()+"  a="+a);
      for (DemoParcelable demoParcelable : list) {
        doFresh(demoParcelable);
      }
      return 0;
    }
 
    @Override
    public void testMethod(Bundle outParams) throws RemoteException {
      // TODO Auto-generated method stub
      if (outParams != null) {
        outParams.putInt("width", 11);
        outParams.putInt("height", 12);
      }
 
    }
  };

   
(4)在androidManifest.xml裡面注冊service服務。   
注意一點:android:process=":remote",代表在應用程序裡,當需要該service時,會自動創建新的進程。而如果是android:process="remote",沒有“:”分號的,則創建全局進程,不同的應用程序共享該進程。
通過ps直接看pid進程號就可以看出。
讓應用的組件在一個單獨的進程中運行,如果帶冒號: ,則創建一個專屬於當前進程的進程,如果不帶冒號,需要使用標准的命名規范命名進程名,例如com.xxx.xxx.xxx,而且該進程是全局共享的進程,即不同應用的組件都可以運行於該進程。
這可以突破應用程序的24M(或16M)內存限制。
總之,使用帶:remote的屬性的進程id pid不同,父進程ID PPID是一樣的。而使用不帶冒號的remote則會創建兩個完全獨立的進程。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved