一人一貓旅行記之Intent傳遞資料原理
安卓提供了Intent機制來實現應用間的通訊,可以在Activity之間、Service、BroadCast中傳遞資料。提起Intent,我們最熟悉的可能就是在啟動Activity的時候攜帶資料,使用非常簡單:
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.putExtra("param1","我是引數1");
startActivity(intent);
在新的Activity,也就是Activity中獲取資料也是非常的簡單:
String param1 = getIntent().getStringExtra("param1");
我們可以看到在傳值的時候,呼叫了Intent的putExtra方法,我們檢視Intent的原始碼,發現Intent中定義了許多的putExtra方法,以我們們的上面的呼叫為例,實際上是呼叫瞭如下的方法:
public Intent putExtra(String name, String value) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.putString(name, value);
return this;
}
從上面的程式碼我們可以明白,在Intent中定義了一個Bundle型別的變數mExtras,當呼叫Intent的putExtra時,會根據第二個引數型別的不同,呼叫Bundle的對應方法。我們跑到Bundle會驚喜的發現沒有putString方法,所以只能到它的父類BaseBundle中尋找,結果如下:
ArrayMap<String, Object> mMap = null;
<-省略部分程式碼->
public void putString(@Nullable String key, @Nullable String value) {
unparcel();
mMap.put(key, value);
}
我們接著看看unparcel這個方法
synchronized void unparcel() {
synchronized (this) {
final Parcel parcelledData = mParcelledData;
if (parcelledData == null) {
if (DEBUG) Log.d(TAG, "unparcel "
+ Integer.toHexString(System.identityHashCode(this))
+ ": no parcelled data");
return;
}
if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
+ "clobber all data inside!", new Throwable());
}
if (isEmptyParcel()) {
if (DEBUG) Log.d(TAG, "unparcel "
+ Integer.toHexString(System.identityHashCode(this)) + ": empty");
if (mMap == null) {
mMap = new ArrayMap<>(1);
} else {
mMap.erase();
}
mParcelledData = null;
return;
}
int N = parcelledData.readInt();
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": reading " + N + " maps");
if (N < 0) {
return;
}
ArrayMap<String, Object> map = mMap;
if (map == null) {
map = new ArrayMap<>(N);
} else {
map.erase();
map.ensureCapacity(N);
}
try {
parcelledData.readArrayMapInternal(map, N, mClassLoader);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
map.erase();
} else {
throw e;
}
} finally {
mMap = map;
parcelledData.recycle();
mParcelledData = null;
}
if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ " final map: " + mMap);
}
}
從上面的程式碼我們可以看到一個Parcel物件mParcelledData,從程式碼看來是對該物件進行處理,實際上就是將Parcel資料遷移至mMap物件中。這樣就可以保證我們從mMap中獲取Bundle攜帶的資料。
@Nullable
public String getString(@Nullable String key) {
unparcel();
final Object o = mMap.get(key);
try {
return (String) o;
} catch (ClassCastException e) {
typeWarning(key, o, "String", e);
return null;
}
}
也可以把unparcel這個方法看成一個預處理,就是把已經序列化的資料反序列化後放在mMap中,之後Bundle就可以通過key從mMap中讀取資料。但是有一個問題,mParcelledData這個物件是從哪裡來的呢?
我們以startActivity為例,最終會調走到ActivityManagerNative中的onTransact函式,我們可以看到如下一段程式碼:
Bundle options = data.readInt() != 0
? Bundle.CREATOR.createFromParcel(data) : null;
我們回到Bundle.java看一下CREATOR
public static final Parcelable.Creator<Bundle> CREATOR =
new Parcelable.Creator<Bundle>() {
@Override
public Bundle createFromParcel(Parcel in) {
return in.readBundle();
}
@Override
public Bundle[] newArray(int size) {
return new Bundle[size];
}
};
從上面這段程式碼,可以看出createFromParcel實際呼叫的是Parcel的readBundle
繼續往下走的話,我們會走到如下方法:
public final Bundle readBundle(ClassLoader loader) {
int length = readInt();
if (length < 0) {
if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
return null;
}
final Bundle bundle = new Bundle(this, length);
if (loader != null) {
bundle.setClassLoader(loader);
}
return bundle;
}
從Bundle的建構函式繼續閱讀程式碼
BaseBundle(Parcel parcelledData, int length) {
readFromParcelInner(parcelledData, length);
}
private void readFromParcelInner(Parcel parcel, int length) {
if (length < 0) {
throw new RuntimeException("Bad length in parcel: " + length);
} else if (length == 0) {
// Empty Bundle or end of data.
mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
return;
}
final int magic = parcel.readInt();
if (magic != BUNDLE_MAGIC) {
throw new IllegalStateException("Bad magic number for Bundle: 0x"
+ Integer.toHexString(magic));
}
// Advance within this Parcel
int offset = parcel.dataPosition();
parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
Parcel p = Parcel.obtain();
p.setDataPosition(0);
p.appendFrom(parcel, offset, length);
if (DEBUG) Log.d(TAG, "Retrieving " + Integer.toHexString(System.identityHashCode(this))
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);
mParcelledData = p;
}
所以我們也可以知道mParcelledData實際就是傳過來的Bundle序列化後的資料
相關文章
- 一人一貓旅行記之Handler原理
- Android Intent 傳遞資料大小限制AndroidIntent
- 將 Intent 序列化,像 Uri 一樣傳遞 Intent!!!Intent
- 使用Intent傳遞物件Intent物件
- 探究intent傳遞大小限制Intent
- 認識一下Flutter中Navigator資料傳遞原理Flutter
- 頁面之間傳遞資料
- vue之prop,emit資料傳遞示例VueMIT
- vue元件之間的資料傳遞Vue元件
- HarmonyOS-基礎之元件資料傳遞元件
- VUE 傳遞資料Vue
- 探索startActivity流程及在Activity間是如何傳遞Intent的Intent
- 不同順序InBoundHandler之間的資料傳遞
- postman(五):在不同介面之間傳遞資料Postman
- flink watermark傳遞原理
- chan中傳遞map資料,傳遞的是引用
- 向上向下傳遞資料
- Flutter學習之Route跳轉及資料傳遞Flutter
- 兄弟元件之間資訊傳遞元件
- Vue元件間傳遞資料Vue元件
- 父子元件的資料傳遞元件
- Flutter 中的資料傳遞Flutter
- Vue元件間資料傳遞Vue元件
- 微信小程式父子元件之間的資料傳遞微信小程式元件
- 深入解析React資料傳遞之元件內部通訊React元件
- Ability之間或者程式間資料傳遞之物件(Sequenceable序列化)物件
- 【UniApp】-uni-app-傳遞資料APP
- golang工作筆記(二)值傳遞與引用傳遞Golang筆記
- Vue元件之間的資料傳遞(通訊、互動)詳解Vue元件
- 子元件給父親傳遞資料元件
- 【UniApp】-uni-app-CompositionAPI傳遞資料APPAPI
- uni-app全域性資料傳遞APP
- vuejs傳遞資料的方法有哪些VueJS
- JavaScript之按值傳遞JavaScript
- Android開發 - Parcelable 介面實現不同元件之間傳遞資料解析Android元件
- Angular路由——在路由時候傳遞資料Angular路由
- Vue 父子元件資料傳遞( inheritAttrs + $attrs + $listeners)Vue元件
- 微信小程式資料傳遞總結微信小程式