Android AIDL使用介紹(2)自定義數據類型的傳遞*


搬運自csdn博主風行南方

1.背景

默認情況下,AIDL只支持下列數據類型:

  • Java八種基礎數據類型(如 int、long、char、boolean 等);
  • String字符串;
  • CharSequence字符序列;
  • List列表,List中的所有元素須是前面提到的數據類型,或者是Parcelable類型(這也是本文准備講解的);
  • Map,Map 中的所有元素必須是以上列表中支持的數據類型,或者您所聲明的由 AIDL 生成的其他接口或Parcelable 類型;

如果想在進程間傳遞以上數據類型之外的對象該怎么辦?這就需用到Parcelable接口,Parcelable意思是“可包裹的”,是Android系統可用的序列化接口的一種,另外一種是來自java的Serialable,和Parcelable相比,Serialable的使用較為簡單,如果只是保存數據到本地這類應用,使用Serialable即可,但在進程間通訊,則建議使用Parcelable。

2.序列化和反序列化

Parcelable是序列化接口,那么什么是序列化,為什么進程間傳輸自定義對象需要使用序列化?百度百科對序列化的定義如下:

序列化是將對象的狀態信息轉換為可以存儲或傳輸的形式的過程。在序列化期間,對象將其當前狀態寫入到臨時或持久性存儲區。之后,可以通過從存儲區中讀取或反序列化對象的狀態,重新創建該對象。

數據在內存中是字節形態,數據分解到最后都是0和1字節流,這就像萬物分解到最后都是原子一樣,進程A要傳遞數據給進程B,同樣是以字節形式傳遞,對於基本數據類型,例如進程A傳遞int類型數據給進程B,因為進程A和進程B都知道int類型,因此進程B可以直接從內存中還原出數據,而對於自定義數據類型,例如進程A自定義了一個Student對象,進程B並不知道,如果直接傳遞字節流給進程B,面對一堆字節流,進程B不知道怎么重構還原,需要進程A告訴重構方法,這便是序列化和反序列化的過程,這也是傳遞自定義對象需使用序列化的原因。

3.使用示例

  1. 服務端創建自定義類型,這包括創建Student.java和Student.aidl,兩個文件分別在aidl目錄下和java目錄下,但兩個文件所在的包名必須相同,否則編譯不通。
    在這里插入圖片描述
    java代碼里實現Parcelable里的接口,其中值得注意的是writeToParcel方法和readFromParcel方法里面的寫和讀取順序是需要一一對應的,Parcel意為包裹,writeToParcel是快遞打包,作用是獲取對象的當前狀態並將其寫入 Parcel,readFromParcel是拆快遞,打包和拆的順序應一一對應,否則讀取數據會混亂,例如writeToParcel寫數據的順序如下:
    public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id);//寫一個int類型的值 dest.writeString(name);//寫一個String類型的值 dest.writeString(gender);//寫一個String類型的值 dest.writeInt(age);//寫一個int類型的值 }

則對應的readFromParcel應按照相應的順序讀取

    public void readFromParcel(Parcel source) { id = source.readInt(); name = source.readString(); gender = source.readString(); age = source.readInt(); }

另外,自定義類型中必須含有一個名稱為CREATOR的靜態成員,該成員是實現 Parcelable.Creator 接口的對象,用於從Parcel生成Parcelable的實例。

 public static final Creator<Student> CREATOR = new Creator<Student>() { @Override public Student createFromParcel(Parcel in) { return new Student(in); } @Override public Student[] newArray(int size) { return new Student[size]; } };

完整的自定義類型java代碼如下:

package com.pm.service; import android.os.Parcel; import android.os.Parcelable; public class Student implements Parcelable { private int id; private String name; private String gender; private int age; protected Student(Parcel in) { id = in.readInt(); name = in.readString(); gender = in.readString(); age = in.readInt(); } public Student(int id, String name, String gender, int age) { this.id = id; this.name = name; this.gender = gender; this.age = age; } public Student(){} public static final Creator<Student> CREATOR = new Creator<Student>() { @Override public Student createFromParcel(Parcel in) { return new Student(in); } @Override public Student[] newArray(int size) { return new Student[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeString(name); dest.writeString(gender); dest.writeInt(age); } public void readFromParcel(Parcel source) { id = source.readInt(); name = source.readString(); gender = source.readString(); age = source.readInt(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; }

java文件創建好了之后,則可以開始創建同名的Parcelable類的aidl文件,代碼如下,代碼較簡單,只是聲明Student是parcelable

// Student.aidl package com.pm.service; // Declare any non-default types here with import statements import com.pm.service.Student; parcelable Student; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. 自定義類型創建好了之后,便可以開始使用了,這跟使用基本類型一樣,不過,在使用之前,需要先使用import引入自定義類型,aidl代碼如下,該代碼是在上篇博客的基礎上新增接口,因此代碼其他部分不再做說明,具體請參考博客Android AIDL使用介紹(1)基本使用
// ServiceAidlInterface.aidl package com.pm.service; // Declare any non-default types here with import statements import com.pm.service.Student; interface ServiceAidlInterface { String ServiceGreet(); List<Student> getStudentList(); }

aidl代碼編寫好了之后,記得同步一下工程,然后在Service里實現新增的方法,完整的代碼如下:

package com.pm.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import java.util.LinkedList; import java.util.List; public class MyService extends Service { private ServiceAidlInterface.Stub serviceAidlInterface= new ServiceAidlInterface.Stub() { @Override public String ServiceGreet() throws RemoteException { return "Hello Client!"; } @Override public List<Student> getStudentList() throws RemoteException { List<Student> list = new LinkedList<>(); for (int i = 0; i < 10; i++) { list.add(new Student(i,"s1"+i,"man",i+10)); } return list; } }; @Override public IBinder onBind(Intent intent) { return serviceAidlInterface.asBinder(); } }

這樣一來,服務端要做的事都已完成,接下來是客戶端的內容。

  1. 從服務端拷貝aidl目錄下的文件和Student.java文件到客戶端,存放的目錄見圖,需要注意的是,客戶端的Student的aidl文件和java文件也要在相同的包名下,因客戶端也是在上篇博客的demo的基礎上編寫,只需增加一個按鈕做事件點擊之用,把服務端傳遞過來的Student對象的實例打印出來,完整的代碼見下:
    在這里插入圖片描述
package com.pm.myaidldemo; import androidx.appcompat.app.AppCompatActivity; 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.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.pm.service.ServiceAidlInterface; import com.pm.service.Student; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { String TAG="AIDLDemo"; private Button bServiceGreet; private Button bGetStu; private TextView tvInfo; private ServiceAidlInterface serviceAidlInterface; private ServiceConnection connection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { serviceAidlInterface = ServiceAidlInterface.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindService(); bServiceGreet=findViewById(R.id.btnServiceGreet); bGetStu=findViewById(R.id.btnGetStudent); tvInfo=findViewById(R.id.tvInfo); bServiceGreet.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String greet= null; try { greet = serviceAidlInterface.ServiceGreet(); tvInfo.setText(greet); } catch (RemoteException e) { e.printStackTrace(); } } }); bGetStu.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { StringBuilder sb=new StringBuilder(); List<Student> stulist= serviceAidlInterface.getStudentList(); for(int i=0;i<stulist.size()-5;i++) { sb.append("ID:"+stulist.get(i).getId()+"\n"); sb.append("Name:"+stulist.get(i).getName()+"\n"); sb.append("Gender:"+stulist.get(i).getGender()+"\n"); sb.append("Age:"+stulist.get(i).getAge()+"\n"); tvInfo.setText(sb.toString()); } } catch (RemoteException e) { e.printStackTrace(); } } }); } private void bindService() { Intent intent = new Intent(); intent.setAction("com.pm.service.MyService"); intent.setPackage("com.pm.service"); this.bindService(intent, connection, BIND_AUTO_CREATE); Log.e(TAG, "bindService: finish!"); } }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM