當前位置:
首頁 > 最新 > 一場由Parcelable引發的血案

一場由Parcelable引發的血案

問題背景

前陣子接手了直播模塊,有個需求需要在已有的AIDL介面中增加多一個int類型的參數B。由於該AIDL介面中已經有了一個自定義類型的參數A(已經實現Parcelable介面),我便將參數B追加到A的後面。嗯,炒雞簡單的,只是運行之後有問題而已(微笑臉)

有一個詭異的問題:無論B傳入什麼值,另一方接收到都始終為0(int的默認值),而A接收到的值卻是正確的?!

Take it easy! 作為共產主義的接班人,我當然是有辦法的啦:

懷疑是Freeline不支持AIDL,改用AS重新build,問題依舊

懷疑是辣雞AS的問題,重新啟動再build,問題依舊

懷疑是工具鏈的版本問題,改為最新版本再次編譯,問題依舊

懷疑是int的問題?將B改為float, boolean等其他的基本類型,問題依舊,取到的始終是默認值

抱著希望在StackOverflow和Google上逛了一圈,無果

求助群里的小夥伴,答曰沒有遇到過此情況,並向我丟了一連竄的「233333」和一波表情

辭職

就在我一籌莫展的時候,突然腦子一抽,試著把介面定義中A參數和B參數位置調換。震驚地發現,居然可以了!!

嗯,此篇文章完結,撒花~

解決方案

將基本類型的參數放在自定義類型的參數前面,雖然解決了問題,但是治標不治本,只能算個workaround。

不知道大家有沒有發現:參數的定義順序會影響結果這一行為,是不是跟實現Parcelable介面的時候有點類似?Parcelable中如果read的順序和write的順序不同的話,產生的結果也不同。

基於這點,我們懷疑問題出在自定義類型的參數A,先來看下相關代碼:

//ILivePlayerService.aidl interface ILivePlayerService {

//參數A:config, 參數B:type

void setPlayerConfig(in PlayerConfig config, int type) }//PlayerConfig.java public class PlayerConfig implements Parcelable { //省略其他代碼.... String streamUrl; int roomId; int anchorId; public PlayerConfig() { } protected PlayerConfig(Parcel in) { //省略其他代碼.... this.streamUrl = in.readString(); this.roomId = in.readInt(); this.anchorId = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { //省略其他代碼.... dest.writeString(this.streamUrl); dest.writeInt(this.roomId); //下面注釋的這句,代碼中是沒有的。問題就出現在這裡.. //dest.writeInt(this.anchorId); } @Override public int describeContents() { return 0; } public static final Creator CREATOR = new Creator() { @Override public PlayerConfig createFromParcel(Parcel source) { return new PlayerConfig(source); } @Override public PlayerConfig[] newArray(int size) { return new PlayerConfig[size]; } }; }

果然, 沒有正確地實現 Parcelable 介面,在寫入的時候(詳見上面代碼中 方法中的注釋)漏掉了變數 ,而讀取的時候卻有。

論接手別人的代碼是一種怎樣的體驗?

我們先把 中漏掉的變數補上,再跑一下看問題是否解決了。

不出所料,那個詭異的問題沒有了,可是為什麼呢?我們來看下AIDL生成的Java代碼:

//AIDL生成的 ILivePlayerService.java //為了方便查看,格式了一下代碼 @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } //setPlayerConfig方法 case TRANSACTION_setPlayerConfig: { data.enforceInterface(DESCRIPTOR); //參數A:config com.kk.model.PlayerConfig _arg0; if ((0 != data.readInt())) {

//在解析的時候,會調用PlayerConfig的createFromParcel方法 _arg0 = com.kk.model.PlayerConfig.CREATOR.createFromParcel(data); } else { _arg0 = null; } //參數B:type int _arg1; _arg1 = data.readInt(); this.setPlayerConfig(_arg0, _arg1); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); }

我們可以看到在解析數據的時候,參數A和參數B都是從data中解析的(共用一個Parcel源)其中,解析參數A 的時候會將 傳入到自己實現的 方法中進行處理,如下

//PlayerConfig.java 部分代碼 public static final Creator CREATOR = new Creator() { @Override public PlayerConfig createFromParcel(Parcel source) {

//接受到aidl傳入的data

return new PlayerConfig(source); } }; protected PlayerConfig(Parcel in) { //aidl傳入的data在這裡解析

this.streamUrl = in.readString();

this.roomId = in.readInt();

this.anchorId = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) {

//省略其他代碼....

dest.writeString(this.streamUrl);

dest.writeInt(this.roomId);

//下面注釋的這句,代碼中是沒有的。問題就出現在這裡..

//dest.writeInt(this.anchorId); }

由於 沒有正確地實現 ,只寫入了1個int類型,但是卻讀取了2個,這就導致了參數A多讀取了一個int… 等到參數B想從 中讀取的時候,就會讀取不到數值(返回默認值)…

這個涉及到了Parcel的內部機制,可以參考這篇文

小結

這次的坑是因為沒有正確實現Parcelable介面導致的。這很不應該,其實我們可以讓工具來做這種體力活,比如AS中有個插件叫做「Android Parcelable code generator」就可以一鍵生成Parcelable代碼,或者去Github搜搜Parcelable相關的註解庫也行~

程序猿要對自己好點,能用工具完成的事情盡量不要自己寫~

踩坑結束!

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 推酷 的精彩文章:

Angular 4 依賴注入教程之一
一封「神奇」快遞引發的刷單追問,淘寶刷單到底動了多少人的隱私?
從記者編輯到產品經理,我的轉行感悟
代碼實例展示Java和Kotlin有哪些區別?
切To B企業的營銷市場,「探跡」用人工智慧提供銷售預測分析

TAG:推酷 |

您可能感興趣

Find life 滴滴再出血案
一場車zhen引發的血案
Gucci背後的家族血案
揚言要打爆theshy的FNC大師兄,喜提2-8成自閉,一顆眼引發的血案
黑五剛過,再發槍擊血案!槍手闖進outlets向人群掃射,華人躲進Coach倉庫撿回小命!
袁崇煥之死:一個flag引發的血案
一場蘭博基尼Urus引發的血案,全球最快SUV都是誰!
Cremona華人血案進入庭審:兇手沒有精神病,屬於危險人群
75歲老婦遭性侵後,羅馬Vittorio華人區再爆血案,一男子被捅數刀!
由兩張圖引發的血案,vivo為何不可以強過蘋果?
血案後,多名學生被查出帶槍上學,Santa Fe附近5所學校再遭槍支威脅
大鍘蟹即將引發血案!爐石傳說:無限buff「螃蟹牧」卡組強勢來襲
鄧倫和楊紫組cp?ins關注引發的血案,雙方粉絲因沒回關開罵
Lily 看了江歌案,我想起20年前的那起留學生血案
十五年前,那場由MSN簽名引發的「血案」
起底BEC,一個漏洞引發的血案
DNF再次登上新聞,由盜號者引發的血案!
王源被BBC點名:一根煙引發的「血案」
一場由刷牙引發的血案
考試引發的血案