編輯:Android開發實例
前言
Android的AIDL不僅可以在綁定服務中傳遞一些Android規定的數據類型的數據,還可以傳遞一些復雜類型的數據。但是與傳遞系統允許的數據類型相比,復雜類型數據的傳遞要做更多的工作,本文就講解一下如何使用AIDL接口通過綁定服務在進程間傳遞數據。關於AIDL傳遞規定類型數據的內容,不了解的朋友可以看看:http://www.fengfly.com/plus/view-214084-1.html。
本文的主要內容:
AIDL傳遞復雜類型對象的特殊處理
前面已經介紹了通過AIDL接口在進程間傳遞系統允許的數據,如果需要傳遞一個復雜類型的對象,就沒那麼簡單了,需要額外做一些處理。如下:
例如:Message.aidl
- parcelable Message;
例如:IGetMsg.aidl
- package com.example.aidlservicedemo.domain;
- // 這是兩個自定義類
- import com.example.aidlservicedemo.domain.Message;
- import com.example.aidlservicedemo.domain.User;
- interface IGetMsg{
- // 在AIDL接口中定義一個getMes方法
- 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也提供了一系列的方法幫助寫入數據與讀取數據,這裡簡單介紹一下:
簡單來說,Parcelable通過writeToParcel()方法,對復雜對象的數據寫入Parcel的方式進行對象序列化,然後在需要的時候,通過其內定義的靜態屬性CREATOR.createFromParcel()進行反序列化的操作。Parcelable對Parcel進行了包裝,其內部就是通過操作Parcel進行序列化與反序列化的。
Parcelable與Parcel均定義在android.os包下,而這種機制不僅用於AIDL,還可以用於Intent傳遞數據等其他地方,這不是本篇博客的主題,以後用到再詳細介紹。
實現Parcelable接口
定義好數據接口的AIDL文件後,需要定義一個數據實現類,實現Parcelable接口,並實現對應的方法,Parcelable有如下幾個必須要實現的抽象方法:
除了上面兩個方法,還需要在實現類中定義一個名為"CREATOR",類型為"Parcelable.Creator<T>"的泛型靜態屬性,它實現了對象的反序列化。它也有兩個必須實現的抽象方法:
例如:
- @Override
- public int describeContents() {
- return 0;
- }
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- Log.i("main", "服務端Message被序列化");
- dest.writeInt(id);
- dest.writeString(msgText);
- dest.writeString(fromName);
- dest.writeString(date);
- }
- public static final Parcelable.Creator<Message> CREATOR = new Creator<Message>() {
- @Override
- public Message[] newArray(int size) {
- return new Message[size];
- }
- @Override
- public Message createFromParcel(Parcel source) {
- Log.i("main", "服務端Message被反序列化");
- return new Message(source.readInt(), source.readString(),
- source.readString(), source.readString());
- }
- };
從上面示例中可以看出,使用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
- package com.example.aidlservicedemo.domain;
- import android.os.Parcel;
- import android.os.Parcelable;
- import android.text.format.DateUtils;
- import android.util.Log;
- public class Message implements Parcelable {
- private int id;
- private String msgText;
- private String fromName;
- private String date;
- public Message() {
- super();
- }
- public Message(int id, String msgText, String fromName, String date) {
- super();
- this.id = id;
- this.msgText = msgText;
- this.fromName = fromName;
- this.date = date;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getMsgText() {
- return msgText;
- }
- public void setMsgText(String msgText) {
- this.msgText = msgText;
- }
- public String getFromName() {
- return fromName;
- }
- public void setFromName(String fromName) {
- this.fromName = fromName;
- }
- public String getDate() {
- return date;
- }
- public void setDate(String date) {
- this.date = date;
- }
- @Override
- public int describeContents() {
- return 0;
- }
- @Override
- public String toString() {
- return "信息內容=" + msgText + ", 發件人="
- + fromName + ", 時間=" + date ;
- }
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- Log.i("main", "客戶端Message被序列化");
- dest.writeInt(id);
- dest.writeString(msgText);
- dest.writeString(fromName);
- dest.writeString(date);
- }
- public static final Parcelable.Creator<Message> CREATOR = new Creator<Message>() {
- @Override
- public Message[] newArray(int size) {
- return new Message[size];
- }
- @Override
- public Message createFromParcel(Parcel source) {
- Log.i("main", "客戶端Message被反序列化");
- return new Message(source.readInt(), source.readString(),
- source.readString(), source.readString());
- }
- };
- }
- Message.java
com.example.aidlservicedemo.domain.User.aidl
parcelable User;
com.example.aidlservicedemo.domain.User.java
- package com.example.aidlservicedemo.domain;
- import android.os.Parcel;
- import android.os.Parcelable;
- import android.util.Log;
- public class User implements Parcelable {
- private int id;
- private String username;
- private String password;
- public User() {
- super();
- }
- public User(int id, String username, String password) {
- super();
- this.id = id;
- this.username = username;
- this.password = password;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getUsername() {
- return username;
- }
- public void setUsername(String username) {
- this.username = username;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- @Override
- public boolean equals(Object o) {
- User us=(User)o;
- if(this.username.equals(us.username)&&this.password.equals(us.password))
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- @Override
- public int describeContents() {
- return 0;
- }
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- Log.i("main", "客戶端User被序列化");
- dest.writeInt(id);
- dest.writeString(username);
- dest.writeString(password);
- }
- public static final Parcelable.Creator<User> CREATOR=new Creator<User>() {
- @Override
- public User[] newArray(int size) {
- return new User[size];
- }
- @Override
- public User createFromParcel(Parcel source) {
- Log.i("main", "客戶端User被反序列化");
- return new User(source.readInt(), source.readString(),
- source.readString());
- }
- };
- }
- User.java
服務:
com.example.aidlservicedemo.
- package com.example.aidlservicedemo;
- import java.util.ArrayList;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import com.example.aidlservicedemo.domain.IGetMsg.Stub;
- import com.example.aidlservicedemo.domain.Message;
- import com.example.aidlservicedemo.domain.User;
- import android.app.Service;
- import android.content.Intent;
- import android.os.IBinder;
- import android.os.RemoteException;
- import android.util.Log;
- public class CustomTypeService extends Service {
- private static final String TAG = "main";
- private MsgBinder msgBinder=null;
- private static Map<User, List<Message>> map=new HashMap<User, List<Message>>();
- static{
- for(int i=0;i<3;i++){
- User user=new User(i, "jack"+i, "9999999999"+i);
- List<Message> messages=new ArrayList<Message>();
- Message msg=null;
- if(i==0)
- {
- msg=new Message(i, "這兩天好嗎?", "Jerry", new Date().toGMTString());
- messages.add(msg);
- }else if(i==1)
- {
- msg=new Message(i, "周天去逛街吧!", "Tim", new Date().toGMTString());
- messages.add(msg);
- msg=new Message(i, "好無聊!", "Wesley", new Date().toGMTString());
- messages.add(msg);
- }
- else
- {
- msg=new Message(i, "上次的問題解決了嗎?", "Bonnie", new Date().toGMTString());
- messages.add(msg);
- msg=new Message(i, "明天一起吃飯吧?", "Matt", new Date().toGMTString());
- messages.add(msg);
- msg=new Message(i, "在哪裡?", "Caroline", new Date().toGMTString());
- messages.add(msg);
- }
- map.put(user, messages);
- }
- }
- public class MsgBinder extends Stub{
- @Override
- public List<Message> getMes(User us) throws RemoteException {
- for(Map.Entry<User, List<Message>> msgs:map.entrySet()){
- if(msgs.getKey().getUsername().equals(us.getUsername())&&msgs.getKey().getPassword().equals(us.getPassword())){
- Log.i(TAG, "找到信息了");
- return msgs.getValue();
- }
- }
- Log.i(TAG, "沒找到信息了");
- return map.get(us);
- }
- }
- @Override
- public IBinder onBind(Intent intent) {
- // TODO Auto-generated method stub
- return msgBinder;
- }
- @Override
- public void onCreate() {
- // TODO Auto-generated method stub
- super.onCreate();
- msgBinder=new MsgBinder();
- }
- @Override
- public void onDestroy() {
- msgBinder=null;
- super.onDestroy();
- }
- }
- CustomTypeService.java
客戶端:
com.example.aidlClientdemo.
- package com.example.aidlClientdemo;
- import java.util.List;
- import java.util.Random;
- import com.example.aidlservicedemo.domain.IGetMsg;
- import com.example.aidlservicedemo.domain.Message;
- import com.example.aidlservicedemo.domain.User;
- import android.app.Activity;
- import android.content.ComponentName;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.Bundle;
- import android.os.IBinder;
- import android.os.RemoteException;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.Toast;
- public class CustomTypeActivity extends Activity {
- private Button btn_startService, btn_endService, btn_getServiceData;
- private IGetMsg getMsg;
- private static User[] users = new User[] {
- new User(0, "jack0", "99999999990"),
- new User(0, "jack1", "99999999991"),
- new User(0, "jack2", "99999999992") };
- private ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- getMsg = null;
- }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- getMsg = IGetMsg.Stub.asInterface(service);
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_service);
- btn_startService = (Button) findViewById(R.id.btn_startService);
- btn_endService = (Button) findViewById(R.id.btn_endService);
- btn_getServiceData = (Button) findViewById(R.id.btn_getServiceData);
- btn_startService.setOnClickListener(click);
- btn_endService.setOnClickListener(click);
- btn_getServiceData.setOnClickListener(click);
- }
- private View.OnClickListener click = new OnClickListener() {
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.btn_startService:
- startService();
- break;
- case R.id.btn_endService:
- endService();
- break;
- case R.id.btn_getServiceData:
- getServiceDate();
- break;
- }
- }
- };
- /**
- * 獲取其他線程服務數據
- */
- private void getServiceDate(){
- try {
- Random random=new Random();
- int nextInt=random.nextInt(2);
- List<Message> msgs=getMsg.getMes(users[nextInt]);
- StringBuilder sBuilder=new StringBuilder();
- for(Message msg:msgs){
- sBuilder.append(msg.toString()+"\n");
- }
- Toast.makeText(CustomTypeActivity.this, sBuilder.toString(), Toast.LENGTH_SHORT).show();
- } catch (Exception e) {
- Toast.makeText(CustomTypeActivity.this, "獲取數據出錯", Toast.LENGTH_SHORT).show();
- e.printStackTrace();
- }
- }
- /**
- * 開始服務
- */
- private void startService() {
- Intent service = new Intent();
- service.setAction("cn.bgxt.Service.CUSTOM_TYPE_SERVICE");
- bindService(service, conn, BIND_AUTO_CREATE);
- Toast.makeText(CustomTypeActivity.this, "綁定服務成功", Toast.LENGTH_SHORT).show();
- }
- /**
- * 停止服務
- */
- private void endService() {
- unbindService(conn);
- Toast.makeText(CustomTypeActivity.this, "解除綁定服務成功", Toast.LENGTH_SHORT).show();
- }
- }
- CustomTypeActivity.java
效果展示:
AIDL傳遞對象序列化過程詳解
通過上面Demo打印的日志,解釋一下序列化的過程,打開Logcat查看日志。
從上圖的PID列可以看出這是兩個線程間的交互。
流程是這樣的,客戶端傳遞一個User對象給服務端,服務端通過User對象處理數據,返回兩個Message對象給客戶端。
首先,在客戶端傳遞給服務端User對象前,客戶端先把User對象序列化,然後傳遞給服務端之後,服務端接收到的是一段序列化後的數據,它再按照原定的規則對數據進行反序列化,然後處理User。當服務端查到這個User有兩條Message時,需要傳遞這兩條Message對象給客戶端,在傳遞前對Message對象進行序列化,客戶端收到服務端傳遞過來的序列化後的數據,再根據既定的規則進行反序列化,得到正確的對象。
從這個流程可以看出,在進程間傳遞的數據必定是被序列化過的,否則無法傳遞。而對於那些AIDL默認允許傳遞的數據類型(int、double、String、List等),它們其實內部已經實現了序列化,所以無需我們再去指定序列化規則。但是對於復雜類型對象而言,系統無法知道如何去序列化與反序列化,所以需要我們指定規則。
源碼下載
本文介紹一個簡易的標准體重計算器Android應用的開發實例。此功能在以前的手
1、Service的種類 按運行地點分類: 類別 區別 優點 缺點 應用 本地服務(Local) 該服務依附在主進程
unity3d發布apk在android虛擬機中運行的詳細步驟(unity3d導出android apk),總的流程分為以下6個步驟: 1、安裝java_jdk
1. UiObejct.getFromParent()的用法:從這個名字就知道,就是從當前對象的父對象中查找想要的子對象,該子對象和當前對象應該是同一層級。 如