Android-面试复习记录

android面试题

binder机制主要有三部分.客户端client,服务端server,服务管理也就是servicemanager
主要就是客户端拿到服务端的代理对象.通过代理对象身上的进行交互,反之服务端也是如此
代理对象也就是具体的服务是从servicemanager中获取的.
在binder通信的中,我们android启动的服务,也就是加载的服务,都会被servicemanager进行管理.

内存中垃圾回收,引用计数法回收.当引用回0的时候,进行回收

ALPHA_8:  每个像素占用1byte内存
ARGB_4444: 每个像素占用2byte内存
ARGB_8888: 每个像素占用4byte内存
RGB_565:   每个像素占用2byte内存

bitmap在内存中占用大小取决于,色彩格式,目标屏幕密度

handler 其实在mainthread的时候,已经调用了looper.prepare()方法,然后在prepare调用的时候其实吧messagequeue已经创建出来了.然后将这个looper引用到了handler里面,我在handler的构造中其实有看到.然后looper.loop().其实这时候是创建了一个死循环,也就是for的死循环.那么这个时候会一直去messagequeue中获取这个msg.当获取到了之后,通过msg.target来获取到handler的对象,然后通过msg.target.dispatchmessage来进行发送这个msg,也就是获取到handler对象.最后通过msg.recycle进行回收.

如果在子线程中调用的时候,handler.sendMessage的时候,msg.target就等于了当前的handler,相当于,message记录了当前的handler对象,这样就有msg就会有handler对象

如果创建一个新的handler的时候,其实是会从之前threadlocal中去取也就是之前的mainthread.通过之前的创建的looper对象,来进行使用

其实也用了binder机制.


fragment生命周期:


onAttach -> oncreate -> oncreateview -> onActivityCreated -> onstart -> onresume -> onPause -> onstop -> ondestoryview -> onDestory -> onDetach

activity生命周期:


oncreate -> onstart -> onresume -> onpasue -> onstop -> ondestory


service生命周期:


onCreate -> onStartCommand -> onDestory

onCreate -> onBind -> onUnBind -> onDestory 

线程同步的方式

synchronized

JAVA中synchronized关键字能够作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。假如再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
 

Lock

1.ReentrantLock 类

java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。 ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)
reentrant 锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了 synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续) synchronized 块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个 synchronized 块时,才释放锁。

Lock 和 synchronized 有一点明显的区别 —— lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放!这一点区别看起来可能没什么,但是实际上,它极为重要。忘记在 finally 块中释放锁,可能会在程序中留下一个定时炸弹,当有一天炸弹爆炸时,您要花费很大力气才有找到源头在哪。而使用同步,JVM 将确保锁会获得自动释放。

ReentrantLock获取锁定与三种方式:

a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁

b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;

c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;

d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

强引用:垃圾回收器打死都不会回收掉一个强引用的,那怕是出现OOM也不会回收掉强引用,所有new出来的都是强引用。

软引用:垃圾回收器会在内存不足的情况下回收掉软引用,如果内存充足的话不会理它

弱引用:它跟软引用类似,但是它更脆弱,只要垃圾回收器一发现它,就会立刻回收掉它。比如一个对象持有一个强引用和弱引用,当强引用被取消时,那么只要GC发现了它,就会立刻回收掉。只是GC发现它的这个过程是不确定的,有可能不会马上发生,所以它可能还会多活一会,中间存在一个优先级。

虚引用:它跟上面3种方式都不同。我对虚引用的理解就是如果一个对象持有虚引用,那么就可以在被GC回收前进行一些设定好的工作等等。因为虚引用有个机制,因为虚引用必须和引用队列联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就回在回收对象的内存前,把这个虚引用加入到与之关联的引用队列中。而程序如果判断到引用队列中已经加入了虚引用,那么就可以了解到被引用的对象马上就要被垃圾回收了,这个时候就可以做些被回收之前的事情啦。


Android启动流程

ActivityManagerServices(AMS):它是一个服务端对象,负责所有的Activity的生命周期,ActivityThread会通过Binder与之交互,而AMS与Zygote之间进行交互则是通过Socket通信(IPC通信在之后会总结到)

ActivityThread:它也就是我们俗称的UI线程/主线程,它里面存在一个main()方法,这也是APP的真正入口,当APP启动时,就会启动ActivityThread中的main方法,它会初始化一些对象,然后开启消息循环队列(之后总结),之后就会Looper.loop死循环,如果有消息就执行,没有就等着,也就是事件驱动模型(edt)的原理。

ApplicationThread:它实现了IBinder接口,是Activity整个框架中客户端和服务端AMS之间通信的接口,同时也是ActivityThread的内部类。这样就有效的把ActivityThread和AMS绑定在一起了。

Instrumentation:这个东西我把它理解为ActivityThread的一个工具类,也算是一个劳动者吧,对于生命周期的所有操作例如onCreate最终都是直接由它来执行的。


当我们点击桌面的APP图标时,Launcher进程会采用Binder的方式向AMS发出startActivity请求
AMS在接收到请求之后,就会通过Socket向Zygote进程发送创建进程的请求
Zygote进程会fork出新的子进程(APP进程)
之后APP进程会再向AMS发起一次请求,AMS收到之后经过一系列的准备工作再回传请求。
APP进程收到AMS返回的请求后,会利用Handler向主线程发送LAUNCH_ACTIVITY消息
主线程在收到消息之后,就创建目标Activity,并回调onCreate()/onStart()/onResume()等方法,UI渲染结束后便可以看到App主界面 

死锁的四个必要条件

互斥条件:资源不能被共享,只能被同一个进程使用
请求与保持条件:已经得到资源的进程可以申请新的资源
非剥夺条件:已经分配的资源不能从相应的进程中被强制剥夺
循环等待条件:系统中若干进程组成环路,该环路中每个进程都在等待相邻进程占用的资源
举个常见的死锁例子:进程A中包含资源A,进程B中包含资源B,A的下一步需要资源B,B的下一步需要资源A,所以它们就互相等待对方占有的资源释放,所以也就产生了一个循环等待死锁。

处理死锁的方法

忽略该问题,也就是鸵鸟算法。当发生了什么问题时,不管他,直接跳过,无视它。
检测死锁并恢复
资源进行动态分配
破除上面的四种死锁条件之一


公平锁/非公平锁(多线程执行顺序的维度)
概念理解
公平锁:加锁前先查看是否有排队等待的线程,有的话优先处理排在前面的线程,先来先得。
非公平所:线程加锁时直接尝试获取锁,获取不到就自动到队尾等待。
例子
ReentrantLock 同时支持两种锁

//创建一个非公平锁,默认是非公平锁

Lock nonFairLock= new ReentrantLock();

Lock nonFairLock= new ReentrantLock(false);

//创建一个公平锁,构造传参true

Lock fairLock= new ReentrantLock(true);


适用场景
更多的是直接使用非公平锁:非公平锁比公平锁性能高5-10倍,因为公平锁需要在多核情况下维护一个队列,如果当前线程不是队列的第一个无法获取锁,增加了线程切换次数。
 

乐观锁/悲观锁(多线程操作共享数据的维度)
概念理解
悲观锁:假设一定会发生并发冲突,通过阻塞其他所有线程来保证数据的完整性。
乐观锁:假设不会发生并发冲突,直接不加锁去完成某项更新,如果冲突就返回失败。
例子
悲观锁:Synchronized多线程同步,具有排他性,也会容易产生死锁。
乐观锁:CAS机制,简单来说会有三个操作数,当前内存变量值V,变量预期值A,即将更新值B,当需要更新变量的时候,会直接将变量值V和预期值A进行比较,如果相同,则直接更新为B;如果不相同,则当前变量值V刷新到预期值中,然后重新尝试比较更新。
适用场景
乐观锁:适用于数据争用不严重/重试代价不大/需要相应速度快的场景。
悲观锁:适用于数据争用严重/重试代价大的场景。

Listview和recycleview

listview和recycleview最大的区别就是一个内存的回收机制,以及内存消耗的处理

listview进行下一个条目的时候,会不停的进行绘制.导致内存的剧烈消耗

recycleview的话,内部其实有一个缓存池,他会将前面一条进行缓存,当你来回滚动的时候,并不会进行不停绘制.

listview需要用户手动进行复用处理,而recycleview会进行自动的复用处理

short 占用2byte

int 占用4byte

long 占用8byte

float 占用4byte

double 占用8byte

char 占用2byte

boolean 数据类型表示一位的信息

慎用浮点类型,比起整个性能,整数类型消耗较少

jvm的两种机制,

类装载机制.装载具有适合名称的类或接口

执行引擎,负责执行包含在已装载的类和接口中的指令

jvm包含

方法区,java堆,java栈,本地方法栈,指令计数器和其他隐含寄存器

android广播

动态注册和静态注册

普通广播,

有序广播,具有优先级

粘性广播.可以发送给以后注册的接受者

垃圾回收算法

引用计数法:当对象产生的时候,添加计数器,当对象的计数器为0的时候,进行回收

复制算法,在内存中开辟一个相同的空间,遍历堆,将正在使用的对象进行复制到新开辟的空间,缺点是,复制对象需要一定的开销,并且总会有一块新的空间

标记整理算法,有两个阶段,首先扫描活跃的对象,并且标记,然后清除未标记的对象,

android的fragmework的优先级

空进程

后台进程

服务进程

可见进程

前台进程

最先回收空进程->后台进行->服务进行->可见进程->前台进程

由android中的ams进行统一管理,也就是activitymanagerservice

内存泄漏

什么是内存泄漏,主要是当前对象销毁了,而另一个对象,还持有当前对象的引用,导致gc无法回收,而引起的内存泄漏.

如何处理,在当前对象被回收的时候,将另一个对象的引用也进行回收处理

内存溢出

如何对内存溢出处理,对对象进行弱引用处理,当内存不足,gc调用的时候,该对象就会被回收.

强软弱虚四种引用

view如何防止父类拦截触摸事件

该方法可以在子类通过调用getParent().requestDisallInterceptRouchEvent(true) 来让父布局禁用拦截事件功能,从而父布局忽略该事件之后的一切Action

发布了74 篇原创文章 · 获赞 22 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/ci250454344/article/details/88388967