您好,登錄后才能下訂單哦!
這篇文章主要介紹了Android下Activity間通信序列化過程中深淺拷貝的示例分析,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
前言
問題的背景是,視頻互動業務需要增加彈幕功能,但是播放器的視圖是偽橫屏的,即,他是一種類似于使用 rotate(90.0)的方式,旋轉橫屏的,在 Activity 層面上還是一個豎屏的狀態。那么彈幕輸入的時候的鍵盤,也是豎屏的。這會帶來比較嚴重的用戶體驗問題。
由于屏幕旋轉狀態在 android 下,是一個 Activity 層面上的事情,而且相當的底層,無從 hook,多方調研以后,決定采拉起一個橫屏的 Activity 作為鍵盤輸入的專用 Activity。
這里的代碼很快就可以寫好,如下所示:
/** * Created by DesGemini on 12/09/2017. */ public class DialogActivity extends Activity { private RelativeLayout mContentView; private View vSendBtn; private EditText etDanmakuInput; private InputMethodManager mInputMethodManager; public static WeakReference<DanmakuWriteCallback> danmakuWriteCallback = new WeakReference<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mInputMethodManager = (InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE); mContentView = (RelativeLayout) getLayoutInflater() .inflate(R.layout.hiv_danmaku_input_dialog, null); vSendBtn = mContentView.findViewById(R.id.tv_danmaku_send_btn); etDanmakuInput = (EditText) mContentView.findViewById(R.id.et_danmaku_input); vSendBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 這里需要處理 Activity 間回傳邏輯 } }); setContentView(mContentView); showSoftKeyboard(); } private boolean showSoftKeyboard() { if (this.etDanmakuInput == null) { return false; } else { etDanmakuInput.postDelayed(new Runnable() { public void run() { etDanmakuInput.requestFocus(); mInputMethodManager.showSoftInput(etDanmakuInput, 0); } }, 100L); return true; } } @Override protected void onPause() { super.onDestroy(); danmakuWriteCallback.getAndSet(null); } @Override public void finish() { super.finish(); } }
DTO 的代碼定義如下所示:
public class DanmakuDialogDTO implements Serializable { public WeakReference<DWDanmakuWriteController.DanmakuWriteCallback> callback; public Map<String, String> utExtraParams; }
那么現在問題來了,怎么把這個 Activity 獲取到的 String 帶回去?
最自然的想法是 onActivityResult,然而,播放器是一個 sdk,寫不了 Activity 里的代碼,也不可能通知許多業務方一一做改動。
那就只能拋開 android 原生的 Activity 間拉起結束中的通信機制了,思考其他可以通信的方法。很自然地,我們想到了 Callback 。結構如下圖。但是 Callback 這樣的一個非基本數據類型的對象怎么在 Activity 間傳遞呢?
嘗試通過存入 Intent 的 Extras的方式,然而 putExtra 方法并不能 put 一個 object,只能 put 一個 serializable。那就讓這個 DTO(Data Transfer Object)implements serializable 接口吧。沒有問題。
然而無法啟動 Activity,會有一個 crash 拋出:
java.lang.NullPointerException: Expected to unbox a 'int' primitive type but was returned null
報錯堆棧如下:
$Proxy1.startActivity(Unknown Source) android.app.Instrumentation.execStartActivity(Instrumentation.java:1520) android.taobao.atlas.runtime.InstrumentationHook$2$1.execStartActivity(InstrumentationHook.java:299)
如果把這個 DTO 的成員變量改為 static 類型,則可以啟動 Activity。
背后的原因是因為,在常規的序列化過程中,淺拷貝其實是沒什么意義的。淺拷貝意味著復制一個引用的地址,是一個內存地址,但是常規序列化,要么跨進程,要么就是網絡傳輸,序列化為 JSON,在這些常規場景里內存地址沒有意義。因此 Java 序列化沒有淺拷貝的選項,也往往是針對一個 POJO 或者 Bean 進行序列化,而不會對一個一般的含有很多引用的類進行序列化。
然而 Android 中的 Activity 與 Activity 間的傳遞對象又有所不同,理論上,都在同一個 Dalvik VM 中運行,相互的類引用都是可以訪問到的。但是由于 Android Intent 設計為序列化傳遞,序列化過程中沒有設計淺拷貝的機制,因此就無法淺拷貝地傳遞引用過去。
那么為什么設為 static 以后就可以傳遞,不會導致 crash 了呢?是因為靜態成員屬于類級別的,雖然不能序列化,但是因為我是在同一個機器(而且是同一個進程),我的jvm已經把這個類連帶著他的靜態變量一起加載進來了,所以獲取到的是類層面上的靜態變量地址,故,功能正常。
那么就決定是使用public static WeakReference<DWDanmakuWriteController.DanmakuWriteCallback> callback;
了。但是事實上遇到了另一個問題:
在第一次 startActivity 的時候,觀察到 Android 做了一次 GC,然后該 WeakReference 就被釋放了,因此 Callback 的業務功能也不能正常執行。引入 WeakReference,原本是為了避開 static cakllback 導致的可能的內存泄漏,然而在這種主動 GC 的情況下,WeakReference 失效了。如果改用 SoftReference,和強引用并沒有什么區別,都不能避免內存的泄漏。
最終,采用 AtomReference 來持有這個 static callback,在 Activity 退出的時機去將 AtomicReference 置空。之所以使用 AtomicReference,是因為考慮到視頻 sdk 有并發場景的可能性,避免一邊置 null 另一邊準備使用的可能。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“Android下Activity間通信序列化過程中深淺拷貝的示例分析”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。