国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > Android > 正文

深入解讀Android的內(nèi)部進(jìn)程通信接口AIDL

2019-12-12 06:27:51
字體:
供稿:網(wǎng)友

意義:

由于每個(gè)應(yīng)用進(jìn)程都有自己的獨(dú)立進(jìn)程空間,在android平臺(tái)上,一個(gè)進(jìn)程通常不能訪問另一個(gè)進(jìn)程的內(nèi)存空間,而我們經(jīng)常需要夸進(jìn)程傳遞對(duì)象,就需要把對(duì)象分解成操作對(duì)象可以理解的基本單元,并且有序的通過進(jìn)程邊界。

定義:

AIDL(Android Interface Definition Language)是一種IDL語(yǔ)言,用于生成可以在Android設(shè)備上兩個(gè)進(jìn)程之間進(jìn)行進(jìn)程間通信(interprocess communication, IPC)的代碼。如果在一個(gè)進(jìn)程中(例如Activity)要調(diào)用另一個(gè)進(jìn)程中(例如Service)對(duì)象的操作,就可以使用AIDL生成可序列化的參數(shù)。

說明以及實(shí)現(xiàn)流程:

AIDL接口和普通的java接口沒有什么區(qū)別,只是擴(kuò)展名為.aidl,保存在src目錄下,如果其他應(yīng)用程序需要IPC,則也需要在src目錄下創(chuàng)建同樣的AIDL文件,創(chuàng)建完畢之后,通過ADT工具,會(huì)在工程的gen目錄下生成相對(duì)應(yīng)的.java文件。

一般實(shí)現(xiàn)兩個(gè)進(jìn)程之間的通信需要實(shí)現(xiàn)下面幾個(gè)步驟

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

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

(3)需要繼承實(shí)現(xiàn)一個(gè)服務(wù)類,跨進(jìn)程調(diào)用的基礎(chǔ)。

(4)在service端實(shí)現(xiàn)AIDL接口,如果有回調(diào)則在client端實(shí)現(xiàn)callback的AIDL接口。

(5)在AndroidManifest.xml注冊(cè)service。

注意:

實(shí)現(xiàn)AIDL,我們需要注意以下五點(diǎn)

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

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

in表示輸入?yún)?shù),調(diào)用者把值傳遞給使用者使用。

out表示輸出參數(shù),調(diào)用者把容器傳遞給使用者填充,然后自己使用處理。

inout標(biāo)書輸入輸出參數(shù),傳送相應(yīng)的值并接收返回。

列舉一個(gè)out的使用例子:
服務(wù)端傳參數(shù)給客戶端,客戶端填充,服務(wù)端調(diào)用完之后,可以讀取到客戶端填寫的內(nèi)容,具體的例子后面將給出。

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

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

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

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

用來存放parcel數(shù)據(jù)的是內(nèi)存(RAM),而不是永遠(yuǎn)介質(zhì)(Nand等)。

parcelable定義了把數(shù)據(jù)寫入parcel和從parcel讀出數(shù)據(jù)的接口,一個(gè)類的實(shí)例,如果需要封裝到消息中去,就必須實(shí)現(xiàn)這一接口,如果實(shí)現(xiàn)了這個(gè)接口,該類的實(shí)例就是可以“被打包”。
Parcelabel 的實(shí)現(xiàn),需要在類中添加一個(gè)靜態(tài)成員變量 CREATOR,這個(gè)變量需要繼承 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];    }  };}


實(shí)例:

下面列舉一個(gè)例子,主要實(shí)現(xiàn)客戶端調(diào)用服務(wù)端然后回調(diào)回來,具體實(shí)現(xiàn)功能改變客戶端的文字和圖片顯示,這個(gè)例子暫時(shí)效果是圖片的更改直接使用客戶端已經(jīng)準(zhǔn)備好的圖片,接下來幾篇博客會(huì)基于這個(gè)功能完善,到達(dá)服務(wù)端可以發(fā)送文字、圖片、文件句柄(I/O流),并且直接由服務(wù)端通過方法名稱直接調(diào)用客戶端方法,客戶端只需要注冊(cè)對(duì)應(yīng)的view并且提供相應(yīng)的方法給服務(wù)端使用,后面的兩部的完善主要用到反射和重寫MemoryFile(達(dá)到parcelable序列化效果)來實(shí)現(xiàn)。

(1)首先按照我們上面的步驟需要?jiǎng)?chuàng)建aidl文件,分別創(chuàng)建調(diào)用和回調(diào)的aidl文件,為了闡述更詳細(xì)一些,小編把parcelable對(duì)象也添加進(jìn)去,僅僅作為測(cè)試。
IMyAidlService.aidl主要由服務(wù)端實(shí)現(xiàn)客戶端調(diào)用  

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

AIDLCallback.aidl主要由客戶端實(shí)現(xiàn),服務(wù)端調(diào)用

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

DemoParcelable.aidl聲明傳遞對(duì)象:

package com.zlc.aidl;parcelable DemoParcelable;

補(bǔ)充一點(diǎn):out和in參數(shù)區(qū)別其實(shí)很明顯我們直接查看adt生成在gen目錄下對(duì)應(yīng)的java文件就可以看出區(qū)別:當(dāng)是out參數(shù)的時(shí)候是執(zhí)行完之后從parcel對(duì)象讀取值,而in參數(shù)時(shí)是寫到parcel對(duì)象里面?zhèn)鬟^去。
我們看下當(dāng)testMethod分別是out和in修飾時(shí)生成的文件
當(dāng)時(shí)out的時(shí)候是從parcel對(duì)象里面讀數(shù)據(jù)

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

當(dāng)時(shí)in的時(shí)候是從parcel對(duì)象里面取數(shù)據(jù)

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)實(shí)現(xiàn)一個(gè)服務(wù)類用來實(shí)現(xiàn)進(jìn)程之間通信MyAidlService.java,貼出部分代碼,詳細(xì)代碼會(huì)在后面上傳。

@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)實(shí)現(xiàn)客戶端連接并且實(shí)現(xiàn)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里面注冊(cè)service服務(wù)。   
注意一點(diǎn):android:process=":remote",代表在應(yīng)用程序里,當(dāng)需要該service時(shí),會(huì)自動(dòng)創(chuàng)建新的進(jìn)程。而如果是android:process="remote",沒有“:”分號(hào)的,則創(chuàng)建全局進(jìn)程,不同的應(yīng)用程序共享該進(jìn)程。
通過ps直接看pid進(jìn)程號(hào)就可以看出。
讓應(yīng)用的組件在一個(gè)單獨(dú)的進(jìn)程中運(yùn)行,如果帶冒號(hào): ,則創(chuàng)建一個(gè)專屬于當(dāng)前進(jìn)程的進(jìn)程,如果不帶冒號(hào),需要使用標(biāo)準(zhǔn)的命名規(guī)范命名進(jìn)程名,例如com.xxx.xxx.xxx,而且該進(jìn)程是全局共享的進(jìn)程,即不同應(yīng)用的組件都可以運(yùn)行于該進(jìn)程。
這可以突破應(yīng)用程序的24M(或16M)內(nèi)存限制。
總之,使用帶:remote的屬性的進(jìn)程id pid不同,父進(jìn)程ID PPID是一樣的。而使用不帶冒號(hào)的remote則會(huì)創(chuàng)建兩個(gè)完全獨(dú)立的進(jìn)程。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 治县。| 滨州市| 钟祥市| 象州县| 石嘴山市| 长汀县| 睢宁县| 扬州市| 凤庆县| 英吉沙县| 安乡县| 胶南市| 宜川县| 吴旗县| 秭归县| 乐安县| 东山县| 章丘市| 台东市| 广安市| 汾西县| 金阳县| 高要市| 哈尔滨市| 朔州市| 柞水县| 慈溪市| 吴川市| 颍上县| 保定市| 读书| 长沙市| 焦作市| 八宿县| 施甸县| 孟津县| 康马县| 定远县| 新沂市| 开鲁县| 临武县|