一場由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點名:一根煙引發的「血案」
※一場由刷牙引發的血案
※考試引發的血案