Android知识点整理

Map

如果一个数组反复插入删除怎么降低时间复杂度?

A.数组插入、删除:是在下标i的地方,后边的数组整体system.copy一份,然后插入到i位置。Copy是一个循环。

链表插入、删除:是一个节点node,node包含两部分,数组和指针,当前指针指向下一个数组。

数组,查找快,直接用下标获取。

链表,查找慢,需要循环获取。

B.标记-查找:删除的时候不删除,赋值为null或者标记其他,这个下次在插入的时候直接赋值即可。然后在适当的机会,循环删除值为null的数组即可。这样就可以优化了对象的反复创建和删除。

C.linkedlist,查询第一个和最后一个时间复杂度一样?一样,查询第一个和倒数第二个一样吗?不一样,liankedlist是双向链表。倒数第二个需要两步。

descript

hashmap?长度默认16

jdk 1.7:为什么用链表?用的是hash碰撞原理

A。把key通过hashcode对length取模,设置下标。所以hashmap的table表是顺序的。然后创建一个节点(数据hashmapentry),放到这个下标的位置。同时每个下表对应的所有节点是一个单链表。

B。Hashcode的获取,就是一个hash碰撞的过程。比如hashcode=1和hashcode=17,是在一个下标上。

C。Get一个数据,就是用过key获取链表,然后在通过key循环具体的值,循环过程中要判断hashcode一致,判断key一致。

D。Hash表在什么情况性能最差?所有数据在一个节点上,那链表久非常大,效率就非常低了。

E。什么长度是16?因为在hash下标的过程中。16的二进制是10000.

descript

jdk1.8:每一个节点,用了红黑树。Treenode。但不是每一个节点都是红黑树。

A。hash冲突后不在用链表保存相同的index节点,而是采用红黑树保存冲突节点。

B。红黑树构建的时候很复杂、效率很低(旋转)。所以当链表超过8个才使用红黑树。

hashmap线程安全问题:不安全。

如果链表被同时操作就会出现问题。

Hashtable线程安全;所有函数(put、get,remove)的时候用到了synchronized。但是这样的任何访问都加了锁,包括所有hashcode上。

Synchronized,内置锁,开锁和关锁都是jvm完成的。所以get时候不能put。只能执行其中一个。

Concurrenthashmap:是基于hashtable的优化,它是在具体的链表上锁,也是用到了synchronized,但是它只是锁在了链表上。

广播注册方式?有什么不同?通信原理是什么?

动态,静态。原理都是Binder。静态广播不需要手动取消注册,周期是整个app期间。动态广播需要。

hashmap和sparsearray哪个更优?

Hashmap:hashcode操作,耗时。Hashmap是任意对象。装载因子(阀值)是0.75。达到16*0.75=12,开始扩容,也是耗时操作。浪费了4个空间,所以耗内存。Get是遍历获取链表,耗时。

Sparsearray:两个数组:默认空间10;key[],value[]. Key与value是一一对应的。Sparsearray是int,节省内存空间。对应的key应该存在数组的哪里?采用了二分查找,找到数组中的位置,然后通过system。Copy移动数组插入。

descript

get?通过二分查找找到下标,然后就可以了。速度快。

拷贝效率也低啊?但是这里,remove时是没有具体删除的,而是标记一个delete。这样插入的时候直接赋值就可以了。而hashmap是循环。

所以,sparsearray时间更快。但是hashmap空间更小。

单例模式:

A:双重校验:

如何防止指令重排,添加volatile。

descript

B:懒汉式单利

线程不安全

descript

线程安全

descript但是同步锁粒度太大:下边的粒度就小很多;

descript

C.饿汉式单例:线程安全

descript

D.静态内部类单例:线程安全的:

descript

E.采用枚举:枚举默认线程安全;

descript

F.使用容器实现单例:

descript

string 的== 与equals的区别?

String str1 = “a”常量池是在堆内存里的。==比较的是内存存放的位置。但是他们对hanshcode是相等的。

descript

equals比较的是字符串的序列,是具体的字字符比较。

Stirng str = “阿”。str = str+“阿”。与string ds = new string (“阿阿”);str和ds是一个对象,string是不可变的,因为string类里的方法,都是final。

descript

volatile:

A.多线程可见

descript

descriptdescriptdescript所以,volatile就相当于一个总线嗅探机制,当其中一个线程变化后,会修改主内存的值,其他线程会被通知,从新从主内存获取数据。并且8个操作是原子性的,其中一个指令执行,其他不会操作的。原理是:当一个线程执行write时,是被加了锁的(如下图的lock)。

descript

descript

B:禁止指令重排(半初始化),原理是内存屏障。

双重检测的单例,为啥加volatile?就是禁止指令重排。顺序如下:

descript

但是在cpu执行过程中,这个顺序会改变。如果在单线程里,这个顺序改变了也没关系,但是在多线程中,如果在一个线程创建了引用,而另一个线程在执行时,就会拿到这个引用,从而,拿到的值有可能是第一个线程未赋值的初始值0,而第二个还在初始化对象。

descript

descriptdescript

上述代码:多线程并发问题。会出现i–,最后不为0的情况。

A.synchronized:内置锁,重量级锁

descript

descript

descript

descript

descript

B.AtomicInteger:乐观锁:无锁机制.

比synchronized快。缺点是:i.decrementAndGet的i—是可操作的。

descript原理:根据堆内存中的偏移量来获取value,比通过对象.value的拷贝方式要快。

descript

C.ReentrantLock:可重入锁,也叫显示锁

是接口lock锁的实现类。也可以自己实现公平锁或者不公平锁,因为队列线程是我们自己的。也是重量级锁。

descript

原理、执行过程:

descript

D.CAS:先比较再交换。也是一种乐观锁,无锁机制。

高并发不适合。但是如果只有几个线程,速度非常快。

而且CAS有ABA问题:尤其是对象操作的时候。

descript

Context是什么?抽象类

A.上下文,贯穿整个应用。

B.运行环境:提供了一个应用运行所需要的信息、资源、系统服务等。

C.场景:用户操作和系统交互这一过程就是一个场景。比如Activity之间的切换、服务的启动等都离不开Context。

descriptdescriptdescriptdescript

什么是Activit、View、Window?

descriptdescript

Dex文件

descript

APK打包流程

Xml通过aapt编译成R.JAVA

Aidl通过aidl文件生成java文件

然后通过javaCompiler把java文件转化为class文件。

然后通过dex把class打包成.Dex文件。

用apkbuild把所有文件,包括资源,图片文件通过zip压缩打包成apk,然后通过对齐,签名形成一个可运行的apk。

descript

事件分发处理机制

descript

descript

事件处理

descript

Down事件的分发流程

descript

measureSpec是什么?原理是什么?

descript

测量主要解决parent和wrap的测量。

FlowLayout-》onMeasure:

//计算孩子的宽/高:

Width=100;height=300;

//设置自己的宽/高:

setMeasureDimension(width,height)

那如何计算孩子的宽高?如何度量?其中view的大小,不是int,而是MeasureSpec。

所有通过childView.getMeasureWidth(),childView.getMeasureheigth(),但是需要在获取之前设置度量孩子的大小childView.measure(widthMeasureSpec,heightMeasureSpec), widthMeasureSpec,heightMeasureSpec是什么?可以认为是父类宽高,childView的最大宽度,就是父类的宽高减去padding。那MeasureSpec如何得到?因为当前的子view也有可能有子view,所以.measure的时候,还需要通过getChildMeasureSpec获取widthMeasureSpec,heightMeasureSpec。

通俗的说?widthMeasureSpec,heightMeasureSpec就是能给chaildView的具体的大小。

descript

那为什要measure?因为只有把父类的具体大小给孩子,才能测量出孩子的具体宽高。

descript

layout自定义View布局原理?

就是确定每一个view的left,top,right,bottom。

descriptonLayout是用的视图坐标系:因为你布局的时候,是根据父布局进行布局的。

Layoutparams保存了view的坐标。

descript

descript

descript

##

inflate解析xml过程?

Xml-》解析-?然后创建view,通过反射创建,比如自定义view在xml中使用,需要全路径名-》创建view后,会把解析的属性给view。

descript

descriptdescriptdescript

沉浸式开发原理?状态栏和ui一致背景?

ViewPage?默认缓存3个界面。

Setoffscreenpagelimit(1), Limint为1.他的意思是最多缓存当前界面的左右两个界面,加上当前就是3个界面。

预加载:问题?缓存,会耗费更多内存,时间?会耗费更多时间。

所以采用懒加载方案,就是loading或者白屏的时候。

所以解决这个问题的原理:populate:填充+fragment adapter适配器模式。

缓存:就是创建fragment放入到arraylist里。

适配:当左右滑动时,先处理左边或右边的界面?如何处理,初始化(instantiateItem)一个fragment(放入arraylist)和销毁(destroyItem)掉一个fragment(从arraylist中删除)。

descript

descriptAttch,fragment不会立即执行。这个时候,首先还要把刚初始化的页面设置为不可见setUerVisibleHint,也就是即将要缓存起来的item不可见。

看下图:首先要缓存tab4,把tab1设为不可见,设置当前的目标tab3设为可见,并且直接用的FragmentCompat,。但是这个可见是在finishUpdate的commit之前执行的,所以,viewpage的fragment的生命周期,第一个是setUserVisibleHint。如下下图。

descriptdescript

descriptsetPrimaryItem会执行两个动作:之前的item设置为不可见,新的item设为可见。

descript

只有执行fragment的CommitAllowingStateLoss之后才会走fragment生命周期。

descriptdescript

descript

descript

RecycleView

缓存和复用都是ViewHolder。其实就是一个itemView。那缓存有几级?

descript

以下就是处理复用机制:

descript

descript

descript

descript从滑动看复用填充流程:

descript

descript

descriptdescript

descript

从onLayout看复用填充流程:最终也会到fill入口,跟之前一样。

缓存?=========================================================================

descript

descriptdescript

descript

descript

descriptdescript

descript

descript

descript

OOM是如何发生的?

descript

Java回收机制?如何减少OOM的概率?

descript

如何排除应用崩溃原因?ANR?

1.把崩溃堆栈传到服务。

  1. 使用第三方开源库。

    descript

descript

descriptdescriptdescript

descript

App启动优化,速度优化?

descript

descript

descript

descriptdescriptdescript

热修复原理?

descript

AndFix:

descriptdescript

Robust:

descript

Tinker&类加载机制:

descript

Bsdiff差分工具:

descript如何拿当前的apk问价:PackageManager

CalssLoader:

descript自己写的类,用的PathClassLoader(mainactivity),系统的类,就是BootClassLoader(application,activity)。

加载一个类方法:PathClassLoader.loadClass();->BaseDexClassLoader->ClassLoader.loadClass(),所以通过一个类全路径,获取Class对象。

descript

所有的类在dex文件里。FindLoadedClass从缓存获取class对象。如果没有,在通过parent .loadClass();parent也是一个ClassLoader。这里用的就是双亲委托机制。为什么要有这个机制:一是安全,而是避免重复加载。但是如果是我们的自己的类,parent肯定加载不到,所以执行findClass()。而这个classloader,是pathclassloader。所以最后看到baseDexClassLoader。

descript

然后看到,通过for循环找,dexElements,是一个数组【class1.dex,class2.dex,。。。。。。】。从而拿到了dex中的Class类对象。所以,我们得到了修复之后的dex,那怎么办,只要把这个dex放到数组的最前面即可,因为是顺序的,所以在使用的时候,拿到这个正确的之后,就直接return了,不会在查询class2.dex……..。最后,先getClassLoader拿到pathclassloader,通过反射,拿到DexPathList,在拿到这个类里的dexElements数组,把这个dex,放到dexElements里即可。

双亲委托机制:

descript在用户的加载类里,如果要加载Activity系统类,这个时候,不需要自己加载,由parent=BootClassLoader加载了。

descript

Qzone:

descript

如何判断文件的一致性?

crc方法。CRC32 即循环冗余校验码(Cyclic Redundancy Check):是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC )是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。

多进程-文件共享

Messenger,Bundle,文件共享,Content Provider……

sp、mmkv、mmap

SharedPreference:

map持久化: 在内存中时,在文件中序列化是xml。

初始化:在子线程里,通过io,获取xml文件,反序列化成map。

如果数据比较大时:读写数据时,awaitLoadedLocket:等待加载完成。所以数据大,需要解析时间长,如果没有解析完,就读写,这个时候需要等待子线程初始化数据完成。

并且sp创建后无法被回收。

写新数据:map-》序列化xml,写入文件。commit同步提交,会阻塞调用线程。Apply异步,无法获取结果,没有事务性api。无法保证数据一致性。同时哪怕异步提交都可能会导致ANR的发生。

更新数据:sp没有更新的概念,是整体的读写操作。哪怕只修改了1条数组,sp也需要全部序列化为xml,覆盖文件进行保存。(全量更新)

优化点:

高效的文件操作;MMAP;

数据格式更精简;二进制;

String一个字节占8位,如“1”(0000 0001)浪费了7位,其实只需要1位就可以;而二进制,一个字节就可以8个数据;

更优的数据更新方式;

FileChannel:

拷贝文件比IO快6-7倍。为什么这么快?底层用到了零拷贝技术;如sendfile,mmap。

MMKV:速度非常快。并且实现了sp和sp.editor接口。用了mmap技术。

零拷贝:是不需要CPU参与的技术。DMA COPY是从内存拷贝到Disk。

descript

descriptdescript映射内存是通过Vma:vm_area_struct-》内存结构。

HTTP2:多路复用。

http1=字符串、文本

http2=二进制。

descript

总长度(key长度+key值+value长度 +value值 ……)长度数据不一定是一个字节。

那map数据存到文件的过程要通过序列化。

那数据格式Protobuf:比json更加精简。Protobuf编码是一个变长编码,就是有可能把int原有4个字节,编码成1,2,3,4,5个字节。

descriptdescript

descript

Serializable

descript1. 不会。

2. 深拷贝。Equal

3. Budle用的是Parcel,为什么不用map,bundle是跨进程传输一些小的对象。

4. 版本控制。

5. 原理:binder,binder大小就是1M-8kb;binder创建的线程池默认15个(我记得是31个)。内核中映射的最大内核空间,只有4M。有没有办法Intent突破1M?mmap的大小可以修改,但是不可以超过4M.还可以把数据写入数据库或者文件,传递数据的文件名。

6. Activity的启动流程要和AMS打交道,是要跨进程。两个不同进程,数据只有序列化之后才能带过去。

7. 序列化是跨进程通信使用的。持久化是保存数据。

Glide

descriptdescript

面试题:为什么glide不能再子线程中with?再子线程中不会添加生命周期管理,只有再主线程中才会用空白的Fragment监听Activity或者Fragment的生命周期。看下面源码:4.11

descript

正在显示的是活动缓存,然后是内存缓存。

descriptdescript

descript

descript

注解

descript

descript

descript

descript

descript

descript

descript

descript

descript

descript

descript

所有javac处理自定义注解类,也是通过ServiceLoader(spi)的方式处理生成实现类的。

descript

SPI

descriptdescript

descript

ASM字节码插桩

动画

descript

descriptdescript

descript

插件化

类加载机制:双亲委派机制

descript

descript

descript

descript

descriptdescript

findClass流程

descript

descriptdescript

descript

合并第三方Dex

思路:

descript

descript

descript

descript

descript

Hook Activity

descript

descript

descript

descript

代码流程:

先实现

descript

descript

descript

descript

descript

descript

descript

descript

descript

再实现

descript

descript

descript

descript

descript

descript

descript

descript

descript

descript

版本适配

descript

descript

descript

descript

descriptdescriptdescript

资源加载

descript

descript

descript

descript

资源插件:创建

descript

descript

descript

descript

descript

解决资源文件冲突:

descript

descript

descript

具体冲突:改变插件Context的mResources

descript

descript

descript

开机启动流程

descript

Loader

从按下电源开始,会通过Boot Rom中的引导芯片代码开始运行。加载引导程序BootLoader到RAM,然后执行。BootLoader会引导操作系统启动。

Kernel

首先启动内核态(idle pid=0),初始化进程管理、内存管理、驱动(Binder,Display,Camera等等)相关工作,并创建内核进程kthreadd(pid=2),同时创建工作的线程kworkder,软中断线程等等守护进程等。这些都在内核态中进行。

init

然后启动用户进程init进程,也是第一个用户进程。从而手机进入了用户态。同时init进程是用户进程的鼻祖。每一个进程都有一个main函数。通过linux一系列执行,最终会进入init.cpp,通过LoadBootScripts方法解析init.rc文件,init会挂载一些文件,通过while循环,启动zygote,ServiceManager进程,音频进程,Camera进程等等。看如下图,包括usb,hardware等等。所以init进程主要启动我们后台需要的一些服务进程。while是一个死循环,保证进程的保活,进程无事可做,就通过epoll机制,休眠。




zygote


启动入口:app_main.cpp中main函数(android 10,11,12),再main函数中构建了AppRuntime,然后在runtime设置进程名,属性,然后会执行start;如下图

在start里,发现启动了vm虚拟机(通过startVm),并设置了vm的参数,如下图:

设置好参数后,接着就创建虚拟机(通过CreateJavaVm方法)
虚拟机作用是什么?管理内存。
虚拟机创建完了,接着通过startReg方法注册android常用的JNI;JNI是啥,就是nativie中间件,用来函数的静态(动态)注册。


接着执行zygoteInit.java的main函数:


在zygote中通过preload预加载函数,会预加载系统类和资源(ImageView、TexetView,Resource)等。

zygoteserver就是为zygote创建了一个socket服务器。
这时候zygote已经做了很多事情了,接下来该创建SystemServer(大儿子),并通过反射方式调用进程初始化的main函数;



接着进入循环等待过程,如下图:

SystemServer

SystemServer通过SystemServiceManager管理启动一系列系统服务SystemService(AMS,WMS,ATMS,PMS),而系统服务创建的时候就会创建一个Binder添加到ServiceManager里.
ServiceManager是一个守护进程,独立进程,只是存放了服务的Binder。
两者不一样,SystemService通过ServiceManager暴漏给上层app。

app进程


WMS原理

WMS原理

Inhalte

  1. Map
    1. 循序表(数组arraylist)和linklist ( 双向链表)链表的插入、删除、查询的效率?
    2. 如果一个数组反复插入删除怎么降低时间复杂度?
    3. hashmap?长度默认16
      1. jdk 1.7:为什么用链表?用的是hash碰撞原理
      2. jdk1.8:每一个节点,用了红黑树。Treenode。但不是每一个节点都是红黑树。
  2. 广播注册方式?有什么不同?通信原理是什么?
  3. hashmap和sparsearray哪个更优?
  4. 单例模式:
    1. A:双重校验:
      1. 如何防止指令重排,添加volatile。
    2. B:懒汉式单利
      1. 线程不安全
      2. 线程安全
    3. C.饿汉式单例:线程安全
    4. D.静态内部类单例:线程安全的:
    5. E.采用枚举:枚举默认线程安全;
    6. F.使用容器实现单例:
  5. string 的== 与equals的区别?
  6. volatile:
    1. A.多线程可见
    2. B:禁止指令重排(半初始化),原理是内存屏障。
    1. A.synchronized:内置锁,重量级锁
    2. B.AtomicInteger:乐观锁:无锁机制.
    3. C.ReentrantLock:可重入锁,也叫显示锁
    4. D.CAS:先比较再交换。也是一种乐观锁,无锁机制。
  7. Context是什么?抽象类
  8. 什么是Activit、View、Window?
  9. Dex文件
  10. APK打包流程
  11. 事件分发处理机制
    1. 事件处理
    2. Down事件的分发流程
  12. measureSpec是什么?原理是什么?
  13. layout自定义View布局原理?
  14. inflate解析xml过程?
  15. 沉浸式开发原理?状态栏和ui一致背景?
  16. ViewPage?默认缓存3个界面。
  17. RecycleView
  18. OOM是如何发生的?
  19. Java回收机制?如何减少OOM的概率?
  20. 如何排除应用崩溃原因?ANR?
  21. App启动优化,速度优化?
  22. 热修复原理?
    1. AndFix:
    2. Robust:
    3. Tinker&类加载机制:
      1. Bsdiff差分工具:
      2. CalssLoader:
      3. 双亲委托机制:
    4. Qzone:
  23. 如何判断文件的一致性?
  24. 多进程-文件共享
  25. sp、mmkv、mmap
    1. SharedPreference:
    2. FileChannel:
    3. MMKV:速度非常快。并且实现了sp和sp.editor接口。用了mmap技术。
  26. Serializable
  27. Glide
  28. 注解
  29. SPI
  30. ASM字节码插桩
  31. 动画
  32. 插件化
    1. 类加载机制:双亲委派机制
    2. findClass流程
    3. 合并第三方Dex
    4. Hook Activity
      1. 先实现
      2. 再实现
      3. 版本适配
    5. 资源加载
    6. 资源插件:创建
      1. 解决资源文件冲突:
      2. 具体冲突:改变插件Context的mResources
  33. 开机启动流程
    1. Loader
    2. Kernel
    3. init
    4. zygote
    5. SystemServer
    6. app进程
  34. WMS原理