国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > Android > 正文

Android下Activity間通信序列化過(guò)程中的深淺拷貝淺析

2019-12-12 01:52:22
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

前言

問(wèn)題的背景是,視頻互動(dòng)業(yè)務(wù)需要增加彈幕功能,但是播放器的視圖是偽橫屏的,即,他是一種類(lèi)似于使用 rotate(90.0)的方式,旋轉(zhuǎn)橫屏的,在 Activity 層面上還是一個(gè)豎屏的狀態(tài)。那么彈幕輸入的時(shí)候的鍵盤(pán),也是豎屏的。這會(huì)帶來(lái)比較嚴(yán)重的用戶(hù)體驗(yàn)問(wèn)題。

由于屏幕旋轉(zhuǎn)狀態(tài)在 android 下,是一個(gè) Activity 層面上的事情,而且相當(dāng)?shù)牡讓樱瑹o(wú)從 hook,多方調(diào)研以后,決定采拉起一個(gè)橫屏的 Activity 作為鍵盤(pán)輸入的專(zhuān)用 Activity。

這里的代碼很快就可以寫(xiě)好,如下所示:

/** * 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)在問(wèn)題來(lái)了,怎么把這個(gè) Activity 獲取到的 String 帶回去?

最自然的想法是 onActivityResult,然而,播放器是一個(gè) sdk,寫(xiě)不了 Activity 里的代碼,也不可能通知許多業(yè)務(wù)方一一做改動(dòng)。

那就只能拋開(kāi) android 原生的 Activity 間拉起結(jié)束中的通信機(jī)制了,思考其他可以通信的方法。很自然地,我們想到了 Callback 。結(jié)構(gòu)如下圖。但是 Callback 這樣的一個(gè)非基本數(shù)據(jù)類(lèi)型的對(duì)象怎么在 Activity 間傳遞呢?


嘗試通過(guò)存入 Intent 的 Extras的方式,然而 putExtra 方法并不能 put 一個(gè) object,只能 put 一個(gè) serializable。那就讓這個(gè) DTO(Data Transfer Object)implements serializable 接口吧。沒(méi)有問(wèn)題。

然而無(wú)法啟動(dòng) Activity,會(huì)有一個(gè) crash 拋出:

java.lang.NullPointerException: Expected to unbox a 'int' primitive type but was returned null

報(bào)錯(cuò)堆棧如下:

$Proxy1.startActivity(Unknown Source)android.app.Instrumentation.execStartActivity(Instrumentation.java:1520)android.taobao.atlas.runtime.InstrumentationHook$2$1.execStartActivity(InstrumentationHook.java:299)

如果把這個(gè) DTO 的成員變量改為 static 類(lèi)型,則可以啟動(dòng) Activity。

背后的原因是因?yàn)椋诔R?guī)的序列化過(guò)程中,淺拷貝其實(shí)是沒(méi)什么意義的。淺拷貝意味著復(fù)制一個(gè)引用的地址,是一個(gè)內(nèi)存地址,但是常規(guī)序列化,要么跨進(jìn)程,要么就是網(wǎng)絡(luò)傳輸,序列化為 JSON,在這些常規(guī)場(chǎng)景里內(nèi)存地址沒(méi)有意義。因此 Java 序列化沒(méi)有淺拷貝的選項(xiàng),也往往是針對(duì)一個(gè) POJO 或者 Bean 進(jìn)行序列化,而不會(huì)對(duì)一個(gè)一般的含有很多引用的類(lèi)進(jìn)行序列化。

然而 Android 中的 Activity 與 Activity 間的傳遞對(duì)象又有所不同,理論上,都在同一個(gè) Dalvik VM 中運(yùn)行,相互的類(lèi)引用都是可以訪問(wèn)到的。但是由于 Android Intent 設(shè)計(jì)為序列化傳遞,序列化過(guò)程中沒(méi)有設(shè)計(jì)淺拷貝的機(jī)制,因此就無(wú)法淺拷貝地傳遞引用過(guò)去。

那么為什么設(shè)為 static 以后就可以傳遞,不會(huì)導(dǎo)致 crash 了呢?是因?yàn)殪o態(tài)成員屬于類(lèi)級(jí)別的,雖然不能序列化,但是因?yàn)槲沂窃谕粋€(gè)機(jī)器(而且是同一個(gè)進(jìn)程),我的jvm已經(jīng)把這個(gè)類(lèi)連帶著他的靜態(tài)變量一起加載進(jìn)來(lái)了,所以獲取到的是類(lèi)層面上的靜態(tài)變量地址,故,功能正常。

那么就決定是使用public static WeakReference<DWDanmakuWriteController.DanmakuWriteCallback> callback;了。但是事實(shí)上遇到了另一個(gè)問(wèn)題:

在第一次 startActivity 的時(shí)候,觀察到 Android 做了一次 GC,然后該 WeakReference 就被釋放了,因此 Callback 的業(yè)務(wù)功能也不能正常執(zhí)行。引入 WeakReference,原本是為了避開(kāi) static cakllback 導(dǎo)致的可能的內(nèi)存泄漏,然而在這種主動(dòng) GC 的情況下,WeakReference 失效了。如果改用 SoftReference,和強(qiáng)引用并沒(méi)有什么區(qū)別,都不能避免內(nèi)存的泄漏。

最終,采用 AtomReference 來(lái)持有這個(gè) static callback,在 Activity 退出的時(shí)機(jī)去將 AtomicReference 置空。之所以使用 AtomicReference,是因?yàn)榭紤]到視頻 sdk 有并發(fā)場(chǎng)景的可能性,避免一邊置 null 另一邊準(zhǔn)備使用的可能。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)武林網(wǎng)的支持。

發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 利辛县| 博客| 太仆寺旗| 泾阳县| 海淀区| 东乌| 蚌埠市| 苏尼特右旗| 司法| 卢龙县| 苍溪县| 阿巴嘎旗| 房产| 神池县| 西宁市| 榆中县| 垣曲县| 扶风县| 泸溪县| 崇义县| 启东市| 明溪县| 安新县| 太和县| 永清县| 民县| 大关县| 定远县| 衡山县| 蓝田县| 平凉市| 井陉县| 孝义市| 通道| 岑溪市| 闽清县| 榕江县| 西平县| 唐山市| 万宁市| 兰州市|