Android下Activity間通信序列化過程中的深淺拷貝淺析
前言
問題的背景是,視頻互動業(yè)務需要增加彈幕功能,但是播放器的視圖是偽橫屏的,即,他是一種類似于使用 rotate(90.0)的方式,旋轉橫屏的,在 Activity 層面上還是一個豎屏的狀態(tài)。那么彈幕輸入的時候的鍵盤,也是豎屏的。這會帶來比較嚴重的用戶體驗問題。
由于屏幕旋轉狀態(tài)在 android 下,是一個 Activity 層面上的事情,而且相當?shù)牡讓?,無從 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;
}
那么現(xiàn)在問題來了,怎么把這個 Activity 獲取到的 String 帶回去?
最自然的想法是 onActivityResult,然而,播放器是一個 sdk,寫不了 Activity 里的代碼,也不可能通知許多業(yè)務方一一做改動。
那就只能拋開 android 原生的 Activity 間拉起結束中的通信機制了,思考其他可以通信的方法。很自然地,我們想到了 Callback 。結構如下圖。但是 Callback 這樣的一個非基本數(shù)據(jù)類型的對象怎么在 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。
背后的原因是因為,在常規(guī)的序列化過程中,淺拷貝其實是沒什么意義的。淺拷貝意味著復制一個引用的地址,是一個內存地址,但是常規(guī)序列化,要么跨進程,要么就是網(wǎng)絡傳輸,序列化為 JSON,在這些常規(guī)場景里內存地址沒有意義。因此 Java 序列化沒有淺拷貝的選項,也往往是針對一個 POJO 或者 Bean 進行序列化,而不會對一個一般的含有很多引用的類進行序列化。
然而 Android 中的 Activity 與 Activity 間的傳遞對象又有所不同,理論上,都在同一個 Dalvik VM 中運行,相互的類引用都是可以訪問到的。但是由于 Android Intent 設計為序列化傳遞,序列化過程中沒有設計淺拷貝的機制,因此就無法淺拷貝地傳遞引用過去。
那么為什么設為 static 以后就可以傳遞,不會導致 crash 了呢?是因為靜態(tài)成員屬于類級別的,雖然不能序列化,但是因為我是在同一個機器(而且是同一個進程),我的jvm已經把這個類連帶著他的靜態(tài)變量一起加載進來了,所以獲取到的是類層面上的靜態(tài)變量地址,故,功能正常。
那么就決定是使用public static WeakReference<DWDanmakuWriteController.DanmakuWriteCallback> callback;了。但是事實上遇到了另一個問題:
在第一次 startActivity 的時候,觀察到 Android 做了一次 GC,然后該 WeakReference 就被釋放了,因此 Callback 的業(yè)務功能也不能正常執(zhí)行。引入 WeakReference,原本是為了避開 static cakllback 導致的可能的內存泄漏,然而在這種主動 GC 的情況下,WeakReference 失效了。如果改用 SoftReference,和強引用并沒有什么區(qū)別,都不能避免內存的泄漏。
最終,采用 AtomReference 來持有這個 static callback,在 Activity 退出的時機去將 AtomicReference 置空。之所以使用 AtomicReference,是因為考慮到視頻 sdk 有并發(fā)場景的可能性,避免一邊置 null 另一邊準備使用的可能。
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關文章
Android原生TabLayout使用的超全解析(看這篇就夠了)
現(xiàn)在很多app都有頂部可左右切換的導航欄,并且還帶動畫效果,要實現(xiàn)這種導航欄,可以使用Android原生的Tablayout也可以借助第三方框架實現(xiàn),這篇文章主要給大家介紹了關于Android原生TabLayout使用的相關資料,需要的朋友可以參考下2022-09-09
Android 實現(xiàn)夜間模式的快速簡單方法實例詳解
這篇文章主要介紹了Android 實現(xiàn)夜間模式的快速簡單方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-09-09
Android高版本API方法如何在低版本系統(tǒng)上做兼容性處理淺析
這篇文章主要給大家介紹了關于Android高版本API方法如何在低版本系統(tǒng)上做兼容性處理的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧2018-08-08
Android學習筆記之ContentProvider和Uri詳解
本篇文章主要介紹了Android學習筆記之ContentProvider和Uri詳解,對于學習Android的朋友具有一定的參考價值,有需要可以可以了解一下。2016-11-11
android開發(fā)socket編程之udp發(fā)送實例分析
這篇文章主要介紹了android開發(fā)socket編程之udp發(fā)送,實例分析了Android開發(fā)socket網(wǎng)絡編程中udp發(fā)送的相關技巧,非常具有實用價值,需要的朋友可以參考下2015-04-04

