2020Android面试学习

Java String 有多长?

2个方面 
栈   String longString = "aaaa....aaaa";  字节数65535,字节码的格式是UTF8
实际是65534,javac源码 length<65535
kotlin是65535
String longString = "叁叁肆那";
一个汉字占3个字节,汉字个数65535/3,是没问题的
源码 length>65535
主要受字节码影响,Latin是65534,非Latin是65535
也会方法区大小限制

堆      byte[] bytes = loadFromFile("test.txt");
        String string = new String(bytes);
通过new出String的对象,理论上最大长度是 Integer.MAX_VALUE
Java虚拟机指令newarray的限制
ArrayList的源码中
Some VM reserve some header words in an array
最大长度是 Integer.MAX_VALUE - 8
也会受到堆内存大小的限制

匿名内部类

匿名内部类名字是 包名+$1 $2
编译器生成
为什么可以new 匿名内部类
子类可以访问父类的构造方法

泛型

类型擦除 从编译的细节
类型擦除 对运行时的影响
类型擦除 对反射的影响
对比类型不擦除的语言
为什么java选择类型擦除?
类型擦除的好处?
运行时内存负担小
兼容性好       1.5之后才有泛型
类型擦除的问题?
基本类型无法作为泛型实参 所以需要装箱和拆箱 开销比较大
泛型类型无法用作方法重载
泛型类型无法当做真实类型使用 编译之后被擦除了,T都是object
静态方法无法引用泛型参数 泛型参数只有类实例化之后才知道,所以static方法不知道
类型强转运行时开销  需要强转 编译时擦除泛型
泛型签名
泛型签名.png

获取泛型


获取泛型.png

获取泛型实例.png
kotlin反射.png

onActivityResult使用很麻烦,为什么不设计成回调

B回到A,A会被销毁,会重新创建对象的实例,所以回调不能使用
解决方案:
  activity嵌套fragment,用fragment里的onActivityrResult里的引用。因为fragment在activity创建之后生成,所以拿到fragment里新的对象实例,fragment持有的对象的activity,必然是新的。
唯一性,view的唯一可以是id,通过findviewbyId获取
fragment的唯一性可以考mWho,通过反射拿到 

如何停止一个线程

线程stop已经被废弃
线程不能直接停止,读写操作容易crash,资源不能清理;也不能暂停,一直占用cpu,其它线程会一直等待。
但是任务可以停止
Interrupt
volatitle Boolean标志位

如何写出线程安全的程序

什么是线程安全?
可变资源(内存)线程间共享
解决方案:
不共享资源  重入函数  threadLocal(final 避免存储大量对象 用完之后及时移除对象)
禁止重排序 final(定义的变量不可以被修改 方法,类不可被复写,继承)volatile(可见性)
保证可见性 fina volatile 加锁(锁释放会强制将缓存刷新到主内存)
保证原子性(简单理解 运行步骤不可拆分 +运算可被拆分成3个步骤,所以不具有原子性)
保证原子性的方法:
加锁,保证操作的互斥性
使用CAS指令(如Unsafe.compareAndSwapInt)
使用原子数值类型(如AtomicInteger)
使用原子属性更新器(如AtomicReferenceFiledUpdater)

ConcurrentHashMap如何实现并发访问

ConcurrentHashMap 类中包含两个静态内部类 HashEntry 和 Segment。HashEntry 用来封装映射表的键 / 值对;Segment 用来充当锁的角色,每个 Segment 对象守护整个散列映射表的若干个桶。每个桶是由若干个 HashEntry 对象链接起来的链表。一个 ConcurrentHashMap 实例中包含由若干个 Segment 对象组成的数组

优化so包

动态下发so包
image.png
image.png

捕获Native异常

old_singalhandlers.png

android_singal_handler.png

JNIEnvHelper.png

Activity的启动流程

Activity跨进程启动.png

如何跨App启动Activity

uid   exported intentFilter  拒接服务漏洞
uid.png

ComponentName.png
exported.png

intentFilter.png
Activity加权限.png

Handler

post为什么能切换主线程?
调用主线程的handler,thread run()方法
threadLocal是什么?
是map,ThreadLocalMap,key是线程名.currentThread
threadLocal是全局唯一的,泛型是looper,Looper是全局唯一的,不能new,只有Looper.prepare()保证唯一
一个线程只绑定一个looper,一个looper只绑定一个mQueue


post,sendMessage,sendEmptyMessageAtTime最终都调用enqueueMessage
发送消息
handler.sendMessage(msg),enqueueMessage压入消息队列 max_pool_size=50
取消息
Looper.loop()轮询器 , msg.target.dispatchMessage,msg.target是handler

Looper.loop()为什么不会ANR?
Binder.clearCallingIdentity() native方法
在for循环中,Trace.traceBegin(traceTag,msg.target.getTraceName(msg))调用nativeTraceBegin native方法
调用2个native方法回收释放

消息队列优化
枚举类型  复用  重复消息过滤  互斥消息取消
image.png
image.png
image.png

如何避免OOM

使用合适的数据结构
内存复用 建立一个内存池 Message pool
避免使用枚举 对象 24Bytes
Bitmap 合适分辨率 放在合适的目录(mdpi)bitmap的重采样和复用配置
谨慎使用多进程  预加载一些配置和资源
使用合适的数据结构.png
5R法则.png

对图片进行缓存

网络/磁盘/内存
LRU 最近最少使用 LinkeHashMap 线程安全synchronized 将被访问的元素移到链表尾部,迭代器访问第一个元素最老
LFU 频率使用最多的

image.png

图片占用内存的大小

jpg 用RGB_565 没有透明通道
使用inSampleize采样 大图->小图
使用矩阵变换来放大图片 小图->大图
使用.9图图做背景
不使用图片 优先使用VectorDrawable

Binder

image.png

AIDL Android Interface Definition Language Android接口定义语言
打开binder   driver="/dev/binder"   
buffer创建    binder_open(driver,128*1024)
开辟内存映射  128kmmap
ServiceMananger启动
打包Parcel中,数据写入binder设备 writeTransactionData , copy_from_user
服务注册,添加到链表svclist中
定义主线程中的线程池
循环从mIn和mOut中取出读写请求 buffer是256字节,发到binder设备中       

案列:三方登录
client 创建AIDL,定义接口方法(与server同一包名),创建service,用于service端回调通信
server 创建AIDL,定义接口方法(与client同一包名),创建service,用于接收client端发送的消息
client  serviceManager  server


image.png

image.png

PackageManagerService
https://www.jianshu.com/p/cb0555d62b9c

在Systemserver中启动
ApplicationPackageManager  IPackageManager(mPM)  AIDL
image.png

安装

有界面安装
PackageInstallerActivity  PackageUtil.getPackageInfo(sourceFile)  startInstall() InstallAppProgress.class(通过handler broadcastReceiver 回调消息)
无界面安装 
调用C adb_commandline(buff4096)  install_app(获取手机内部存储路径 sd卡存储路径) 通过shell:pm PackageManagerService handler发送消息 默认安装4次,4次不成功即失败
image.png

无界面安装 .png


image.png

image.png

image.png

ActivityManagerService

image.png

image.png

调用2次binder机制 App进程->系统进程->App进程
源码分析.png

App启动流程

image.png

image.png

换肤

思路
1.
 替换textcolor 和 background
自定义Textview Button Imageview LinearLayout
重写AppCompatViewInflater
在onCreate中重写Factory2
系统API UIMode判断是白天黑夜
2.
将resource打包apk
反射获取AssetManager,new resource,加载path,拿到皮肤包的包名
apk内resources.arsc映射文件,资源name必须一一对应,通过资源id,获取name,type

资源加载流程.png

image.png

image.png

image.png

image.png

image.png

image.png

插件化

Hook
类加载  createClassLoader
资源加载 AssetManager
四大组件  activity hook Intent service hook AMP 动态代理 
广播 解析插件Manifest 将静态广播转成动态 保活只能在宿主中

shadow
静态代理
image.png

Tinker热修复

代码 dexDiff算法
资源 Entry的BSDiff
46831行代码
算法机制
old new 先根据索引排序,只要old < new delete = replace  > add 

1.获取系统的classLoader pathList
2.构造dexElements
3.将补丁dex注入到系统dexElements的最前面

sp原理 mmkv

ArrayMap  ConcurrentHashMap分段加锁
2级缓存 磁盘 内存
读数据会阻塞 加锁
commit会阻塞 apply开启线程异步

MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。
内存准备
通过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由操作系统负责将内存回写到文件,不必担心 crash 导致数据丢失。
数据组织
数据序列化方面我们选用 protobuf 协议,pb 在性能和空间占用上都有不错的表现。
写入优化
考虑到主要使用场景是频繁地进行写入更新,我们需要有增量更新的能力。我们考虑将增量 kv 对象序列化后,append 到内存末尾。
空间增长
使用 append 实现增量更新带来了一个新的问题,就是不断 append 的话,文件大小会增长得不可控。我们需要在性能和空间上做个折中。

socket TCP UDP

TCP socket 
 ServerSocket  accept()  阻塞 inputstream
 ClientSocket  IP 端口号 outputstream  
流的方式发送
UDP socket
DatagramSocket receive()  阻塞
ClientSocket  InetAddress IP 端口号 send
包的方式发送 最大长度 65535-8
UDP 不可靠 是服务端抓取数据
单播 广播 多播

Https为什么安全

加密 TLS
对身份进行验证 证书

kotlin协程

协程就是个线程框架
协程的挂起本质就是线程切出去再切回来
是轻量级的线程,解决并发问题
CoroutineScope创建协程
coroutineScope.launch(Dispatchers.IO) {
    ...
}
withContext 这个函数可以切换到指定的线程,并在闭包内的逻辑执行结束之后,自动把线程切回去继续执行 消除了并发代码在协作时的嵌套

suspend 『挂起』是非阻塞式的 挂起的对象是协程 只是一个提醒 我是一个耗时函数,我被我的创建者用挂起的方式放在后台运行,所以请在协程里调用我
什么是挂起?挂起,就是一个稍后会被自动切回来的线程调度操作
启动一个协程可以使用 launch 或者 async 函数,协程其实就是这两个函数中闭包的代码块。

kotlin inline

减少方法压栈,出栈,进而减少资源消耗
也就是说inline关键字实际上增加了代码量,但是提升了性能,而且增加的代码量是在编译期执行的,对程序可读性不会造成影响

APT javapont

注解目标是在类 方法 参数
编译期 运行时
extends AbstractProessor

内存抖动

循环 频繁调用导致GC频繁
自我介绍
您好,我叫汤文斌,在上家公司担任Android leader ,带领3人团队,负责公司5个项目,3个已经上线,2个是公司内部项目。上线的项目平均日活在1w左右,崩溃率维持在1%。主要负责产品和前后端沟通,架构搭建和技术选型,以及新技术的布道。

APP瘦身优化

代码优化   混淆 第三方库 移除无用代码
资源优化   混淆 压缩 删除冗余资源 
so优化      只用arm包,最小

列表卡顿优化

布局 不要嵌套
图片 不要太大 滑动取消加载
线程  使用线程池

jetpack

lifecycle 解决代码入侵 解决内存泄漏 解决统一管理
liveData和lifecycle绑定 可以感应生命周期 不会崩溃
viewmodel 管理数据 重建不丢失

GC

可达性分析,寻找GCRoot
GCRoot对象  Java虚拟机栈 方法区 活跃的 Native 引用的对象存在
堆内存分配不足  systen.gc()

classLoader 双亲委派模式

先找父类,父类找不到自身才会执行实际的类加载过程
自定义classLoader 重写findClass 调用defineClass方法将字节码转成Class对象
Android中classLoader PathClassLoader DexClassLoader
PathClassLoader用来加载系统apk和被安装到手机的apk内的dex文件
DexClassLoader可以从SD卡上加载包含class.dex的.jar和.apk文件

LeakCanary原理

weakReference  ReferenceQueue
页面需要被回收,就将其包装到weakReference中,并且在weakReference的构造器中传入自定义的ReferenceQueue
将包装的weakReference做一个标记key,并且在一个强引用Set中添加相应的key记录
主动触发GC,遍历ReferenceQueue,删除对应的Set记录
如果Set中依然有对象,则内存泄漏

你可能感兴趣的:(2020Android面试学习)