时间:2021-05-20
前言
问题的背景是,视频互动业务需要增加弹幕功能,但是播放器的视图是伪横屏的,即,他是一种类似于使用 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报错堆栈如下:
如果把这个 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 另一边准备使用的可能。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
Android中,Activity和Fragment之间传递对象,可以通过将对象序列化并存入Bundle或者Intent中进行传递,也可以将对象转化为JSON字
今天查阅资料,简单了解了一下Parcelable接口,它是android提供的序列化对象的接口,比java中的Serializable高效些。通过这个接口序列化
1.Java序列化与反序列化Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。2.为什么需要序列化
1、Java序列化与反序列化是什么?Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程:序列化:对象
详解Python序列化Serialize和反序列化Deserialize序列化(serialization)序列化是将对象状态转换为可保持或传输的格式的过程。与