解鎖Android高效數據傳輸的秘鑰 - Parcelable剖析


作為Android開發者,我們經常需要在不同的組件(Activity、Service等)之間傳輸數據。這里的"傳輸"往往不僅僅是簡單的數據復制,還可能涉及跨進程的內存復制操作。當傳輸的數據量較大時,這種操作可能會帶來嚴重的性能問題。而Android系統為我們提供了Parcelable這一高效的序列化傳輸機制,很好地解決了這一痛點。今天,就讓我們一起來探訕Parcelable的神奇之處。


一、Parcelable架構與原理


Parcelable是Android中一種高效的序列化機制,用于實現進程間通信(IPC)中的對象傳遞。

Parcelable相對于Serializable的使用相對復雜一些,但Parcelable的效率相對Serializable也高很多,這一直是Google工程師引以為傲的,Parcelable和Serializable的效率對比Parcelable vs Serializable號稱快10倍的效率。

與Serializable接口不同,Parcelable采用的是手工編碼的方式,序列化后的數據更為緊湊。系統將數據打包到一個全局內存區域中,可供不同線程/進程共享訪問。


1、Parcelable的設計理念

Parcelable的設計理念是在保證一定性能的前提下,盡可能節省內存和CPU開銷。

從架構上來看,Parcelable涉及到了Binder驅動、Parcel容器和IPCThreadState等幾個關鍵組件,共同構成了高效的序列化通道:


(1)、Binder驅動

Binder驅動是Android的核心組件之一,負責進程間的數據傳輸。它在內核層為每個進程維護了一塊受保護的共享內存區域,用于在進程間傳遞Parcelable對象。


(2)、Parcel容器

Parcel對象是存儲序列化數據的臨時載體。開發者需要先將對象寫入Parcel中,然后由Binder驅動完成Parcel在進程間的拷貝和傳遞。


(3)、IPCThreadState

IPCThreadState是一個線程私有數據結構,負責在進程間管理請求和應答的Parcel對象數據。每個線程在與其他進程通信時,都會使用自己的IPCThreadState實例。


(4)、Parcelable接口

Parcelable接口定義了將對象寫入和從Parcel容器讀取的抽象協議,開發者需要手動實現這兩個序列化方法。系統會按此協議完成對象的編碼/解碼操作。


序列化的基本流程如下:

  1. 當一個進程需要向另一個進程傳輸數據時,會先初始化一個Parcel容器對象;
  2. 將要傳遞的Parcelable對象通過writeToParcel()方法寫入Parcel容器;
  3. Binder驅動從發送方進程拷貝這個Parcel容器到內核共享內存區域;
  4. 接收方進程從共享內存區讀取Parcel數據,并通過Parcelable.Creator反序列化出原始對象;
  5. 接收進程的目標組件(如Activity)即可使用這個反序列化出的對象數據。

整個過程無需經過Java層的序列化操作,因此效率極高。Parcel容器采用面向流的編碼格式存儲數據,格式緊湊,內存占用小。

此外,Parcelable的實現細節還包括:

  • 支持平臺默認Java數據類型的高效編解碼;
  • 使用標志位壓縮編碼,節省空間;
  • 引入Parcel窗口緩存,加快讀寫效率;
  • 等等一系列優化手段。

從架構和流程上看,Parcelable不僅擁有簡單的接口定義,而且在系統層得到了全方位的優化支持,使其在Android世界中成為高效低耗的序列化標準。當然,與之對應的是開發者必須自行編寫序列化方法的工作量。但從性能的角度來看,這一點工作量是完全值得的。


二、Parcelable接口的使用


要使用Parcelable,需要自己實現這兩個接口方法。

// 定義一個數據類MyData,實現Parcelable接口
public class MyData implements Parcelable {private int id;private String name;private boolean isAdult;// 構造函數public MyData(int id, String name, boolean isAdult) {this.id = id;this.name = name;this.isAdult = isAdult;}// 從Parcel反序列化時使用的特殊構造函數protected MyData(Parcel in) {id = in.readInt();name = in.readString();isAdult = in.readByte() != 0;}// writeToParcel方法,將數據寫入Parcel@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(id);dest.writeString(name);dest.writeByte((byte) (isAdult ? 1 : 0));}// 生成用于反序列的CREATOR對象public static final Creator<MyData> CREATOR = new Creator<MyData>() {@Overridepublic MyData createFromParcel(Parcel in) {return new MyData(in);}@Overridepublic MyData[] newArray(int size) {return new MyData[size];}};// describeContents是一個內部接口標志@Overridepublic int describeContents() {return 0;}// getter/setter ...
}

接著就可以通過Intent、Binder等方式傳輸這個Parcelable對象了。系統在底層會自動完成序列化和反序列化的工作。

// 使用示例:傳遞Parcelable對象
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 創建MyData對象MyData data = new MyData(1, "Jack", true);// 使用Intent傳遞MyDataIntent intent = new Intent(this, SecondActivity.class);intent.putExtra("data", data);startActivity(intent);}
}// 接收Parcelable對象
public class SecondActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second);// 從Intent中取出MyData對象MyData data = getIntent().getParcelableExtra("data");if (data != null) {int id = data.getId();String name = data.getName();boolean isAdult = data.isAdult();// 處理data...}}
}

在這個例子中:

  1. 我們定義了一個MyData類,實現了Parcelable接口。
  2. 在MyData類中,我們提供了一個用于Parcel反序列化的特殊構造函數,以及writeToParcel和describeContents方法。
  3. 同時生成了一個CREATOR對象,用于從Parcel重構MyData實例。
  4. 在MainActivity中,我們創建了一個MyData對象,并通過Intent將它傳遞給SecondActivity。
  5. 在SecondActivity中,我們從Intent中取出了MyData對象,可以使用其中的數據。

通過這個案例,你可以看到使用Parcelable傳遞一個自定義數據對象是非常簡單的。只需要完成幾個基本方法的實現,就可以實現對象的高效序列化和反序列化。

與手動實現序列化和Binder傳遞相比,使用Parcelable的代碼更加簡潔、安全,并得到了系統級的性能優化。這就是Parcelable作為Android高效IPC解決方案的魅力所在。


三、Parcelable的使用場景


以下是Parcelable主要用于的數據傳輸場景,以及結合案例代碼演示和使用Parcelable的優點分析。

1、Activity間傳遞數據


當從一個Activity導航到另一個Activity時,可以使用Intent攜帶數據。如果數據對象實現了Parcelable接口,可以直接在Intent中使用。

// 創建Parcelable對象
MyData myData = new MyData("Hello", 123);// 通過Intent傳遞數據
Intent intent = new Intent(CurrentActivity.this, NextActivity.class);
intent.putExtra("MY_DATA_KEY", myData);
startActivity(intent);

在接收的Activity中:

// 接收Parcelable數據
Intent intent = getIntent();
MyData myData = intent.getParcelableExtra("MY_DATA_KEY");

2、Activity與Service傳遞數據


Parcelable也可以用于ActivityService之間的數據傳輸。可以通過Intent發送數據到Service,或者Service返回結果給Activity

// Activity發送數據到Service
Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.putExtra("MY_DATA_KEY", myData);
startService(serviceIntent);

3、通過Binder傳輸數據


在使用AIDL(Android Interface Definition Language)定義服務時,Parcelable可以用來在客戶端和服務器之間傳遞數據。

// 在AIDL接口定義中
parcelable MyData;

4、將對象保存在Bundle或保存實例狀態


Activity的生命周期中,可以在onSaveInstanceState方法中使用Bundle保存Parcelable對象。

@Override
public void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);outState.putParcelable("MY_DATA_KEY", myData);
}@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {super.onRestoreInstanceState(savedInstanceState);myData = savedInstanceState.getParcelable("MY_DATA_KEY");
}

四、Parcelable與Serializable的比較


與Java中的Serializable相比,Parcelable有以下優勢:

1、性能更優
Parcelable序列化后的碼流要比Serializable小得多,內存開銷和CPU損耗也更低。

2、使用成本低
Parcelable無需使用反射,只需手寫幾個方法即可。

3、沒有安全隱患
Parcelable不會自動完成數據的深復制,避免了Serializable可能帶來的安全隱患。

當然,其缺點是需要手動編碼實現序列化邏輯,并維護代碼與類結構的同步,工作量較高。而Serializable則可以自動完成序列化。


五、Parcelable的性能優化建議


盡管Parcelable已經相當高效,但我們在實際使用時仍可以通過一些優化手段達到更佳的性能表現:

1、盡量使用標量類型
標量類型(int/long)可以直接通過writeInt/writeLong方法進行序列化,性能較高。

2、減少自動裝箱操作
避免對裝箱對象進行序列化,如Integer等。應直接使用基本類型。

3、編寫高效的Parcelable方法
在writeToParcel方法中應先寫入有效數據,而不是創建臨時對象。

4、啟用Parcelable代碼生成器
Parceler等工具可以自動生成Parcelable代碼,提高開發效率。


六、不得不提及的Bundler


Parcelable的強大遠不止于上述簡單用法。在Android 10開始,Google引入了Bundler框架,可以將任意的應用程序數據自動打包成一個Parcelable的Bundle,從而實現高效的跨進程通信。Bundler極大地簡化了使用Parcelable的難度。

這一強大功能曾廣受期待。令人惋惜的是,Bundler目前的可用性和成熟度似乎還有待提高。但不可否認,Google為我們展現了Parcelable在未來更大的應用前景。

無論是Android系統的Binder,還是Chrome瀏覽器的IPC數據傳輸,Parcelable都扮演著舉足輕重的角色。它使得Android應用能夠高效、安全地在進程間傳輸數據。面向未來,或許Parcelable的序列化能力會不斷增強,甚至取代JVM的Serializable成為跨平臺的數據序列化標準。讓我們拭目以待吧!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/14595.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/14595.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/14595.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

web自動化之PO模式

PO模式 1、為什么需要PO思想&#xff1f; 首先我們觀察和思考一下&#xff0c;目前我們寫的作業腳本的問題&#xff1a; 元素定位和操作動 作寫到一起了&#xff0c;這就就會用導致一個問題&#xff1a; UI的頁面元素比較容易變化的&#xff0c;所以元素定位和腳本操作寫到一…

如何將照片從 iPhone 傳輸到閃存驅動器【無質量損壞】

概括 人們喜歡用 iPhone 拍照&#xff0c;因為照片通常都很漂亮&#xff0c;這都要歸功于 iPhone 令人驚嘆的技術。但照片更新后會占用更多空間&#xff0c;并且您可能會開始收到沒有存儲空間的通知。因此&#xff0c;您可以將照片傳輸到 USB 驅動器&#xff0c;然后從 iPhone…

Spring Boot構建mvc項目

好的,以下是一個簡單的Java MVC(Model-View-Controller)項目示例,使用Spring Boot框架和MySQL數據庫。這個項目包括基本的CRUD操作。 項目結構 src/ └── main/├── java/│ └── com/│ └── example/│ └── demo/│ ├──…

springboot-阿里羚羊 服務端埋點

官方文檔 集成Java SDK 手動引入jar包「quickaplus-log-collector-java-sdk-1.0.1-SNAPSHOT.jar」 <dependency><groupId>com.alibaba.lingyang</groupId><artifactId>quickaplus-log-collector-java-sdk</artifactId><version>1.0.1&l…

應用案例 | 如何實時監測和管理冷鏈倉庫溫濕度?

一、項目背景 冷鏈倉庫溫濕度管理的重要性在于確保倉庫內產品的質量和安全。通過遵循相關法規和標準&#xff0c;滿足客戶對產品質量的需求&#xff0c;同時實施有效的溫濕度管理措施&#xff0c;可以降低成本并提高倉庫作業效率。該項目的實施旨在幫助客戶保證產品的新鮮度&a…

Java - AbstractQueuedSynchronizer

AQS簡介 AQS全稱AbstractQueuedSynchronizer&#xff0c;抽象隊列同步器&#xff0c;是一個實現同步組件的基礎框架。AQS使用一個int類型的成員變量state維護同步狀態&#xff0c;通過內置的同步隊列&#xff08;CLH鎖、FIFO&#xff09;完成線程的排隊工作&#xff0c;底層主…

echarts 散點圖修改散點圖中圖形形狀顏色大小

話不多說&#xff0c;直接上代碼 let option {color:[xxx, xxx, xxx, xxx], //直接設置color可修改圖形顏色title: {text: 散點圖圖形,},tooltip: {trigger: axis,axisPointer: {type: cross}},legend: {top: 2,right:2,itemWidth: 10,itemHeight: 10,textStyle:{fontSize:14}…

shell腳本條件語句和循環語句

文章目錄 一、條件語句測試比較整數數值字符串比較邏輯運算雙中括號&#xff08; &#xff09;{ }if語句結構case語句 二、循環語句基礎知識for循環whileuntil雙重循環及跳出循環 一、條件語句 測試 條件測試&#xff1a;判斷某需求是否滿足&#xff0c;需要由測試機制來實現…

視頻分類——C3D使用

整體比較分散&#xff0c;可能很多源碼都需要修改&#xff0c;需要有耐心。 一、數據準備 PS 調研后&#xff0c;上手容易代碼比較簡潔的是&#xff1a;https://github.com/Niki173/C3D/tree/main 因為源碼很多參數都寫死到了源碼中&#xff0c;沒有解耦&#xff0c;并且默…

CCF-CSP認證 2024年3月 4.化學方程式配平

題解&#xff1a;首先完成數據的讀入&#xff0c;然后高斯消元求秩按題意解即可 #pragma GCC optimize(2, 3, "Ofast", "inline") #include <bits/stdc.h> using namespace std; const int maxn 100;using matrix double[maxn][maxn]; using vect…

5.20Git

版本控制工具Git&#xff0c;其他的工具還有SVN 共享代碼&#xff0c;追溯記錄&#xff0c;存儲.c文件 Git實現的功能&#xff1a;回溯&#xff08;以前某個時間節點的數據情況&#xff09;共享&#xff08;大家共享修改&#xff09; Git&#xff1a;80% SVN&#xff…

QT tableWidget詳細分析

一.定義 QTableWidget是一個用于顯示表格數據的Qt控件&#xff0c;它是一個基于Qt Model/View框架的視圖組件。QTableWidget提供了一種簡單的方式來展示和編輯表格數據&#xff0c;用戶可以通過添加行、列和單元格來構建一個完整的數據表格。 下面是一些QTableWidget的主要特點…

The Missing Semester of Your CS Education(計算機教育中缺失的一課)

Shell 工具和腳本(Shell Tools and Scripting) 一、shell腳本 1.1、變量賦值 在bash中為變量賦值的語法是foobar&#xff0c;訪問變量中存儲的數值&#xff0c;其語法為 $foo。 需要注意的是&#xff0c;foo bar &#xff08;使用空格隔開&#xff09;是不能正確工作的&…

網工內推 | 香港移動,10年以上數通經驗,CCIE,5W-6W

01 香港移動招聘 &#x1f537;招聘崗位&#xff1a;網絡工程師 &#x1f537;崗位要求&#xff1a; 需要有10年及以上數通經驗&#xff0c;有CCIE 證書&#xff0c;懂技術管理&#xff0c;溝通暢通 &#x1f537;語言要求&#xff1a; 粵語英語 &#x1f537;薪資&#xff1…

基于灰狼優化算法優化RBF(GWO-RBF)的數據回歸預測(多輸入多輸出)

代碼原理及流程 基于灰狼優化算法優化多輸入多輸出&#xff08;MIMO&#xff09;的RBF神經網絡的數據回歸預測&#xff0c;可以采取以下步驟&#xff1a; 1. 數據準備&#xff1a;準備包含多個輸入特征和多個輸出目標的數據集&#xff0c;確保數據已經經過預處理和歸一化。 …

TCP - 半連接隊列和全連接隊列

目錄 一、半連接隊列和全連接隊列的概念 二、全連接隊列溢出 三、半連接隊列溢出 一、半連接隊列和全連接隊列的概念 1. 半連接隊列&#xff1a;服務端收到客戶端發送的 SYN 包時&#xff0c;內核會將該連接加入半連接 SYN 隊列&#xff0c;并向客戶端返回響應 2. 全連接隊…

CSS基礎(第二天)

Emmet語法 快速生成HTML結構語法 1. 生成標簽 直接輸入標簽名 按tab鍵即可 比如 div 然后tab 鍵&#xff0c; 就可以生成 <div></div> 2. 如果想要生成多個相同標簽 加上 * 就可以了 比如 div*3 就可以快速生成3個div 3. 如果有父子級關系的標簽&#xff0c;可以…

算法刷題筆記 數的范圍(C++實現)(二分法重要例題)

文章目錄 題目描述題目思路題目代碼&#xff08;C&#xff09;題目感想 題目描述 給定一個按照升序排列的長度為n的整數數組&#xff0c;以及q個查詢。對于每個查詢&#xff0c;返回一個元素k的起始位置和終止位置&#xff08;位置從0開始計數&#xff09;。如果數組中不存在該…

Docker【2】iptables 錯誤解決

iptables 錯誤解決 問題說明問題分析解決步驟1. 確保 iptables 模塊已加載2. 檢查和重啟 docker 服務3. 檢查 firewalld 狀態4. 重置 iptables 規則5. 查看和更新 Docker 配置 總結 問題說明 執行的 docker 命令如下&#xff0c;啟動 nginx 并設置宿主機端口 (8080) 與容器端口…

學習Uni-app開發小程序Day25

這一章學習了觸底加載更多阻止無效的網絡請求、分類列表存入Storage在預覽頁面讀取緩存展示、通過swiper的事件實現真正的壁紙預覽及切換 觸底加載更多阻止無效的網絡請求、load-more樣式的展現 前面已經學習了當列表觸底后&#xff0c;會繼續加載&#xff0c;當到最后一層后…