android
2022-09-07 19:15:09 114 举报
AI智能生成
Android是一个开源的移动操作系统,主要用于智能手机和平板电脑。它由Google开发并维护,基于Linux内核。Android系统以其丰富的应用程序、灵活的用户界面和良好的兼容性而受到广泛欢迎。Android操作系统具有开放性,允许开发者自由地定制和优化设备功能。此外,Android还提供了一套完整的软件开发工具包(SDK),使开发者能够轻松地为Android设备创建应用程序。总之,Android是一个功能强大、易于使用且高度可定制的移动操作系统,为全球数亿用户提供了便捷的智能设备体验。
作者其他创作
大纲/内容
动画
帧动画
帧动画是顺序播放一组预先定义好的图片,使用简单,但容易引起OOM,所以在使用帧动画时应尽量避免使用过多尺寸较大的图片。系统提供了另一个类 AnimationDrawble 来使用帧动画,使用的时候,需要通过 XML 定义一个 AnimationDrawble
'''
''
补间动画
动效
平移动画|<translate>|TranslateAnimation|移动View
缩放动画|<scale>|ScaleAnimation|放大或缩小View
旋转动画|<rotate>|RotateAnimation|旋转View
透明度动画|<alpha>|AlphaAnimation|改变View的透明度
setAnimationListener 给 View 动画添加过程监听,(开始、结束、重复)状态
作用对象是View
属性动画
属性动画可以对任何对象做动画,甚至还可以没有对象。可以在一个时间间隔内完成对象从一个属性值到另一个属性值的改变。==与View动画相比,属性动画几乎无所不能,只要对象有这个属性,它都能实现动画效果
属性动画有ValueAnimator、ObjectAnimator和AnimatorSet等概念。其中ObjectAnimator继承自ValueAnimator,AnimatorSet是动画集合
理解差值器和估值器
时间插值器( TimeInterpolator) 的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有LinearInterpolator( 线性插值器:匀速动画) ,AccelerateDecelerateInterpolator( 加速减速插值器:动画两头慢中间快) ,DecelerateInterpolator(减速插值器:动画越来越慢)
估值器(类型估值算法, TypeEvaluator) 的作用是根据当前属性改变的百分比来计算改变后的属性值。系统预置有IntEvaluator(针对整型属性) 、FloatEvaluator(浮点型属性) 、ArgbEvaluator(针对 Color 属性)
对任意属性做动画
优化
内存优化
内存泄漏
1.单例持有Activity的强引用->把传入的Context改为同应用生命周期一样长的Application中的Context
2.Handler引起的内存泄漏->将Handler声明为静态内部类
3.匿名内部类在异步线程中的使用
4 static修饰成员变量,被 Static 关键字修饰的成员变量的生命周期 = 应用程序的生命周期,则容易出现该成员变量的生命周期 > 引用实例生命周期的情况,当引用实例需结束生命周期销毁时,会因静态变量的持有而无法被回收,从而出现内存泄露
5. 避免使用非静态内部类和匿名类
6 集合引发的内存泄漏,添加元素后,仍引用着 集合元素对象,导致该集合元素对象不可被回收,从而 导致内存泄漏
7. webView引发的内存泄漏
其他
构造adapter么有使用缓存的convertview
bitmap在不使用的时候没有使用recycle()释放内存
非静态内部类的静态实例容易造成内存泄漏
线程未终止造成的内存泄漏
对象的注册与反注册没有成对出现造成的内存泄漏;如:注册广播接收器,注册观察者
创建与关闭没有成对出现造成的内存泄漏;如:Cursor资源必须手动关闭,webview必须手动销毁,io流对象必须手动关闭等
不要在执行频率很高的方法或者循环中创建对象(比如onMeasure)
避免代码设计模式的错误造成内存泄露;譬如循环引用,A持有B,B持有C,C持有A
OOM
1.内部类请使用static,因为非静态内部类默认持有外部类的引用,比如在Activity里面直接放一个自定义的Adapter
2.静态类(比如Application,单例类,其他static类)请不要持有Activity引用,因为静态类生命周期比Activity长。解决办法:在需要的地方用BaseApplication.getTopActivity。或者Activity作为弱引用传入。
3.注意Handler会默认持有当前Activity,用的时候最好不要直接new Handler().post(new Runnable...),除非你确定这个runnable会在Activity销毁前执行完
4.图片压缩
缓存机制
内存缓存,读取速度最快
硬盘缓存(文件缓存),读取速度比内存速度慢
网络缓存,读取速度最慢
压缩
质量压缩
在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的:
采样率压缩
缩放法压缩,效果和方法2一样
加载大图
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// 第一次解析将inJustDecodeBounds设置为true,禁止为bitmap分配内存,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//int imageHeight = options.outHeight;
//int imageWidth = options.outWidth;
//String imageType = options.outMimeType
BitmapFactory.decodeResource(res, resId, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.RGB_565;//
return BitmapFactory.decodeResource(res, resId, options);
}
int reqWidth, int reqHeight) {
// 第一次解析将inJustDecodeBounds设置为true,禁止为bitmap分配内存,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//int imageHeight = options.outHeight;
//int imageWidth = options.outWidth;
//String imageType = options.outMimeType
BitmapFactory.decodeResource(res, resId, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.RGB_565;//
return BitmapFactory.decodeResource(res, resId, options);
}
LruCache
LruCache是个泛型类,内部采用LinkedHashMap来实现缓存机制,它提供get方法和put方法来获取缓存和添加缓存,其最重要的方法trimToSize是用来移除最少使用的缓存和使用最久的缓存,并添加最新的缓存到队列中。
dump,nemory,mat
UI卡顿,优化
原因
1.在UI线程中做轻微耗时操作,导致UI线程卡顿
2.布局Layout过于复杂,无法在16ms内完成渲染
6.频繁触发GC,导致暂时阻塞渲染操作
7.复杂运算
8.ANR
优化
过度绘制
布局视图扁平化
移除嵌套布局
使用merge,viewstub,include标签
使用性能消耗更小的布局ConstraintLayout
RecycleView/Listview 使用Viewholder复用item
去除不必要的背景色
UI线程的复杂运算运算
可以使用TraceView工具分析
频繁GC
原因
1.内存抖动,大量的对象被创建又在短时间内马上释放
2.瞬间产生大量的对象会严重占用Young Generation的内存区域,当达到阀值,会触发GC
解决方案:不要在循环中大量使用局部变量
耗时操作处理
Traceview ,BlockCannery,data/anr/traces.txt
冷启动优化
避免启动白屏
webview首屏优化 离线包
apk瘦身
去除冗余资源
图片替换或压缩
缩小jar包
lint
混淆
混淆方式
子主题
子主题
流量、电量优化
动态化
插件化
什么是插件化
App 的部分功能模块在打包时并不以传统⽅式打包进 apk ⽂件中,⽽是以另⼀种形式⼆次封装进 apk
内部,或者放在⽹络上适时下载,在需要的时候动态对这些功能模块进⾏加载,称之为插件化。
这些单独⼆次封装的功能模块 apk ,就称作「插件」,初始安装的 apk 称作「宿主」。
插件化是组件化的更进⼀步推进。
内部,或者放在⽹络上适时下载,在需要的时候动态对这些功能模块进⾏加载,称之为插件化。
这些单独⼆次封装的功能模块 apk ,就称作「插件」,初始安装的 apk 称作「宿主」。
插件化是组件化的更进⼀步推进。
作用
早期:解决 dex 65535 问题。⾕歌后来也出了 multidex ⼯具来专⻔解决
减⼩安装包⼤⼩
动态部署
bug热修复
插件化基础->反射
关于dex
class:java 编译后的⽂件,每个类对应⼀个 class ⽂件
dex:Dalvik EXecutable 把 class 打包在⼀起,⼀个 dex 可以包含多个 class ⽂件
odex:Optimized DEX 针对系统的优化,例如某个⽅法的调⽤指令,会把虚拟的调⽤转换为使
⽤具体的 index,这样在执⾏的时候就不⽤再查找了
⽤具体的 index,这样在执⾏的时候就不⽤再查找了
oat:Optimized Android file Type(预编译)。使⽤ AOT 策略对 dex 预先编译(解释)成本地指令,这样
再运⾏阶段就不需再经历⼀次解释过程,程序的运⾏可以更快
再运⾏阶段就不需再经历⼀次解释过程,程序的运⾏可以更快
核心问题:
类加载
DexClassLoader的构造函数比PathClassLoader多了一个,optimizedDirectory参数,这个是用来指定dex的优化产物odex的路径,在源码注释中,指出这个参数从API 26后就弃用了
PathClassLoader主要用来加载系统类和应用程序的类,在ART虚拟机上可以加载未安装的apk的dex,在Dalvik则不行
DexClassLoader用来加载未安装apk的dex
资源加载
Android系统通过Resource对象加载资源,因此只需要添加资源(即apk文件)所在路径到AssetManager中,即可实现对插件资源的访问。
组件生命周期管理
Activity的启动过程
Step1.Activity1调用startActivity,实际会调用Instrumentation(仪表盘 )类的execStartActivity方法,Instrumentation是系统用来监控Activity运行的一个类,Activity的整个生命周期都有它的影子
Step2.通过跨进程的binder调用,进入到ActivityManagerService(AMS)中,其内部会处理Activity栈。之后又通过跨进程调用进入到Activity2所在的进程中
Step3.ApplicationThread是一个binder对象,其运行在binder线程池中,内部包含一个H类,该类继承于Handler。ApplicationThread将启动Activity2的信息通过H对象发送给主线程
Step4.主线程拿到Activity2的信息后,调用Instrumentation类的newAcitivity方法,其内部通过ClassLoader创建Activity2实例。
加载插件中的类
处理插件Activity的启动
Step1.在宿主工程的AndroidManifest.xml中预先注册Activity进行占坑
Step2.使用占坑Activity绕过AMS验证。
Step3. 还原插件Activity
Step4. 在Application中hook Instrumentation。
子主题
热更新
热更新和插件化区别
1.插件化的内容在元App中没有,而热更新是在元App中的内容做了改动
2.插件化在代码中有固定的入口,而热更新则可能改变任何一个位置的代码
原理
1.ClassLoader的dex文件替换
2.直接修改字节码
前置知识:loadClass() 的类加载过程
宏观上:是⼀个带缓存的、从上到下的加载过程(即⽹上所说的「双亲委托机制」)
对于具体的⼀个 ClassLoader:
先从自己的缓存中取
自己没有缓存,就找ClassLoader要(parent.loadClass())
⽗ View 也没有,就⾃⼰加载(findClass())
BaseDexClassLoader 或者它的⼦类
(DexClassLoader、PathClassLoader 等)的
findClass():
(DexClassLoader、PathClassLoader 等)的
findClass():
通过它的 pathList.findClass()
它的 pathList.loadClass() 通过 DexPathList 的 dexElements 的 findClass()
所以热更新的关键在于,把补丁 dex ⽂件加载放进⼀个 Element,并且插⼊到
dexElements 这个数组的前⾯(插⼊到后⾯的话会被忽略掉)
dexElements 这个数组的前⾯(插⼊到后⾯的话会被忽略掉)
RN/Fluter
架构
组件化
页面路由
ARouter
公共资源
mvp
mvvm,livedata
其他
wms 、ams
app,activity启动流程
app打包流程
屏幕适配
子主题
子主题
子主题
apt
aop
四大组件
Activity(Context)
生命周期
onCreate()
onStart()
onResume()
onPause()
onStop()
onDestory()
启动模式
standard
singleTop
singleTask
singleInstance
Service(Context)
生命周期
绑定模式
第一次 bindService()的时候,执行的方法为 onCreate()、onBind()解除绑定的时候会执行
onUnbind()、onDestory(
onUnbind()、onDestory(
非绑定模式
当第一次调用 startService 的时候执行的方法依次为 onCreate()、onStartCommand(),(onStart())
当 Service 关闭的时候调用 onDestory 方法
当 Service 关闭的时候调用 onDestory 方法
启动方式
BroastReciver
生命周期
广播接收者的生命周期非常短暂的,在接收到广播的时候创建,onReceive()方法结束之后销毁;
注册方式
动态注册
静态注册
类型
有序广播::按照被接收者的优先级顺序,在被接收者中依次传播,每个对象有权终止广播
无序广播::完全异步,逻辑上可以被任何广播接收者接收到。优点是效率较高。缺点是一个接收者不能将处理结
果传递给下一个接收者,并无法终止广播 intent 的传播
果传递给下一个接收者,并无法终止广播 intent 的传播
接收方式,有序,无序,广播类型,粘性,非粘性
内部通信实现机制:通过 Android 系统的 Binder
ContentProvide
描述:ContentProvider 是应用程序之间共享数据的接口。使用的时候首先自定义一个类继承 ContentProvider,然后
覆写 query、insert、update、delete 等方法。提供给第三发应用,进程间通讯,
覆写 query、insert、update、delete 等方法。提供给第三发应用,进程间通讯,
说说 ContentProvider、ContentResolver、ContentObserver 之间的关系
ContentProvider 内容提供者,用于对外提供数据
ContentResolver.notifyChange(uri)发出消息
ContentResolver 内容解析者,用于获取内容提供者提供的数据
ContentObserver 内容监听器,可以监听数据的改变状态
ContentResolver.registerContentObserver()监听消息。
常用控件
Recycleview
4级缓存+局部刷新
与listview比较
Webview
JSBridge、deeplink、首屏加速,离线包
解决内存泄漏
独立进程,简单暴力,但是可能涉及到进程间通讯
动态添加webview
对传入webview中使用的Context使用弱引用
在布局创建ViewGroup用力啊放置WebView
Activity创建时添加add进来,在Activity停止时remove掉
Window
Toast,Dialog,PopupWindow
View
自定义
继承已有的view
1.重写onMeasure()
2.用getMeasuredWidth()和getMearsureHeight()获取测量到的尺寸
3.计算出最终要的尺寸
4.setMeasuredDimension(w,h)把结果保存
完全自定义view
1.重写onMeasure()
2.计算出自己的尺寸 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
3.用resolveSize()或resolvesizeAndState()修正结果
⾸先⽤ MeasureSpec.getMode(measureSpec) 和
MeasureSpec.getSize(measureSpec) 取出⽗ 对⾃⼰的尺⼨限制类型和具体限制
尺⼨
MeasureSpec.getSize(measureSpec) 取出⽗ 对⾃⼰的尺⼨限制类型和具体限制
尺⼨
如果 measure spec 的 mode 是 EXACTLY,表示⽗ View 对⼦ View 的尺⼨做出
了精确限制,所以就放弃计算出的 size,直接选⽤ measure spec 的 size;
了精确限制,所以就放弃计算出的 size,直接选⽤ measure spec 的 size;
如果 measure spec 的 mode 是 AT_MOST,对应View的默认大小,表示⽗ View 对⼦ View 的尺⼨只限
制了上限,需要看情况
制了上限,需要看情况
如果计算出的 size 不⼤于 spec 中限制的 size,表示尺⼨没有超出限制,所
以选⽤计算出的 size
以选⽤计算出的 size
⽽如果计算出的 size ⼤于 spec 中限制的 size,表示尺⼨超限了,所以选⽤
spec 的 size,并且在 resolveSizeAndState() 中会添加标志
MEASURED_STATE_TOO_SMALL(这个标志可以辅助⽗ View 做测量和布
局的计算
spec 的 size,并且在 resolveSizeAndState() 中会添加标志
MEASURED_STATE_TOO_SMALL(这个标志可以辅助⽗ View 做测量和布
局的计算
如果 measure spec 的 mode 是 UNSPECIFIED,父容器不对View进行任何限制,要多大给多大,一般用于系统内部
4.setMeasuredDimension(w,h)把结果保存
自定义layout
重写 onMeasure()
1. 遍历每个⼦ View,⽤ measureChildWidthMargins() 测量⼦ View
需要重写 generateLayoutParams() 并返回 MarginLayoutParams 才能使⽤
measureChildWithMargins() ⽅法
measureChildWithMargins() ⽅法
需要看下measureChildWidthMargins()内部实现
有些⼦ View 可能需要重新测量(⽐如换⾏处)
测量完成后,得出⼦ View 的实际位置和尺⼨,并暂时保存
子主题
测量出所有⼦ View 的位置和尺⼨后,计算出⾃⼰的尺⼨,并⽤
setMeasuredDimension(width, height) 保存
setMeasuredDimension(width, height) 保存
重写 onLayout()
绘制
onMeure()->onLayout->onDraw(canvas.draw())
reqestLayout()与invalidate()区别
事件分发
Activity
View
ViewGroup
滑动冲突
背景
在界面中,只要内外两层同时可以滑动,这个时候就会产生滑动冲突。滑动冲突的解决有模板方法
场景
1. 外部滑动和内部滑动方向不一致
2.外部滑动方向和内部滑动方向一致
3. 上面两种情况的嵌套
处理规则
场景一,处理的规则是:当用户左右( 上下) 滑动时,需要让外部的View拦截点击事件,当用户上下( 左右) 滑动的时候,需要让内部的View拦截点击事件。根据滑动的方向判断谁来拦截事件
场景二,由于滑动方向一致,这时候只能在业务上找到突破点,根据业务需求,规定什么时候让外部View拦截事件
场景三,情况相对比较复杂,同样根据需求在业务上找到突破点
解决方式
1.外部拦截法
所谓外部拦截法是指点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,否则就不拦截。下面是伪代码:
针对不同冲突,只需修改父容器需要当前事件的条件即可。其他不需修改也不能修改。
针对不同冲突,只需修改父容器需要当前事件的条件即可。其他不需修改也不能修改。
- ACTION_DOWN:必须返回false。因为如果返回true,后续事件都会被拦截,无法传递给子View。
- ACTION_MOVE:根据需要决定是否拦截
- ACTIONUP:必须返回false。如果拦截,那么子View无法接受up事件,无法完成click操作。而如果是父容器需要该事件,那么在ACTIONMOVE时已经进行了拦截,ACTION_UP不会经过onInterceptTouchEvent方法,直接交给父容器处理。
2.内部拦截法
1.内部拦截法是指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗,否则就交由父容器进行处理。这种方法与Android事件分发机制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作。
2.除了子元素需要做处理外,父元素也要默认拦截除了ACTION_DOWN以外的其他事件,这样当子元素调用parent.requestDisallowInterceptTouchEvent(false)方法时,父元素才能继续拦截所需的事件。因此,父元素要做以下修改
通讯机制
线程通讯
子线程->UI线程 Handler.post()、View.post()、Activity.runOnUIThread()
UI线程->子线程 Looper.prepare()+Looper.loop()或HandleThread
Handler
角色
message : 系统生成的消息
messageQueue:消息队列,其重要功能是对队列中的消息进行入队messageQueue.enqueueMessage())和出队-
(messageQueue.next())操作
(messageQueue.next())操作
handler: 消息辅助类,主要功能是向消息队列中发送各种事件消息和接受消息并处理消息
looper:不断循环执行消息出队操作(looper.loop()),按分发机制将消息分发给相应的目标handler进行处理
问题
系统怎么知道将消息分发给哪个handler?
Handler发送消息时最终调用的enqueueMessage中会给发送的Message的target变量赋值为当前的handler(也就是发送消息的handler),然后系统会根据target来匹配接受消息的的handler对象是哪
在loop的循环体中调用Message msg = queue.next() 可能会造成阻塞,
那我们平时在使用的时候为何又没有造成阻塞呢?
那我们平时在使用的时候为何又没有造成阻塞呢?
1.造成ANR的原因
子主当前的事件没有机会得到处理(即主线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)题
当前的事件正在处理,但没有及时完成
2.ActivityThread中main方法
首先 ActivityThread 并不是一个 Thread,就只是一个 final 类而已。我们常说的主线程就是从这个类的 main 方法开始,main 方法很简短
根据ActivityThread的源码可以看出,在应用程序启动的时候在ActivityThread的main方法中手动调用了Looper.preper()、Looper.loop()
根据ActivityThread的源码可以看出,在应用程序启动的时候在ActivityThread的main方法中手动调用了Looper.preper()、Looper.loop()
3.Looper.loop()方法无限循环
4.处理消息handleMessage方法
- 可以看见Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施。
- 如果某个消息处理时间过长,比如你在onCreate(),onResume()里面处理耗时操作,那么下一次的消息比如用户的点击事件不能处理了,整个循环就会产生卡顿,时间一长就成了ANR
在UI线程中并没有显式调用preper()、loop()等方法为何没有抛出异常?
子线程创建handler步骤
1.调用Looper.praper()
2.创建Handler对象
3.调用Looper.loop()
作用
接收、发送消息
原理
如何创建looper,如何获取looper
子主题
子主题
内存泄漏和解决办法
原因:静态内部类持有外部类的匿名引用,导致外部 activity无法释放
解决:handler噶为静态内部类,然后mHandler.removeCallback()
AyancTask
本质:封装了线程池和handler的异步框架
使用:
机制原理:
1.本质是一个静态的线程池,派生出的子类可以实现不同行=的异步任务,这些任务都是提交到静态的线程池中执行
2.线程池中的工作现场执行doInBackground(mParams)方法执行异步任务
3.当任务状态改变后,工作线程会向ui线程发送消息,AsyncTask内部的InternalHandler响应这小消息,并调用先关的回调函数
注意事项
内存泄漏
生命周期
结果丢失
handlerThread
产生背景
开启Thread子线程进行耗时操作,多次创建和销毁线程很消耗系统资源
作用:在工作线程中执行任务,如:耗时任务
本质
是一个线程类,继承了Thread
特点
有自己的内部Looper对象,可以进行looper循环
通过HandlerThread对象传递给Handler对象,可以再handlerMessage中执行异步任务
优点不会有堵塞,减少了对性能的消耗,缺点是不能同时进行多任务的处理,需要等待进行处理,处理效率较低
与线程池重并发不同,HandlerTheread是一个串行队列,背后只有一个线程
子主题
进程通讯
Binder
AIDL
同进程:asInterface()->强转->直接调用
跨进程:asInterface->Proxy(mRote.transact())-
数据序列化
Seriailzable
1.Serializable 是java的序列化技术,最简单的使用方式为在需要序列化的class增加implements Serializable,并增加一个唯一个序列化id: private static final long serialVersionUID = 1L; 默认方式最好直接设置为1L,因为java sdk会自动进行hash计算,并生成唯一的UID值。手动设置serialVersionUID的好处是当前class如果改变了成员变量,比如增加或者删除之后,这个UID是不改变的,那么反序列化就不会失败;自动设置则在改变了成员变量之后就会重新计算获得新的UID,从而导致失败。不过,大多数情况下两者都可以。
2.Seralizable相对Parcelable而言,好处就是非常简单,只需对需要序列化的类class执行就可以,不需要手动去处理序列化和反序列化的过程,所以常常用于网络请求数据处理,Activity之间传递值的使用。
3.Seralizable无法序列化静态变量,使用transient修饰的对象也无法序列化。
4.当一个父类实现序列化,子类自动实现序列化,不需要再显示实现Serializable接口。
2.Seralizable相对Parcelable而言,好处就是非常简单,只需对需要序列化的类class执行就可以,不需要手动去处理序列化和反序列化的过程,所以常常用于网络请求数据处理,Activity之间传递值的使用。
3.Seralizable无法序列化静态变量,使用transient修饰的对象也无法序列化。
4.当一个父类实现序列化,子类自动实现序列化,不需要再显示实现Serializable接口。
Parcelable
特点
1.Parcelable是android特有的序列化API,它的出现是为了解决Serializable在序列化的过程中消耗资源严重的问题,但是因为本身使用需要手动处理序列化和反序列化过程,会与具体的代码绑定,使用较为繁琐,一般只获取内存数据的时候使用
2.而Parcelable依赖于Parcel,Parcel的意思是包装,实现原理是在内存中建立一块共享数据块,序列化和反序列化均是操作这一块的数据,如此来实现
3.Parcelable的三个过程:序列化、反序列化和描述
writeXX
readXX
子主题
子主题
Serializable 和Parcelable 的区别
1.作用
Serializable的作用是为了保存对象的属性到本地文件、数据库、网络流、RMI(Remote Method Invocation)以方便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。使用了反射技术,并且期间产生临时对象
Android的Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。
Android的Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。
2.效率及选择
Parcelable的性能比Serializable好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable,如activity间传输数据,而Serializable可将数据持久化方便保存,所以在需要保存或网络传输数据时选择Serializable,因为android不同版本Parcelable可能不同,所以不推荐使用Parcelable进行数据持久化
3.高级功能上
Serializable序列化不保存静态变量,可以使用transient关键字对部分字段不进行序列化,也可以覆盖writeObject、readObject方法以实现序列化过程自定义
如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。换句话说,用transient关键字标记的成员变量不参与序列化过程。
如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。换句话说,用transient关键字标记的成员变量不参与序列化过程。
4.编程实现
四大组件、Messenger、Socket
binder
消息推送&保活
进程保活
长连接保活
心跳包
提示用户加入白名单,针对各种机型展示不同的引导页
0 条评论
下一页