裸辞-疫情-闭关-复习-大厂offer(一)(中)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 裸辞-疫情-闭关-复习-大厂offer(一)

recyclerview


  1. Recycler有4个层次用于缓存ViewHolder对象,优先级从高到底依次为ArrayList mAttachedScrapArrayList mCachedViewsViewCacheExtension mViewCacheExtensionRecycledViewPool mRecyclerPool。如果四层缓存都未命中,则重新创建并绑定ViewHolder对象


  1. 缓存性能:


缓存 重新创建ViewHolder 重新绑定数据
mAttachedScrap false false
mCachedViews false false
mRecyclerPool false true


  1. 缓存容量:


  • mAttachedScrap:没有大小限制,但最多包含屏幕可见表项。


  • mCachedViews:默认大小限制为2,放不下时,按照先进先出原则将最先进入的ViewHolder存入回收池以腾出空间。


  • mRecyclerPool:对ViewHolderviewType分类存储(通过SparseArray),同类ViewHolder存储在默认大小为5的ArrayList中。


  1. 缓存用途:


  • mAttachedScrap:用于布局过程中屏幕可见表项的回收和复用。


  • mCachedViews:用于移出屏幕表项的回收和复用,且只能用于指定位置的表项,有点像“回收池预备队列”,即总是先回收到mCachedViews,当它放不下的时候,按照先进先出原则将最先进入的ViewHolder存入回收池。


  • mRecyclerPool:用于移出屏幕表项的回收和复用,且只能用于指定viewType的表项


  1. 缓存结构:


  • mAttachedScrap:ArrayList


  • mCachedViews:ArrayList


  • mRecyclerPool:对ViewHolder按viewType分类存储在SparseArray中,同类ViewHolder存储在ScrapData中的ArrayList中


ItemDecoration


  • 用于绘制ItemView以外的内容,有两个回调onDraw和onDrawOver,区别是绘制的顺序有先后,onDraw()会在RecyclerView.onDraw()中调用,表示绘制RecyclerView自身的内容,会在绘制孩子之前,所以出现在孩子下面,而onDrawOver() 是在 RecyclerView.draw()中调用,在绘制孩子之后调用,所以会出现在孩子上方


view生命周期


构造View --> onFinishInflate --> onAttachedToWindow --> onMeasure --> onSizeChanged --> onLayout --> onDraw --> onDetachedFromWindow


Bitmap


  • raw和drawable和sdcard,这些不带dpi的文件夹中的图片被解析时不会进行缩放(inDensity默认为160)


  • 获取bitmap宽高:若inJustDecodeBounds为true,则不会把bitmap图片的像素加载到内存(实际是在Native层解码了图片,但是没有生成Java层的Bitmap),只是获取该bitmap的原始宽(outWidth)和高(outHeight)


  • 在4.4之前如果复用bitmap则不支持在native层缩放,缩放放到java层,将原来bimap基础上新建bitmap,销毁原来的,这样效率低, 4.4以后支持在native层做缩放


  • 8.0,bitmap像素数据在native,6.0以前finalize释放native的bitmap,之后通过注册NativeAllocationRegistry(简化了Cleaner的使用),将native资源的大小计入GC触发的策略之中。即使java堆增长缓慢,而native堆增长快速也同样会触发gc


  • Cleaner用于回收native堆中的像素数据:Cleaner继承自虚引用,虚引用指向对象被回收时,虚引用对象会进入ReferenceQueue,异步线程ReferenceQueueDaemon会在ReferenceQueue.wait()上等待,只有对象被回收,然后遍历引用队列,若存在Cleaner则调用clean方法释放native内存


  • Bitmap大小=长* 宽 * 像素大小


  • BitmapFactory.Options


  • 复用bitmap:加载时设置inBitmap表示使用之前bitmap对象使用过的内存 而不是重新开辟新内存(如果被复用的Bitmap == 返回的被加载的Bitmap,那么说明复用成功了)。复用条件是,图像是可变的isMutable为true


  • inPreferredConfig


  • 只有当图片是webp 或者png24的时候,inPreferredConfig才会有效果。
  • ALPHA_8 : 图片只有alpha值,没有RGB值,一个像素占用一个字节
  • RGB_565:2字节


  • ARGB_8888 : 一个像素占用4个字节


  • inSampleSize:为2的幂次,表示压缩宽高为原来的1/2,像素密度不变,按需加载,按ImageView大小加载,计算inSampleSize的算法是,原始宽高不停除2,inSampleSize不停乘2,直到原始宽高小于需求宽高。


  • 回收bitmap(Bitmap.recycle()):只是释放图片native对象的内存,并且去除图片像素数据的引用,让图片像素数据可以被垃圾回收


  • Bitmap占用内存大小因素:


  1. 图片的原始宽高(即我们在图片编辑软件中看到的宽高)


  1. 解码图片时的Config配置(即每个像素占用几个字节)


  1. 解码图片时的缩放因子(即inTargetDensity/inDensity)


  • BitmapRegionDecoder用于图片分块加载


  • jpg 色彩丰富,没有兼容性问题,不支持透明度,和动画,适用于摄影作品。jpg在高对比度的场景下效果不好,比如黑色文字在白色的背景上


  • png包括透明度适用于图标,因为大面积的重复颜色,适用于无损压缩


  • webp包括有损和无损两个方式,webp的浏览器支持不佳,webp支持动画和透明度,之前动画只能用gif,透明度只能选png,有损压缩后的webp的解码速度慢,比gif慢2倍


  • 图片缩放比例 scale = 设备分辨率 / 资源目录分辨率  如:1080x1920的图片显示xhdpi中的图片,scale = 480 / 320 = 1.5,图片的宽高会乘以scale


ANR


  • KeyDispatchTimeout:View的点击事件或者触摸事件在特定的时间(5s)内无法得到响应。


  • BroadcastTimeout:广播onReceive()函数运行在主线程中,在特定的时间(10s)内无法完成处理。


  • ServiceTimeout:Service的各个生命周期函数在特定时间(20s)内无法完成处理


Lifecycle


  • 让任何组件可以作为观察者观察界面生命周期


  • 通过LifecycleRegistry,它持有所有观察者,通过注册ActivityLifecycleCallbacks 实现生命周期的分发,如果是29 以下则将ReportFragment添加到activity中


  • 监听应用前后台切换:通过registerActivityLifecycleCallbacks,然后在维护一个活跃activity的数量,ProcessLifecycleOwner为我们做了这件事情 用于监听应用前后台切换,ProcessLifecycleOwner的初始化通过ContentProvider实现


恢复数据


  1. onSaveInstanceState()+onRestoreInstanceState():会进行序列化到磁盘,耗时,杀进程依然存在


  1. Fragment+setRetainInstance():数据保存在内存,配置发生变化时数据依然存在,但杀进程后数据不存在


  1. onRetainNonConfigurationInstance() + getLastNonConfigurationInstance():数据保存在内存,配置发生变化时数据依然存在,但杀进程后数据不存在


进程优先级


一共有五个进程优先级


  1. 前台进程(Foreground process):该进程中有前台组件正在运行,oom_adj:FOREGROUND_APP_ADJ=0


  • 正在交互的Activity,Activity.onResume()


  • 前台服务


  • Service.onCreate() onStart()正在执行


  • Receiver.onReceive()正在执行


  1. 可见进程(Visible process) VISIBLE_APP_ADJ = 1


  • 正在交互的Activity,Activity.onPause()


  1. 服务进程(Service process)


  • 后台服务


  1. 后台进程(Background process) BACKUP_APP_ADJ = 3


  • 不可见Activity Activity.onStop()


  • 后台进程优先级等于后台服务,所以长时间后台任务最后其服务


  1. 空进程(Empty process):不包含任何组件的进程,Activity 在退出的时候进程不会销毁, 会保留一个空进程方便以后启动. 但在内存不足时进程会被销毁


按下返回键退出应用,此时应用进程变成缓存进程,随时可能被杀掉


按下home键退出应用,此时应用是不可见进程


LruCache


  • ~是内存缓存,持有一个 LinkedHashMap 实例


  • ~用LinkedHashMap作为存储结构,且LinkedHashMap按访问顺序排序,最新的结点在尾部,最老的结点在头部


启动优化


  1. 视觉优化:windowBackground设置一张图片(成为StartingWindow的Decorview的背景)


  1. 初始化任务优化:可以异步初始化的,放异步线程初始化,必须在主线程但可以延迟初始化的,放在IdleHandler中,


  1. ContentProvider 优化:去掉没有必要的contentProvider


  1. 缩小main dex:MultidexTransform 解析所有manifest中声明的组件生成manifest_keep.txt,再查找manifest_keep.txt中所有类的直接引用类,将其保存在maindexlist.txt中,最后将maindexlist.txt中的所有class编译进main.dex。multiDex优化,自行解析AndroidManifest,自定义main.dex生成逻辑,将和启动页相关的代码分在主dex中,减小主dex大小,加快加载速度。


  1. multiDex.install 异步化,在4.4以下的机型MultiDex.install()耗时。它会先解压apk,遍历其中的dex文件,然后压缩成对应的zip文件(这是第一次的逻辑,第二次启动时已经有zip文件则直接读取。)然后通过反射,将其余的dex追加到DexPathList的尾部。这个过程中的压缩成zip可以免去,以提升速度。可以将这个过程放在单独的一个进程中做(在attachBaseContext中,开启一个死循环等待multidex完成),而且该进程有一个activity界面展示loading,但加载完毕后通知主进程,在跳转到闪屏页


LiveData


  • LiveData 的数据观察者在内部被包装成另一个对象(实现了 LifecycleEventObserver 接口),它同时具备了数据观察能力和生命周期观察能力


  • LiveData 内部会将数据观察者进行封装,使其具备生命周期感知能力。当生命周期状态为 DESTROYED 时,自动移除观察者


  • LiveData 的观察者会维护一个“值的版本号”,用于判断上次分发的值是否是最新值,“新观察者”被“老值”通知的现象叫“粘性”。因为新观察者的版本号总是小于最新版号,且添加观察者时会触发一次老值的分发



  • 在高频数据更新的场景下使用 LiveData.postValue() 时,会造成数据丢失。因为“设值”和“分发值”是分开执行的,之间存在延迟。值先被缓存在变量中,再向主线程抛一个分发值的任务。若在这延迟之间再一次调用 postValue(),则变量中缓存的值被更新,之前的值在没有被分发之前就被擦除了。


ViewModel


  • ViewModel 实例被存储在ViewModelStore的map中, 在配置发生变化时onRetainNonConfigurationInstance会被调用(ViewModelStore的map对象会被存储在NonConfigurationInstances中),并会返回这个对象.在恢复ViewModel时再getLastNonConfigurationInstance中再次获取


  • Activity 实现了LifecycleOwner,等onDestroy时会尝试(非配置变化时)调用 store的 clear(遍历了 viewmodel的clear)


  • ViewModel 在 Fragment 中不会因配置改变而销毁的原因其实是因为其声明的 ViewModel 是存储在 FragmentManagerViewModel 中的,而 FragmentManagerViewModel 是存储在宿主 Activity 中的 ViewModelStore 中,又因 Activity 中 ViewModelStore不会因配置改变而销毁,故 Fragment 中 ViewModel 也不会因配置改变而销毁。


DiskLruCache


  • 内部有一个线程池(只有一个线程)用于清理缓存


  • 内部有一个LinkedHashMap结构代表内存中的缓存,键是key,值是Entry实体(key+file文件)


  • ~有一个journal文件缓存操作的日志文件,构造时会读取日志文件并将其转换成LinkedHashMap存储在内存


  • 取缓存时,先读取内存中的Entry,然后将其转换成Snapshot对象,可以从Snapshot中拿到输入流


  • 写缓存时,新建Entry实体并存在LinkedHashMap中,将其转换成Editor对象,可以从中拿到输出流


  • 通过LinkedHashMap实现LRU替换


  • 每一个Cache项有四个文件,两个状态(DIRTY,CLEAN),每个状态对应两个文件:一个文件存储Cache meta数据,一个文件存储Cache内容数据


编译打包流程


  1. 打包资源,res下文件转换成二进制,asset目录


  1. 编译java文件为class文件


  1. 将class文件转换为dex文件


  1. 将资源和dex打包到apk


  1. 签名


launch mode


taskAffinity与allowTaskReparenting配合:我们可以在AndroidManifest.xml为Activity配置android:allowTaskReparenting属性,表示允许此Activity更换其从属的任务栈。设置此属性的Activity一但当前Task切换到了后台,就会回到它“倾向”的任务栈中


  1. singleTask 全局唯一,先检查是否有和待启动Activity的taskAffinity相同的task(若未显示指定,则默认和app的第一个activity拥有相同的taskAffinity,为包名),若无则新建task并新建Activity压栈,若有则把该task移到前台并在该task中寻找待启动activity,若找到则将该task中该activity之上的所有activity弹出,让其成为栈顶(此时onCreate()不被调,onNewIntent()被调),若没有找到则新建Activity实例并压栈


  1. singleInstance 全局唯一,如果不存在待启动Activity,则新建task来容纳待启动activity,并且该task不能放入其他activity,若存在,则将对应的task移到前台 该类型只能在manifest中指定,不能通过Intent.setFlag() 该类型只能作用于Task栈底的Activity


  1. standard 全局不唯一,每次都在当前task中新建一个实例(待启动activity.onCreate()每次都会被调用)


  1. singleTop 全局不唯一,只有当Activity位于task的栈顶时,该activity实例才会被重复利用(onNewIntent()被调用而不是onCreate()),否则都会新建实例


ActivityManagerService


  • 在SystemServer中被启动,通过SystemServiceManager.startService(ActivityManagerService.Lifecycle.class)启动
  • 负责四大组件的启动切换调度


  • AMS 构造的时候


  1. 创建了两个线程,一个是工作线程,一个是和进程启动相关的线程


  1. 启动了低内存检测LowMemDetector,通过epoll机制获取低内存通知


  1. 构建ActiveServices管理service


  1. 构建ProviderMap管理contentProvider


  1. 初始化ActivityTaskManager


  1. 开启Watchdog,监听线程阻塞


  1. 创建OomAdjuster用于调整进程的优先级等级


  1. 创建BatteryStatsService和ProcessStatsService用于管理电量状态和进程状态


  • 获取AMS对象,是通过 ServiceManager.getService() 获取一个 IBinder 对象,然后通过asInterface()获取本地对象或者远程对象的本地代理(Android 10中所有系统服务都通过AIDL接口来获取,在Android10以前获取服务不是通过直接通过AIDL接口的,而是通过ActivityManagerNative来转发,本质还是通过AIDL生成类Stub来获取)


  • 它负责管理Activity,它通过ActivityStackSupervisor管理Activity调度,ActivityStackSupervisor实例在它构造函数中被创建


  • 它会和应用进程双向通信以完成启动Activity,


  • 它维护所有进程信息,包括系统进程,它通过ProcessRecord来维护进程运行时的状态信息,需要将应用进程绑定到ProcessRecord才能开始一个Application的构建


动画


  1. view animation


  • 也称为补间动画:定义关键帧,其余帧由系统补齐,有点像导航


  • 局限于view 局限于位移 旋转 透明度 缩放


  • 用父控件的Transformation中的matrix,将matrix应用到View 上,每次绘制的时候会检查动画是否完成,若没完成则调用invalidate(),ViewRootImpl向编舞者跑了一个遍历View树的任务,会有同步消息屏障


  • 不能监听动画变化的过程


  • 还能响应原有位置的触摸事件,是因为会将 matrix 反向运算


  1. property animation


  • 构建值变化的完整序列 并将其运用到视图属性上


  • 不仅仅适用于view 可用于任何提供了getter和setter的对象的任何属性上


  • 通过向 Choreographer 不停post 一个动画类型任务(在绘制任务之前执行), 当前帧完毕后若动画未结束继续post 没有同步消息屏障


  • 可监听动画变化过程


  • Interpolator决定值变化的速度(根据时间流逝的百分比计算出当前属性值改变的百分比)


  • 若是硬件加速,则直接修改RenderNode中的相关属性,不需要重新构建DisplayList,若是软件绘制,则会触发invalidate


  1. 帧动画:就像连环画一样,一张张图片连续播放。AnimationDrawable 会在动画播放之前将所有帧都加在到内存,耗内存。


关于帧动画的性能优化可以点击Android性能优化 | 帧动画OOM?优化帧动画之SurfaceView逐帧解析


ConstraintLayout 性能


  • 额外的封装,会将容器控件包装成ConstraintWidgetContainer,子控件包装成ConstraintWidget


  • 在测量布局的时候,会想把所有的ConstraintWidget移除,然后遍历所有子控件并重新添加ConstraintWidget,如果子控件有约束则将其连接到锚点,构建依赖图,深度遍历依赖图,进行求解(Cassowary 算法),最终得到相对于父亲的上下左右。


Java & Kotlin


string


string是final类型的char数组,表示引用不会改变


final


表示引用指向不能变,但其指向的变量是可变的


泛型


  • 泛型的目的是类型参数化,即用变量表示类型


  • 提升安全性:泛型可以把使用Object的错误提前到编译后,而不是运行后,提升安全性


  • 消除强转:没有泛型的时候,都用Object代替,因为Object可以强转成任何类型


  • PECS是在使用泛型时为了遵守里氏替换原则必须准守的原则,使用泛型增加代码适用性时保证了类型安全。


  1. PE:Producer extends 实现协变效果:泛型类和类型参数的抽象程度具有相同的变化方向。泛型类只生产泛型,即泛型只会出现在类方法的返回值位置,kotlin中用out表示,java用extend表示


  1. CS:consumer super 实现逆变效果:泛型类只消费泛型,即泛型只出现在类方法的参数位,kotlin中用in表示,java用super表示。


  • 类型参数的父子关系是否会延续到外部类上,若延续的叫协变,否则父子关系转向了,这叫逆变,若没有父子关系则叫不变型 ,泛型是不变型


  • 类型擦除:为了兼容1.5以前的代码,即编译后的实参类型是Object或者上界


  • 当子类覆盖或者实现父类方法时,方法的形参要比父类方法的更为宽松;


  • 当子类覆盖或者实现父类方法时,方法的返回值要比父类的更严格。


  • 如果在编译的时候就保存了泛型类型到字节码中,那么在运行时我们就可以通过反射获取到,如果在运行时传入实际的泛型类型,这个时候就会被擦除,反射获取不到当前传入的泛型实际类型


  • Kotlin reified 可避免类型擦除,它会将方法内联到执行的地方,并且对泛型对象进行instanceof 的分类讨论。


关于泛型的详细分析可以点击Kotlin 进阶 | 不变型、协变、逆变


java对象生命周期


  1. 创建:为对象分配内存,构造对象


  1. 应用:至少一个强引用指向它


  1. 不可见:不再持有强引用(程序执行超出了对象的作用域)


  1. 不可达:没有强引用指向


  1. 收集:准备gc,会执行 finalize()


  1. 终结:等待垃圾回收


  1. Deallocated:回收完成


类加载


  • 编译:javac 命令把 .java 文件编译成字节码(.class 文件)


  • 运行:jvm执行.class


  • 类加载过程:加载---链接---初始化


  1. 类加载:jvm把.class作为二进制流读入内存,并实例化一个Class对象,jvm 并不是一次性把所有类都加在到内存,而是执行过程中遇到没有加载的才加载,并只加载一次(Android加载的dex)


  1. 验证:二进制合法性校验


  1. 准备:为类变量在方法区赋初始值


  1. 解析:将类名,方法名,字段名替换为内存地址


  1. 初始化:对类的主动引用,包括new 调用静态方法,使用静态字段


  1. 使用:


  1. 卸载: 统计类加载耗时:反射BaseDexClassLoader的 pathList,写入自定义的 PathClassLoader(装饰者增加耗时统计)


  • 类加载器 PathClassLoader:只能加载应用包内的dex


  • 类加载器 DexClassLoader:可以加载任意位置的 dex


  • 类加载器 BaseDexClassLoader:持有 DexPathList,结构如下, BaseDexClassLoader(DexPathList(Element数组(DexFile(多个Class))))


  • DexPathList: 将 dex 文件转换成 element 存入数组(dexElements),findClass()是遍历Elements并进行类名匹配。


  • Android 类加载过程:Dex 文件在类加载器中被包装成 Element,Element以数组形式被DexPathList 持有,加载类时通过遍历Element数组进行类名匹配查找,只要把新的Dex文件插入到 Element头部即可实现热修(反射)。


  • 双亲委托:类加载器加载类时,将加载请求逐级向上委托,直到BootStrapClassloader,真正的加载从顶层开始,逐级向下查找。避免了类重复加载,以及安全,因为系统类总是由上层类加载器加载,无法通过自定义篡改


目录
相关文章
|
7月前
|
Dubbo NoSQL Java
太为难我了,阿里面试了7轮(5年经验,拿下P7岗offer)
今年的大环境非常差,互联网企业裁员的现象比往年更严重了,可今年刚好是我的第一个“五年计划”截止的时间点,说什么也不能够耽搁了,所以早早准备的跳槽也在疫情好转之后开始进行了。但是,不得不说,这次阿里面试真的太难为我了,可以说是和面试官大战了7个回合,不过好在最后给了offer。
|
7月前
|
设计模式 算法 NoSQL
Java开发三年四面字节跳动复习一个月斩获offer,寒冬并不可怕
目前互联网行业形势越来越严峻,我接连投递了很多的简历,得到的回复却是寥寥无几,索性好好复习了大概一个半月的样子,挑战字节跳动成功!!接下来分享我在字节面试遇到的面试题,欢迎大家文末留言与我一起讨论!
|
7月前
|
消息中间件 Dubbo Java
疫情下的机遇,阿里直招怒斩"P7"offer,自曝狂啃六遍的面试笔记
工作肯定会找的,面试肯定要过的,小编在这里为大家整理了我的一位朋友,一位从中游公司跳槽到阿里P7的面试题库
|
存储 算法 NoSQL
膜拜!砍下13个大厂Offer神仙案例! | 彭文华
膜拜!砍下13个大厂Offer神仙案例! | 彭文华
|
设计模式 前端开发 算法
面经分享:美团面试也太难了!4面美团终成Offer
这篇文章分享我一个学弟的美团实习面试经历,万万没想到现在大厂实习面试也这么难,下面是他的面经,各位读者老哥可以参考浏览。 美团我是在拉勾网上投的简历,之前也投过一次,简历都没通过删选,后来让学姐帮我改了一下简历,重新投另一个部门,获得了面试机会。10月23日中午HR打电话过来预约了下午4点半面试,说会在线写代码,让我准备好网络环境。结果5点半还没打电话过来,被放鸽子。与hr重新沟通过后,确定下周一下午再面,可是跟hr沟通预约这一套貌似在美团并没有什么用。
|
存储 缓存 编解码
裸辞-疫情-闭关-复习-大厂offer(二)(下)
裸辞-疫情-闭关-复习-大厂offer(二)
104 0
|
存储 缓存 安全
裸辞-疫情-闭关-复习-大厂offer(一)(下)
裸辞-疫情-闭关-复习-大厂offer(一)
88 0
|
存储 缓存 网络协议
裸辞-疫情-闭关-复习-大厂offer(二)(中)
裸辞-疫情-闭关-复习-大厂offer(二)
115 0
|
存储 消息中间件 前端开发
裸辞-疫情-闭关-复习-大厂offer(一)
裸辞-疫情-闭关-复习-大厂offer(一)
110 0
|
存储 设计模式 算法
裸辞-疫情-闭关-复习-大厂offer(二)
裸辞-疫情-闭关-复习-大厂offer(二)
132 0