Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android應用開發入門(五十一)AIDL接口通過綁定Service在進程間傳遞復雜對象

Android應用開發入門(五十一)AIDL接口通過綁定Service在進程間傳遞復雜對象

編輯:Android開發實例

前言

  Android的AIDL不僅可以在綁定服務中傳遞一些Android規定的數據類型的數據,還可以傳遞一些復雜類型的數據。但是與傳遞系統允許的數據類型相比,復雜類型數據的傳遞要做更多的工作,本文就講解一下如何使用AIDL接口通過綁定服務在進程間傳遞數據。關於AIDL傳遞規定類型數據的內容,不了解的朋友可以看看:http://www.fengfly.com/plus/view-214084-1.html。

  本文的主要內容:

  1. AIDL傳遞復雜類型對象的特殊處理
  2. Parcelable與Parcel接口
  3. 實現Parcelable接口
  4. AIDL傳遞復雜類型對象Demo
  5. AIDL傳遞對象序列化過程詳解

 

AIDL傳遞復雜類型對象的特殊處理 

  前面已經介紹了通過AIDL接口在進程間傳遞系統允許的數據,如果需要傳遞一個復雜類型的對象,就沒那麼簡單了,需要額外做一些處理。如下:

  1. 定義數據接口的AIDL文件中,使用parcelable關鍵字,例如:parcelable Message;
  2. 在其數據實現類中實現Parcelable接口,並實現對應的方法。
  3. 在業務接口的AIDL文件中,使用import引入數據接口AIDL的包名。

  例如:Message.aidl

  1. parcelable Message;  

  例如:IGetMsg.aidl

  1. package com.example.aidlservicedemo.domain;  
  2.  
  3. // 這是兩個自定義類  
  4. import com.example.aidlservicedemo.domain.Message;  
  5. import com.example.aidlservicedemo.domain.User;  
  6.  
  7. interface IGetMsg{  
  8.     // 在AIDL接口中定義一個getMes方法  
  9.     List<Message> getMes(in User us);  

Parcelable與Parcel接口

  先來說說Android對象序列化,在Android中序列化對象主要有兩種方式,實現Serializable接口或是實現Parcelable接口。Serializable接口是JavaSE原生支持的,而Parcelable接口是Android所特有的,它的序列化和反序列化的效率均比Serializable接口高,而AIDL進行在進程間通信(IPC),就是需要實現這個Parcelable接口。

  Parcelable接口的作用:實現了Parcelable接口的實例,可以將自身的數據信息寫入一個Parcel對象,也可以從parcel中恢復到對象的狀態。而Parcel就是完成數據序列化寫入的載體。

  上面提到Parcel,再來聊聊Parcel是什麼?Android系統設計之初,定位就是針對內存受限的設備,因此對性能要求更好,所以系統中采用進程間通信(IPC)機制,必然要求性能更優良的序列化方式,所以Parcel就被設計出來了,其定位就是輕量級的高效的對象序列化機制與反序列化機制。如果讀一下Android的底層代碼,會發現Parcel是使用C++實現的,底層直接通過Parcel指針操作內存實現,所以它的才更高效。

  Parcel也提供了一系列的方法幫助寫入數據與讀取數據,這裡簡單介紹一下:

  • obtain():在池中獲取一個新的Parcel。
  • dataSize():得到當前Parcel對象的實際存儲空間。
  • dataPostion():獲取當前Parcel對象的偏移量。
  • setDataPosition():設置當前Parcel對象的偏移量。
  • recyle():清空、回收當前Parcel對象的內存。
  • writeXxx():向當前Parcel對象寫入數據,具有多種重載。
  • readXxx():從當前Parcel對象讀取數據,具有多種重載。

  簡單來說,Parcelable通過writeToParcel()方法,對復雜對象的數據寫入Parcel的方式進行對象序列化,然後在需要的時候,通過其內定義的靜態屬性CREATOR.createFromParcel()進行反序列化的操作。Parcelable對Parcel進行了包裝,其內部就是通過操作Parcel進行序列化與反序列化的。

  Parcelable與Parcel均定義在android.os包下,而這種機制不僅用於AIDL,還可以用於Intent傳遞數據等其他地方,這不是本篇博客的主題,以後用到再詳細介紹。

實現Parcelable接口

  定義好數據接口的AIDL文件後,需要定義一個數據實現類,實現Parcelable接口,並實現對應的方法,Parcelable有如下幾個必須要實現的抽象方法:

  • abstract int describeContents():返回一個位掩碼,表示一組特殊對象類型的Parcelable,一般返回0即可。
  • asbtract void writeToParcel(Parcel dest,int flags):實現對象的序列化,通過Parcel的一系列writeXxx()方法序列化對象。

  除了上面兩個方法,還需要在實現類中定義一個名為"CREATOR",類型為"Parcelable.Creator<T>"的泛型靜態屬性,它實現了對象的反序列化。它也有兩個必須實現的抽象方法:

  • abstract T createFromParcel(Parcel source):通過source對象,根據writeToParcel()方法序列化的數據,反序列化一個Parcelable對象。
  • abstract T[] newArray(int size):創建一個新的Parcelable對象的數組。

  例如:

  1. @Override 
  2.     public int describeContents() {  
  3.         return 0;  
  4.     }  
  5.       
  6.     @Override 
  7.     public void writeToParcel(Parcel dest, int flags) {  
  8.         Log.i("main", "服務端Message被序列化");  
  9.         dest.writeInt(id);  
  10.         dest.writeString(msgText);  
  11.         dest.writeString(fromName);  
  12.         dest.writeString(date);  
  13.     }  
  14.  
  15.     public static final Parcelable.Creator<Message> CREATOR = new Creator<Message>() {  
  16.  
  17.         @Override 
  18.         public Message[] newArray(int size) {  
  19.             return new Message[size];  
  20.         }  
  21.  
  22.         @Override 
  23.         public Message createFromParcel(Parcel source) {  
  24.             Log.i("main", "服務端Message被反序列化");  
  25.             return new Message(source.readInt(), source.readString(),  
  26.                     source.readString(), source.readString());  
  27.         }  
  28.     }; 

  從上面示例中可以看出,使用writeToParcel()方法進行序列化,通過CREATOR.createFromParcel進行反序列化,它們都傳遞一個Parcel類型的對象,這裡要注意的是兩個方法中Parcel對象的writeXxx()和readXxx()方法的順序必須一致,因為一般序列化數據是以鏈的形式序列化的,如果順序不對,反序列化的數據會出錯。

AIDL傳遞復雜類型對象Demo

   關鍵點已經講到, 下面通過一個簡單的Demo來演示AIDL傳遞復雜對象。

  AIDL接口:

  com.example.aidlservicedemo.domain.Message.aidl

parcelable Message; 

  com.example.aidlservicedemo.domain.Message.java

  1. package com.example.aidlservicedemo.domain;  
  2.  
  3. import android.os.Parcel;  
  4. import android.os.Parcelable;  
  5. import android.text.format.DateUtils;  
  6. import android.util.Log;  
  7.  
  8. public class Message implements Parcelable {  
  9.  
  10.     private int id;  
  11.     private String msgText;  
  12.     private String fromName;  
  13.     private String date;  
  14.  
  15.     public Message() {  
  16.         super();  
  17.  
  18.     }  
  19.  
  20.     public Message(int id, String msgText, String fromName, String date) {  
  21.         super();  
  22.         this.id = id;  
  23.         this.msgText = msgText;  
  24.         this.fromName = fromName;  
  25.         this.date = date;  
  26.     }  
  27.  
  28.     public int getId() {  
  29.         return id;  
  30.     }  
  31.  
  32.     public void setId(int id) {  
  33.         this.id = id;  
  34.     }  
  35.  
  36.     public String getMsgText() {  
  37.         return msgText;  
  38.     }  
  39.  
  40.     public void setMsgText(String msgText) {  
  41.         this.msgText = msgText;  
  42.     }  
  43.  
  44.     public String getFromName() {  
  45.         return fromName;  
  46.     }  
  47.  
  48.     public void setFromName(String fromName) {  
  49.         this.fromName = fromName;  
  50.     }  
  51.  
  52.     public String getDate() {  
  53.         return date;  
  54.     }  
  55.  
  56.     public void setDate(String date) {  
  57.         this.date = date;  
  58.     }  
  59.  
  60.     @Override 
  61.     public int describeContents() {  
  62.         return 0;  
  63.     }  
  64.  
  65.     @Override 
  66.     public String toString() {  
  67.         return "信息內容=" + msgText + ", 發件人=" 
  68.                 + fromName + ", 時間=" + date ;  
  69.     }  
  70.  
  71.     @Override 
  72.     public void writeToParcel(Parcel dest, int flags) {  
  73.         Log.i("main", "客戶端Message被序列化");  
  74.         dest.writeInt(id);  
  75.         dest.writeString(msgText);  
  76.         dest.writeString(fromName);  
  77.         dest.writeString(date);  
  78.     }  
  79.  
  80.     public static final Parcelable.Creator<Message> CREATOR = new Creator<Message>() {  
  81.  
  82.         @Override 
  83.         public Message[] newArray(int size) {  
  84.             return new Message[size];  
  85.         }  
  86.  
  87.         @Override 
  88.         public Message createFromParcel(Parcel source) {  
  89.             Log.i("main", "客戶端Message被反序列化");  
  90.             return new Message(source.readInt(), source.readString(),  
  91.                     source.readString(), source.readString());  
  92.         }  
  93.     };  
  94. }  
  95. Message.java 

 com.example.aidlservicedemo.domain.User.aidl

parcelable User;

 com.example.aidlservicedemo.domain.User.java

  1. package com.example.aidlservicedemo.domain;  
  2.  
  3. import android.os.Parcel;  
  4. import android.os.Parcelable;  
  5. import android.util.Log;  
  6.  
  7. public class User implements Parcelable {  
  8.       
  9.       
  10.     private int id;  
  11.     private String username;  
  12.     private String password;  
  13.     public User() {  
  14.         super();  
  15.     }  
  16.     public User(int id, String username, String password) {  
  17.         super();  
  18.         this.id = id;  
  19.         this.username = username;  
  20.         this.password = password;  
  21.     }  
  22.  
  23.     public int getId() {  
  24.         return id;  
  25.     }  
  26.  
  27.     public void setId(int id) {  
  28.         this.id = id;  
  29.     }  
  30.  
  31.     public String getUsername() {  
  32.         return username;  
  33.     }  
  34.  
  35.     public void setUsername(String username) {  
  36.         this.username = username;  
  37.     }  
  38.  
  39.     public String getPassword() {  
  40.         return password;  
  41.     }  
  42.  
  43.     public void setPassword(String password) {  
  44.         this.password = password;  
  45.     }  
  46.  
  47.     @Override 
  48.     public boolean equals(Object o) {  
  49.  
  50.         User us=(User)o;  
  51.         if(this.username.equals(us.username)&&this.password.equals(us.password))  
  52.         {  
  53.             return true;  
  54.         }  
  55.         else 
  56.         {  
  57.             return false;  
  58.         }  
  59.     }  
  60.       
  61.     @Override 
  62.     public int describeContents() {  
  63.         return 0;  
  64.     }  
  65.  
  66.     @Override 
  67.     public void writeToParcel(Parcel dest, int flags) {  
  68.         Log.i("main", "客戶端User被序列化");  
  69.         dest.writeInt(id);  
  70.         dest.writeString(username);  
  71.         dest.writeString(password);  
  72.     }  
  73.     public static final Parcelable.Creator<User> CREATOR=new Creator<User>() {  
  74.  
  75.         @Override 
  76.         public User[] newArray(int size) {  
  77.             return new User[size];  
  78.         }  
  79.  
  80.         @Override 
  81.         public User createFromParcel(Parcel source) {  
  82.             Log.i("main", "客戶端User被反序列化");  
  83.             return new User(source.readInt(), source.readString(),  
  84.                     source.readString());  
  85.         }  
  86.     };  
  87. }  
  88. User.java 

  服務:

  com.example.aidlservicedemo.

  1. package com.example.aidlservicedemo;  
  2.  
  3. import java.util.ArrayList;  
  4. import java.util.Date;  
  5. import java.util.HashMap;  
  6. import java.util.List;  
  7. import java.util.Map;  
  8.  
  9. import com.example.aidlservicedemo.domain.IGetMsg.Stub;  
  10. import com.example.aidlservicedemo.domain.Message;  
  11. import com.example.aidlservicedemo.domain.User;  
  12.  
  13. import android.app.Service;  
  14. import android.content.Intent;  
  15. import android.os.IBinder;  
  16. import android.os.RemoteException;  
  17. import android.util.Log;  
  18.  
  19. public class CustomTypeService extends Service {  
  20.     private static final String TAG = "main";  
  21.     private MsgBinder msgBinder=null;  
  22.     private static Map<User, List<Message>> map=new HashMap<User, List<Message>>();  
  23.     static{  
  24.         for(int i=0;i<3;i++){  
  25.             User user=new User(i, "jack"+i, "9999999999"+i);  
  26.             List<Message> messages=new ArrayList<Message>();  
  27.             Message msg=null;  
  28.             if(i==0)  
  29.             {  
  30.                 msg=new Message(i, "這兩天好嗎?", "Jerry", new Date().toGMTString());  
  31.                 messages.add(msg);  
  32.             }else if(i==1)  
  33.             {  
  34.                 msg=new Message(i, "周天去逛街吧!", "Tim", new Date().toGMTString());  
  35.                 messages.add(msg);  
  36.                 msg=new Message(i, "好無聊!", "Wesley", new Date().toGMTString());  
  37.                 messages.add(msg);  
  38.             }  
  39.             else 
  40.             {  
  41.                 msg=new Message(i, "上次的問題解決了嗎?", "Bonnie", new Date().toGMTString());  
  42.                 messages.add(msg);  
  43.                 msg=new Message(i, "明天一起吃飯吧?", "Matt", new Date().toGMTString());  
  44.                 messages.add(msg);  
  45.                 msg=new Message(i, "在哪裡?", "Caroline", new Date().toGMTString());  
  46.                 messages.add(msg);  
  47.             }  
  48.             map.put(user, messages);  
  49.         }  
  50.     }  
  51.       
  52.     public class MsgBinder extends Stub{  
  53.  
  54.         @Override 
  55.         public List<Message> getMes(User us) throws RemoteException {              
  56.             for(Map.Entry<User, List<Message>> msgs:map.entrySet()){  
  57.                 if(msgs.getKey().getUsername().equals(us.getUsername())&&msgs.getKey().getPassword().equals(us.getPassword())){  
  58.                     Log.i(TAG, "找到信息了");  
  59.                     return msgs.getValue();  
  60.                 }  
  61.             }  
  62.             Log.i(TAG, "沒找到信息了");  
  63.             return map.get(us);  
  64.         }  
  65.           
  66.     }  
  67.       
  68.     @Override 
  69.     public IBinder onBind(Intent intent) {  
  70.         // TODO Auto-generated method stub  
  71.         return msgBinder;  
  72.     }  
  73.  
  74.     @Override 
  75.     public void onCreate() {  
  76.         // TODO Auto-generated method stub  
  77.         super.onCreate();  
  78.         msgBinder=new MsgBinder();  
  79.     }  
  80.  
  81.     @Override 
  82.     public void onDestroy() {  
  83.         msgBinder=null;  
  84.         super.onDestroy();  
  85.     }  
  86.  
  87. }  
  88. CustomTypeService.java 

  客戶端:

  com.example.aidlClientdemo.

  1. package com.example.aidlClientdemo;  
  2.  
  3. import java.util.List;  
  4. import java.util.Random;  
  5.  
  6. import com.example.aidlservicedemo.domain.IGetMsg;  
  7. import com.example.aidlservicedemo.domain.Message;  
  8. import com.example.aidlservicedemo.domain.User;  
  9.  
  10. import android.app.Activity;  
  11. import android.content.ComponentName;  
  12. import android.content.Intent;  
  13. import android.content.ServiceConnection;  
  14. import android.os.Bundle;  
  15. import android.os.IBinder;  
  16. import android.os.RemoteException;  
  17. import android.view.View;  
  18. import android.view.View.OnClickListener;  
  19. import android.widget.Button;  
  20. import android.widget.Toast;  
  21.  
  22. public class CustomTypeActivity extends Activity {  
  23.     private Button btn_startService, btn_endService, btn_getServiceData;  
  24.     private IGetMsg getMsg;  
  25.  
  26.     private static User[] users = new User[] {  
  27.             new User(0, "jack0", "99999999990"),  
  28.             new User(0, "jack1", "99999999991"),  
  29.             new User(0, "jack2", "99999999992") };  
  30.  
  31.     private ServiceConnection conn = new ServiceConnection() {  
  32.  
  33.         @Override 
  34.         public void onServiceDisconnected(ComponentName name) {  
  35.             getMsg = null;  
  36.         }  
  37.  
  38.         @Override 
  39.         public void onServiceConnected(ComponentName name, IBinder service) {  
  40.             getMsg = IGetMsg.Stub.asInterface(service);  
  41.         }  
  42.     };  
  43.  
  44.     @Override 
  45.     protected void onCreate(Bundle savedInstanceState) {  
  46.         super.onCreate(savedInstanceState);  
  47.         setContentView(R.layout.activity_service);  
  48.  
  49.         btn_startService = (Button) findViewById(R.id.btn_startService);  
  50.         btn_endService = (Button) findViewById(R.id.btn_endService);  
  51.         btn_getServiceData = (Button) findViewById(R.id.btn_getServiceData);  
  52.         btn_startService.setOnClickListener(click);  
  53.         btn_endService.setOnClickListener(click);  
  54.         btn_getServiceData.setOnClickListener(click);  
  55.     }  
  56.  
  57.     private View.OnClickListener click = new OnClickListener() {  
  58.  
  59.         @Override 
  60.         public void onClick(View v) {  
  61.  
  62.             switch (v.getId()) {  
  63.             case R.id.btn_startService:  
  64.                 startService();  
  65.                 break;  
  66.             case R.id.btn_endService:  
  67.                 endService();  
  68.                 break;  
  69.             case R.id.btn_getServiceData:  
  70.                 getServiceDate();  
  71.                 break;  
  72.             }  
  73.  
  74.         }  
  75.     };  
  76.  
  77.     /**  
  78.      * 獲取其他線程服務數據  
  79.      */ 
  80.     private void getServiceDate(){  
  81.         try {  
  82.             Random random=new Random();          
  83.             int nextInt=random.nextInt(2);  
  84.             List<Message> msgs=getMsg.getMes(users[nextInt]);  
  85.             StringBuilder sBuilder=new StringBuilder();  
  86.             for(Message msg:msgs){  
  87.                 sBuilder.append(msg.toString()+"\n");  
  88.             }  
  89.             Toast.makeText(CustomTypeActivity.this, sBuilder.toString(), Toast.LENGTH_SHORT).show();  
  90.         } catch (Exception e) {  
  91.             Toast.makeText(CustomTypeActivity.this, "獲取數據出錯", Toast.LENGTH_SHORT).show();  
  92.             e.printStackTrace();  
  93.         }  
  94.     }  
  95.  
  96.     /**  
  97.      * 開始服務  
  98.      */ 
  99.     private void startService() {  
  100.         Intent service = new Intent();  
  101.         service.setAction("cn.bgxt.Service.CUSTOM_TYPE_SERVICE");  
  102.         bindService(service, conn, BIND_AUTO_CREATE);  
  103.         Toast.makeText(CustomTypeActivity.this, "綁定服務成功", Toast.LENGTH_SHORT).show();  
  104.     }  
  105.  
  106.     /**  
  107.      * 停止服務  
  108.      */ 
  109.     private void endService() {  
  110.         unbindService(conn);  
  111.         Toast.makeText(CustomTypeActivity.this, "解除綁定服務成功", Toast.LENGTH_SHORT).show();  
  112.     }  
  113.  
  114. }  
  115. CustomTypeActivity.java 

 效果展示:

AIDL傳遞對象序列化過程詳解

  通過上面Demo打印的日志,解釋一下序列化的過程,打開Logcat查看日志。

  從上圖的PID列可以看出這是兩個線程間的交互。

  流程是這樣的,客戶端傳遞一個User對象給服務端,服務端通過User對象處理數據,返回兩個Message對象給客戶端。

  首先,在客戶端傳遞給服務端User對象前,客戶端先把User對象序列化,然後傳遞給服務端之後,服務端接收到的是一段序列化後的數據,它再按照原定的規則對數據進行反序列化,然後處理User。當服務端查到這個User有兩條Message時,需要傳遞這兩條Message對象給客戶端,在傳遞前對Message對象進行序列化,客戶端收到服務端傳遞過來的序列化後的數據,再根據既定的規則進行反序列化,得到正確的對象。

  從這個流程可以看出,在進程間傳遞的數據必定是被序列化過的,否則無法傳遞。而對於那些AIDL默認允許傳遞的數據類型(int、double、String、List等),它們其實內部已經實現了序列化,所以無需我們再去指定序列化規則。但是對於復雜類型對象而言,系統無法知道如何去序列化與反序列化,所以需要我們指定規則。

 

  源碼下載

 

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