在java有Serializable的前提下,Android为什么设计出了Parcelable?
java中的序列化方式Serializable效率比较低,主要有以下原因:
Serializable
在序列化过程中会创建大量的临时变量,这样就会造成大量的GC。Serializable
使用了大量反射,而反射操作耗时。Serializable
使用了大量的IO操作,也影响了耗时。
所以Android就像重新设计了IPC方式Binder一样,重新设计了一种序列化方式,结合Binder
的方式,对上述三点进行了优化,一定程度上提高了序列化和反序列化的效率。
Serializable、Parcelable、Json等序列化方式我们该怎么选择?
先说说序列化的用处,主要用在三个方面:
1、内存数据传输
内存传输方面,主要用Parcelable
。一是因为Parcelable
在内存传输的效率比Serializable
高。二是因为在Android中很多传输数据的方法中,自带了对于Serializable、Parcelable
类型的传输方法。比如:
- Bundle.putParcelable,
- Intent putExtra(String name, Parcelable value)
等等吧,基本上对象传输的方法都支持了,所以这也是Parcelable
的优势。
2、 数据持久化(本地存储)
如果只针对Serializable和Parcelable两种序列化方式,需要选择Serializable。
首先,Serializable
本身就是存储到二进制文件,所以用于持久化比较方便。而Parcelable
序列化是在内存中操作,如果进程关闭或者重启的时候,内存中的数据就会消失,那么Parcelable序列化用来持久化就有可能会失败,也就是数据不会连续完整。而且Parcelable还有一个问题是兼容性,每个Android版本可能内部实现都不一样,知识用于内存中也就是传递数据的话是不影响的,但是如果持久化可能就会有问题了,低版本的数据拿到高版本可能会出现兼容性问题。
但是实际情况,对于Android中的对象本地化存储,一般是以数据库、SP的方式进行保存。
3、 网络传输
而对于网络传输的情况,一般就是使用JSON
了。主要有以下几点原因:
- 1、轻量级,没有多余的数据。
- 2、与语言无关,所以能兼容所有平台语言。
- 3、易读性,易解析。
Parcelable一定比Serializable快吗?
正常情况下,对象在内存中进行传输确实是Parcelable
比较快,但是Serializable
是有缓存的概念的,有人做了一个比较有趣的实验:
当序列化一个超级大的对象图表(表示通过一个对象,拥有通过某路径能访问到其他很多的对象),并且每个对象有10个以上属性时,并且Serializable
实现了writeObject()以及readObject(),在平均每台安卓设备上,Serializable
序列化速度大于Parcelable
3.6倍,反序列化速度大于1.6倍.
具体原因就是因为Serilazable
的实现方式中,是有缓存的概念的,当一个对象被解析过后,将会缓存在HandleTable
中,当下一次解析到同一种类型的对象后,便可以向二进制流中,写入对应的缓存索引即可。但是对于Parcel来说,没有这种概念,每一次的序列化都是独立的,每一个对象,都当作一种新的对象以及新的类型的方式来处理。
具体过程可以看看这篇:https://juejin.cn/post/6854573218334769166
为什么Java提供了Serializable的序列化方式,而不是直接使用json或者xml?
我觉得是历史遗留问题。
有的人可能会想到各种理由,比如可以标记哪些类可以被序列化。又或者可以通过UID来标示反序列化为同一个对象。等等。
但是我觉得最大的问题还是历史遗留问题,在以前,json还没有成为大家认同的数据结构,所以Java就设计出了Serializable
的序列化方式来解决对象持久化和对象传输的问题。然后Java中各种API就会依赖于这种序列化方式,这么些年过去了,Java体系的庞大也造成难以改变这个问题,牵一发而动全身。
为什么我这么说呢?
主要有两点依据:
- 一是曾经Oracle Java平台组的架构师说过,删除Java的序列化机制并且提供给用户可以选择的序列化方式(比如json)是他们计划中的一部分,因为Java序列化也造成了很多Java漏洞。具体可以参见文章:https://www.infoworld.com/article/3275924/oracle-plans-to-dump-risky-java-serialization.html
- 二是因为在
Serializable
类的介绍注释中,明确说到推荐大家选择JSON 和 GSON库,因为它简洁、易读、高效。
* <h3>Recommended Alternatives</h3> * <strong>JSON</strong> is concise, human-readable and efficient. Android * includes both a {@link android.util.JsonReader streaming API} and a {@link * org.json.JSONObject tree API} to read and write JSON. Use a binding library * like <a href="http://code.google.com/p/google-gson/">GSON</a> to read and * write Java objects directly.
Window是什么
窗口。你可以理解为手机上的整个画面,所有的视图都是通过Window呈现的,比如Activity、dialog都是附加在Window上的。Window类的唯一实现是PhoneWindow,这个名字就更加好记了吧,手机窗口呗。
那Window到底在哪里呢?我们看到的View是Window吗?是也不是。
如果说的只是Window概念的话,那可以说是的,View就是Window的存在形式,Window管理着View。
如果说是Window类的话,那确实不是View,唯一实现类PhoneWindow管理着当前界面上的View,包括根布局——DecorView,和其他子view的添加删除等等。
不知道你晕没有,我总结下,Window是个概念性的东西,你看不到他,如果你能感知它的存在,那么就是通过View,所以View是Window的存在形式,有了View,你才感知到View外层有一个皇帝的新衣
——window。
WindowManager是什么?和WMS的关系?
WindowManager就是用来管理Window的,实现类为WindowManagerImpl,实际工作会委托给WindowManagerGlobal类中完成。
而具体的Window操作,WM会通过Binder告诉WMS,WMS做最后的真正操作Window的工作,会为这个Window分配Surface,并绘制到屏幕上。
怎么添加一个Window?
var windowParams: WindowManager.LayoutParams = WindowManager.LayoutParams() windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE windowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG var btn = Button(this) windowManager.addView(btn, windowParams)
简单贴了下代码,加了一个Button。
有的朋友可能会疑惑了,这明明是个Button,是个View啊,咋成了Window?
刚才说过了,View是Window的表现形式,在实际实现中,添加window其实就是添加了一个你看不到的window,并且里面有View才能让你感觉得到这个是一个Window。
所以通过windowManager添加的View其实就是添加Window的过程。
这其中还有两个比较重要的属性:flags和type,下面会依次说到。
Window怎样可以显示到锁屏界面
Window的flag可以控制Window的显示特性,也就是该怎么显示、touch事件处理、与设备的关系、等等。所以这里问的锁屏界面显示也是其中的一种Flag。
// Window不需要获取焦点,也不接受各种输入事件。 public static final int FLAG_NOT_FOCUSABLE = 0x00000008; // @deprecated Use {@link android.R.attr#showWhenLocked} or // {@link android.app.Activity#setShowWhenLocked(boolean)} instead to prevent an // unintentional double life-cycle event. // 窗口可以在锁屏的 Window 之上显示 public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
Window三种类型都存在的情况下,显示层级是怎样。
Type表示Window的类型,一共三种:
- 应用Window。对应着一个Activity,Window层级为1~99,在视图最下层。
- 子Window。不能单独存在,需要附属在特定的父Window之中(如Dialog就是子Window),Window层级为1000~1999。
- 系统Window。需要声明权限才能创建的Window,比如Toast和系统状态栏,Window层级为2000-2999,处在视图最上层。
可以看到,区别就是有个Window层级(z-ordered),层级高的能覆盖住层级低的,离用户更近。
Window就是指PhoneWindow吗?
如果有人问我这个问题,我肯定心里要大大的疑惑了🤔。
可不就是PhoneWindow吗?都唯一实现类了,净问些奇怪问题。
但是面试的时候遇到这种问题总要答啊?这时候就要扯出Window的概念了。
如果指的Window类,那么PhoneWindow作为唯一实现类,一般指的就是PhoneWindow。
如果指的Window这个概念,那肯定不是指PhoneWindow,而是存在于界面上真实的View。当然也不是所有的View都是Window,而是通过WindowManager添加到屏幕的view才是Window,所以PopupWindow是Window,上述问题中添加的单个View也是Window。
PhoneWindow什么时候被创建的?
熟悉Activity启动流程的朋友应该知道,启动过程会执行到ActivityThread的handleLaunchActivity方法,这里初始化了WindowManagerGlobal,也就是WindowManager实际操作Window的类,待会会看到:
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) { //... WindowManagerGlobal.initialize(); //... final Activity a = performLaunchActivity(r, customIntent); //... return a; }
然后会执行到performLaunchActivity中创建Activity,并调用attach方法进行一些数据的初始化(伪代码):
final void attach() { //初始化PhoneWindow mWindow = new PhoneWindow(this, window, activityConfigCallback); mWindow.setWindowControllerCallback(mWindowControllerCallback); mWindow.setCallback(this); //和WindowManager关联 mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); mWindowManager = mWindow.getWindowManager(); }
可以看到,在Activity的attach方法中,创建了PhoneWindow,并且设置了callback,windowManager。
这里的callback待会会说到,跟事件分发有关系,可以说是当前Activity和PhoneWindow建立联系。