Android中Parcel的分析以及使用
簡單點來說:Parcel就是一個存放讀取資料的容器, android系統中的binder程式間通訊(IPC)就使用了Parcel類來進行客戶端與服務端資料的互動,而且AIDL的資料也是通過Parcel來互動的。在Java空間和C++都實現了Parcel,由於它在C/C++中,直接使用了記憶體來讀取資料,因此,它更有效率。
分析Binder機制中的客戶端與伺服器端進行實際操作ontransact()函式 :
- //引數說明:
- // code :是請求的ID號
- // data :客戶端請求傳送的引數
- // reply:伺服器端返回的結果
- // flags:一些額外的標識,如FLAG_ONEWAY等,通常為0.
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
從中我們可以看到Parcel的重要性以及窺探它的使用情況,接下來,我主要分析它的儲存機制。
常用方法介紹:
obtain() 獲得一個新的parcel ,相當於new一個物件
dataSize() 得到當前parcel物件的實際儲存空間
dataCapacity() 得到當前parcel物件的已分配的儲存空間, >=dataSize()值 (以空間換時間)
dataPostion() 獲得當前parcel物件的偏移量(類似於檔案流指標的偏移量)
setDataPosition() 設定偏移量
recyle() 清空、回收parcel物件的記憶體
writeInt(int) 寫入一個整數
writeFloat(float) 寫入一個浮點數
writeDouble(double) 寫入一個雙精度數
writeString(string) 寫入一個字串
當然,還有更多的writeXXX()方法,與之對應的就是readXXX(),具體方法請參閱SDK。
其中幾個值得注意的方法為:
writeException() 在Parcel隊頭寫入一個異常
writeException() Parcel隊頭寫入“無異常“
readException() 在Parcel隊頭讀取,若讀取值為異常,則丟擲該異常;否則,程式正常執行。
一、Parcel的分析
相信看了前面的值,對Parcel的使用該有了初步印象。那麼,Parcel的內部儲存機制是怎麼樣的?偏移量又是
什麼情況?讓我們回憶一下基本資料型別的取值範圍:
boolean 1bit 1位元組
char 16bit 2位元組
int 32bit 4位元組
long 64bit 8位元組
float 32bit 4位元組
double 64bit 8位元組
如果大家對C語言熟悉的話,C語言中結構體的記憶體對齊和Parcel採用的記憶體存放機制一樣,即讀取最小位元組
為32bit,也即4個位元組。高於4個位元組的,以實際資料型別進行存放,但得為4byte的倍數。基本公式如下:
實際存放位元組:
判別一: 32bit (<=32bit) 例如:boolean,char,int
判別二: 實際佔用位元組(>32bit) 例如:long,float,String,陣列等
當我們使用readXXX()方法時,讀取方法也如上述:
實際讀取位元組:
判別一: 32bit (<=32bit) 例如:boolean,char,int
判別二: 實際位元組大小(>32bit) 例如:long,float,String,數值等
由上可以知道,當我們寫入/讀取一個資料時,偏移量至少為4byte(32bit),於是,偏移量的公式如下:
f(x)= 4x (x=0,1,…n)
事實上,我們可以顯示的通過setDataPostion(int postion) 來直接操作我們欲讀取資料時的偏移量。毫無疑問,
你可以設定任何偏移量,但所讀取的值是型別可能有誤。因此顯示設定偏移量讀取值的時候,需要小心。
另外一個注意點就是我們在writeXXX()和readXXX()時,導致的偏移量是共用的,例如,我們在writeInt(23)後,
此時的datapostion=4,如果我們想讀取5,簡單的通過readInt()是不行的,只能得到0。這時我們只能通過
setDataPosition(0)設定為起始偏移量,從起始位置讀取四個位元組,即23。因此,在讀取某個值時,可能需要使用
setDataPostion(int postion)使偏移量裝換到我們的值處。
巧用setDataPosition()方法,當我們的parcel物件中只存在某一型別時,我們就可以通過這個方法來快速的讀取
所有值。具體方法如下:
- /**
- * 前提條件,Parcel存在多個型別相同的物件,本例子以10個float物件說明:
- */
- public void readSameType() {
- Parcel parcel =Parcel.obtain() ;
- for (int i = 0; i < 10; i++) {
- parcel.writeDouble(i);
- Log.i(TAG, "write double ----> " + getParcelInfo());
- }
- //方法一 ,顯示設定偏移量
- int i = 0;
- int datasize = parcel.dataSize();
- while (i < datasize) {
- parcel.setDataPosition(i);
- double fvalue = parcel.readDouble();
- Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());
- i += 8; // double佔用位元組為 8byte
- }
- // 方法二,由於物件的型別一致,我們可以直接利用readXXX()讀取值會產生偏移量
- // parcel.setDataPosition(0) ; //
- // while(parcel.dataPosition()<parcel.dataSize()){
- // double fvalue = parcel.readDouble();
- // Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());
- // }
- }
由於可能存在讀取值的偏差,一個預設的取值規範為:
1、 讀取複雜物件時: 物件匹配時,返回當前偏移位置的該物件;
物件不匹配時,返回null物件 ;
2、 讀取簡單物件時: 物件匹配時,返回當前偏移位置的該物件 ;
物件不匹配時,返回0;
下面,給出一張淺顯的Parcel的存放空間圖,希望大家在理解的同時,更能體味其中滋味。有點簡單,求諒解。
相信通過前面的介紹,你一定很瞭解了了Parcel的儲存機制,下面給定一應用程式來實踐。
1、佈局檔案如下:
- <span style="font-size:13px;"><?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical" android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <TextView android:layout_width="fill_parent"
- android:layout_height="wrap_content" android:text="@string/hello" />
- <LinearLayout android:orientation="horizontal"
- android:layout_width="fill_parent" android:layout_height="wrap_content">
- <Button android:id="@+id/btWriteByte" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="寫入一個byte值"></Button>
- <Button android:id="@+id/btWriteInt" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="寫入一個int值"></Button>
- </LinearLayout>
- <LinearLayout android:orientation="horizontal"
- android:layout_width="fill_parent" android:layout_height="wrap_content">
- <Button android:id="@+id/btWriteDouble" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="寫入一個double值"></Button>
- <Button android:id="@+id/btWriteString" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="寫入一個String值"></Button>
- </LinearLayout>
- <View android:layout_width="fill_parent" android:layout_height="2dip"
- android:background="#FF1493"></View>
- <LinearLayout android:orientation="horizontal"
- android:layout_marginTop="5dip" android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <Button android:id="@+id/btReadByte" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="讀取一個byte值"></Button>
- <Button android:id="@+id/btReadInt" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="讀取一個int值"></Button>
- </LinearLayout>
- <LinearLayout android:orientation="horizontal"
- android:layout_width="fill_parent" android:layout_height="wrap_content">
- <Button android:id="@+id/btReadDouble" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="讀取一個double值"></Button>
- <Button android:id="@+id/btReadString" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="讀取一個String值"></Button>
- </LinearLayout>
- <View android:layout_width="fill_parent" android:layout_height="2dip"
- android:background="#FF1493"></View>
- <Button android:id="@+id/btSameType" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="利用setDataPosition讀取多個值"></Button>
- </LinearLayout></span>
2、配置檔案如下:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.qinjuning.parcel"
- android:versionCode="1"
- android:versionName="1.0">
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <activity android:name=".MainActivity" android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
- </manifest>
3、程式主檔案如下:
- <span style="font-size:13px;">public class MainActivity extends Activity implements OnClickListener {
- private static String TAG = "PARCELTEST";
- // Button ID
- private static int[] btIds = new int[] { R.id.btWriteByte, R.id.btWriteInt,
- R.id.btReadDouble, R.id.btWriteString, R.id.btReadByte,
- R.id.btReadInt, R.id.btReadDouble, R.id.btReadString,
- R.id.btSameType };
- // 每種型別的當前值
- private byte cur_byte = 1; // 每次總寫入 false
- private int cur_int = 10; // 寫入值 cur_int ++ ;
- private double cur_float = 100.0d; // 寫入值 cur_float++ ;
- private String cur_str = "QinJun -->" + cur_int; // 寫入值 "QinJun -->"+cur_int
- private Parcel parcel = null;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- for (int i = 0; i < btIds.length; i++) {
- Button bt = (Button) findViewById(btIds[i]);
- bt.setOnClickListener(this);
- }
- parcel = Parcel.obtain(); // 獲得一個Parcel物件 ,相當於new一個,初始大小為0
- Log.i(TAG, "The original parcel info" + getParcelInfo());
- }
- @Override
- public void onClick(View view) {
- // TODO Auto-generated method stub
- int viewId = view.getId();
- switch (viewId) {
- case R.id.btWriteByte:
- parcel.setDataPosition(0);
- parcel.writeByte(cur_byte);
- Log.i(TAG, " after write byte, --->" + getParcelInfo());
- break;
- case R.id.btWriteInt:
- parcel.writeInt(cur_int);
- Log.i(TAG, " after write int, --->" + getParcelInfo());
- break;
- case R.id.btWriteDouble:
- parcel.writeDouble(cur_float);
- Log.i(TAG, " after write float, --->" + getParcelInfo());
- break;
- case R.id.btWriteString:
- parcel.writeString(cur_str);
- Log.i(TAG, " after write String, --->" + getParcelInfo());
- break;
- case R.id.btReadByte:
- byte b = parcel.readByte();
- Log.i(TAG, " read byte is=" + b + ", --->" + getParcelInfo()
- + "String");
- break;
- case R.id.btReadInt:
- int i = parcel.readInt();
- Log.i(TAG, " read int is=" + i + ", --->" + getParcelInfo());
- break;
- case R.id.btReadDouble:
- float f = parcel.readFloat();
- readSameType();
- Log.i(TAG, " read float is=" + f + ", --->" + getParcelInfo());
- break;
- case R.id.btReadString:
- parcel.setDataPosition(0);
- String str = parcel.readString();
- Log.i(TAG, " read float is=" + str + ", --->" + getParcelInfo());
- break;
- case R.id.btSameType:
- readSameType();
- break;
- default:
- break;
- }
- }
- private String getParcelInfo() {// 得到parcel的資訊
- return "dataSize = " + parcel.dataSize() + ", dataCapacity="
- + parcel.dataCapacity() + ", dataPositon = "
- + parcel.dataPosition();
- }
- /**
- * 前提條件,Parcel存在多個型別相同的物件,本例子以10個float物件說明:
- */
- public void readSameType() {
- for (int i = 0; i < 10; i++) {
- parcel.writeDouble(i);
- Log.i(TAG, "write double ----> " + getParcelInfo());
- }
- //方法一 ,顯示設定偏移量
- int i = 0;
- int datasize = parcel.dataSize();
- while (i < datasize) {
- parcel.setDataPosition(i);
- double fvalue = parcel.readDouble();
- Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());
- i += 8; // double佔用位元組為 8byte
- }
- // 方法二,由於物件的型別一致,我們可以直接利用readXXX()讀取值會產生偏移量
- // parcel.setDataPosition(0) ; //
- // while(parcel.dataPosition()<parcel.dataSize()){
- // double fvalue = parcel.readDouble();
- // Log.i(TAG, " read double is=" + fvalue + ", --->" + getParcelInfo());
- // }
- }</span>
- }
由於取值時,可能存在型別的轉換,因此點選按鈕時,可能不會產生預期結果。因此,得保證偏移量對應數值的正確性。
二、Parcel的使用
關於Parcel的使用,請參考如下兩票文章,小生也就不再贅述了。
1、 Android高手進階教程(十七)之---Android中Intent傳遞物件的兩種方法(Serializable,Parcelable)!
2、 Android高手進階教程(二十五)之---Android 中的AIDL!!!相關文章
- 打包工具-Parcel 的使用
- Android開發 - Creator 類從 Parcel 中建立新的物件解析Android物件
- Android記憶體洩露分析以及工具的使用Android記憶體洩露
- Android重修課 -- Parcel機制Android
- Android中AVD的使用以及錯誤處理方法Android
- Vue 中 $on $once $off $emit 詳細分析,以及使用VueMIT
- Parcel 與React 結合使用的小練手React
- Android 的滑動分析以及各種實現Android
- Android/java 多執行緒(一)-Thread的使用以及原始碼分析AndroidJava執行緒thread原始碼
- Parcel 打包器簡單使用記錄
- HBaseThrift使用以及Thriftserver分析Server
- Android 常用換膚方式以及原理分析Android
- Android中的使用Android
- 從 webpack 到全面擁抱 Parcel #1 探索 ParcelWeb
- ArrayList分析2 :Itr、ListIterator以及SubList中的坑
- 更優雅的使用 Parcel 進行前端專案的打包前端
- parcel打包vueVue
- Parcel Vs WebpackWeb
- lit parcel模板
- Android HAL 層框架分析以及程式碼示例Android框架
- Java反射以及在Android中的特殊應用Java反射Android
- Xposed瞭解以及在Android中的應用Android
- Android中SearchView的使用AndroidView
- Android中Handler的使用Android
- Android中SwipeMenuListView的使用AndroidView
- Android中SoundPool的使用Android
- Android中獲取系統記憶體資訊以及程式資訊-----ActivityManager的使用(一)Android記憶體
- arm 中斷配置以及處理的原始碼分析原始碼
- 從 webpack 到全面擁抱 Parcel #2 讓 Parcel 支援 vueWebVue
- ios中Runtime的介紹以及使用iOS
- CyclicBarrier、CountDownLatch以及Semaphore使用及其原理分析CountDownLatch
- JNI解析以及在Android中的實際應用Android
- Parcel上手實踐
- javascript中的this使用場景以及箭頭函式中的thisJavaScript函式
- OpenCV-android-sdk 配置以及使用(Java)OpenCVAndroidJava
- Android Gesture 手勢建立以及使用示例Android
- lisp 中的 【,@】 與 【·】 以及【‘】 以及【 。,】Lisp
- 使用parcel替代webpack製作omi-tap外掛Web