将一个复杂对象的构建与它的表示分离,使得一样的构建过程能够建立不一样的表示。java
使用场景好比最多见的 AlertDialog,拿咱们开发过程当中举例,好比 Camera 开发过 程中,可能须要设置一个初始化的相机配置,设置摄像头方向,闪光灯开闭,成 像质量等等,这种场景下就可使用建造者模式 面试
动态的给一个对象添加一些额外的职责,就增长功能来讲,装饰模 式比生成子类更为灵活。装饰者模式能够在不改变原有类结构的状况下曾强类的 功能,好比 Java 中的 BufferedInputStream
包装 FileInputStream
,举个开发中的 例子,好比在咱们现有网络框架上须要增长新的功能,那么再包装一层便可,装 饰者模式解决了继承存在的一些问题,好比多层继承代码的臃肿,使代码逻辑更 清晰算法
还有等等.......观察者模式, 代理模式,门面模式,单例模式,生产者消费者模式。sql
java 中有三种建立线程的方式,或者说四种 数据库
1.继承 Thread 类实现多线程
2.实现Runnable
接口
3.实现Callable
接口
4.经过线程池设计模式
线程池的工做原理: 线程池能够减小建立和销毁线程的次数,从而减小系统资源 的消耗,当一个任务提交到线程池时 缓存
a. 首先判断核心线程池中的线程是否已经满了,若是没满,则建立一个核心线 程执行任务,不然进入下一步
b. 判断工做队列是否已满,没有满则加入工做队列,不然执行下一步
c. 判断线程数是否达到了最大值,若是不是,则建立非核心线程执行任务,否 则执行饱和策略,默认抛出异常安全
Handler
,Message
,looper
和 MessageQueue
构成了安卓的消息机制,handler 创 建后能够经过 sendMessage
将消息加入消息队列,而后 looper
不断的将消息从 MessageQueue
中取出来,回调到 Hander
的 handleMessage
方法,从而实现线程 的通讯。性能优化
从两种状况来讲,第一在 UI 线程建立 Handler,此时咱们不须要手动开启 looper
, 由于在应用启动时,在 ActivityThread
的 main 方法中就建立了一个当前主线程的 looper
,并开启了消息队列,消息队列是一个无限循环,为何无限循环不会 ANR? 由于能够说,应用的整个生命周期就是运行在这个消息循环中的,安卓是由事件 驱动的,Looper.loop
不断的接收处理事件,每个点击触摸或者 Activity 每个 生命周期都是在 Looper.loop
的控制之下的,looper.loop
一旦结束,应用程序的 生命周期也就结束了。咱们能够想一想什么状况下会发生 ANR,第一,事件没有得 处处理,第二,事件正在处理,可是没有及时完成,而对事件进行处理的就是looper
,因此只能说事件的处理若是阻塞会致使 ANR,而不能说 looper
的无限循 环会 ANR网络
另外一种状况就是在子线程建立Handler,此时因为这个线程中没有默认开启的消息 队列,因此咱们须要手动调用 looper.prepare()
,并经过 looper.loop
开启消息 主线程 Looper
从消息队列读取消息,当读完全部消息时,主线程阻塞。子线程 往消息队列发送消息,而且往管道文件写数据,主线程即被唤醒,从管道文件读 取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。所以 loop 的循环并不会对 CPU 性能有过多的消耗。
非静态内部类会持有外部类的引用,若是非静态内部类的实例是静态的,就会长 期的维持着外部类的引用,组织被系统回收,解决办法是使用静态内部类
匿名内部类一样会持有外部类的引用,若是在线程中执行耗时操做就有可能发生 内存泄漏,致使外部类没法被回收,直到耗时任务结束,解决办法是在页面退出 时结束线程中的任务
Handler 致使的内存泄漏也能够被概括为非静态内部类致使的,Handler 内部 message 是被存储在 MessageQueue
中的,有些 message 不能立刻被处理,存在 的时间会很长,致使 handler 没法被回收,若是 handler 是非静态的,就会致使 它的外部类没法被回收,
解决办法是 :
1.使用静态 handler,外部类引用使用弱引 用处理
2.在退出页面时移除消息队列中的消息
根据场景肯定使用 Activity 的 Context 仍是 Application
的 Context
,由于两者生命周 期不一样,对于没必要须使用 Activity 的 Context 的场景(Dialog),一概采用 Application
的 Context
,单例模式是最多见的发生此泄漏的场景,好比传入一个 Activity 的 Context 被静态类引用,致使没法回收.
使用静态 View 能够避免每次启动 Activity 都去读取并渲染 View,可是静态 View会持有 Activity 的引用,致使没法回收,解决办法是在 Activity 销毁的时候将静态 View 设置为 null(View 一旦被加载到界面中将会持有一个 Context 对象的引用, 在这个例子中,这个 context 对象是咱们的 Activity,声明一个静态变量引用这个 View,也就引用了 activity)
WebView
只要使用一次,内存就不会被释放,因此 WebView
都存在内存泄漏的 问题,一般的解决办法是为 WebView
单开一个进程,使用 AIDL
进行通讯,根据 业务需求在合适的时机释放掉
如 Cursor,File 等,内部每每都使用了缓冲,会形成内存泄漏,必定要确保关闭 它并将引用置为 null
集合用于保存对象,若是集合愈来愈大,不进行合理的清理,尤为是入股集合是 静态的
bitmap 是比较占内存的,因此必定要在不使用的时候及时进行清理,避免静态 变量持有大的 bitmap 对象
不少须要 register
和 unregister
的系统服务要在合适的时候进行 unregister
,手动添 加的 listener
也须要及时移除
1.使用更加轻量的数据结构: 如使用 ArrayMap/SparseArray
替代 HashMap
,HashMap
更耗内存,由于它须要额外的实例对象来记录 Mapping
操做, SparseArray
更加高效,由于它避免了 Key Value
的自动装箱,和装箱后的解箱操 做
2.便面枚举的使用,能够用静态常量或者注解@IntDef 替代
3.Bitmap 优化:
a.尺寸压缩: 经过 InSampleSize
设置合适的缩放
b.颜色质量: 设置合适的 format,ARGB_6666/RBG_545/ARGB_4444/ALPHA_6,存在很大差别.
c.inBitmap: 使用 inBitmap
属性能够告知 Bitmap
解码器去尝试使用已经存在的内 存区域,新解码的 Bitmap
会尝试去使用以前那张 Bitmap 在 Heap 中所占据的 pixel data 内存区域,而不是去问内存从新申请一块区域来存放 Bitmap。利用这种特 性,即便是上千张的图片,也只会仅仅只须要占用屏幕所可以显示的图片数量的 内存大小,但复用存在一些限制,具体体如今:在 Android 4.4 以前只能重用相 同大小的 Bitmap 的内存,而 Android 4.4 及之后版本则只要后来的 Bitmap 比以前 的小便可。使用 inBitmap
参数前,每建立一个 Bitmap 对象都会分配一块内存供 其使用,而使用了 inBitmap
参数后,多个 Bitmap 能够复用一块内存,这样能够 提升性能
4.StringBuilder
替代 String: 在有些时候,代码中会须要使用到大量的字符串拼接 的操做,这种时候有必要考虑使用 StringBuilder
来替代频繁的“+”
5.避免在相似 onDraw
这样的方法中建立对象,由于它会迅速占用大量内存,引 起频繁的 GC 甚至内存抖动
6.减小内存泄漏也是一种避免 OOM 的方法
onRestart
的调用场景(1)按下 home 键以后,而后切换回来,会调用
onRestart()
。
(2)从本 Activity 跳转到另外一个 Activity 以后,按 back 键返回原来 Activity,会 调用onRestart()
;
(3)从本 Activity 切换到其余的应用,而后再从其余应用切换回来,会调用onRestart()
;
说下 Activity 的横竖屏的切换的生命周期,用那个方法来保存数据,二者的区别。 触发在何时在那个方法里能够获取数据等。
a: Service 设置成 START_STICKY kill 后会被重启(等待 5 秒左右),重传 Intent,保 持与重启前同样
b: 经过startForeground
将进程设置为前台进程, 作前台服务,优先级和前台 应用一个级别,除非在系统内存很是缺,不然此进程不会被 kill
c: 双进程 Service: 让 2 个进程互相保护对方,其中一个 Service 被清理后,另 外没被清理的进程能够当即重启进程
d: 用 C 编写守护进程(即子进程) : Android 系统中当前进程(Process)fork 出来的子 进程,被系统认为是两个不一样的进程。当父进程被杀死的时候,子进程仍然能够 存活,并不受影响(Android5.0 以上的版本不可行)联系厂商,加入白名单
e. 锁屏状态下,开启一个一像素 Activity
1.耗时的网络访问
2.大量的数据读写
3.数据库操做
4.硬件操做(好比 camera)
5.调用 thread 的join()
方法、sleep()
方法、wait()
方法或者等待线程锁的时候
6.service binder 的数量达到上限
7.system server 中发生WatchDog ANR
8.service 忙致使超时无响应
9.其余线程持有锁,致使主线程等待超时
10.其它线程终止或崩溃致使主线程一直等待
当 Android 端须要得到数据时好比获取网络中的图片,首先从内存中查找(按键 查找),内存中没有的再从磁盘文件或 sqlite
中去查找,若磁盘中也没有才经过 网络获取
线程就是进程中运行的多个子任务,是操做系统调用的最小单元
New: 新建状态,new 出来,尚未调用 start
Runnable: 可运行状态,调用 start 进入可运行状态,可能运行也可能没有运行, 取决于操做系统的调度
Blocked: 阻塞状态,被锁阻塞,暂时不活动,阻塞状态是线程阻塞在进入synchronized 关键字修饰的方法或代码块(获取锁)时的状态。
Waiting: 等待状态,不活动,不运行任何代码,等待线程调度器调度,wait sleep
Timed Waiting: 超时等待,在指定时间自行返回
Terminated: 终止状态,包括正常终止和异常终止
a.继承 Thread 重写 run 方法
b.实现 Runnable
重写 run 方法
c.实现 Callable 重写 call 方法 实现 Callable 和实现 Runnable
相似,可是功能更强大,具体表如今
Runnable
不行Runnable
的 run 方法不行Fulture
对象监听目标线程调用 call 方法的结果, 获得返回值,(fulture.get()
,调用后会阻塞,直到获取到返回值)通常状况下,线程不执行完任务不会退出,可是在有些场景下,咱们须要手动控 制线程中断结束任务,Java 中有提供线程中断机制相关的 Api,每一个线程都一个状 态位用于标识当前线程对象是不是中断状态
public boolean isInterrupted() //
判断中断标识位是不是 true,不会改变标 识位 public void interrupt() //
将中断标识位设置为 truepublic static boolean interrupted() //
判断当前线程是否被中断,而且该方法调用结束的时 候会清空中断标识位
须要注意的是 interrupt()
方法并不会真的中断线程,它只是将中断标识位设置 为 true,具体是否要中断由程序来判断,以下,只要线程中断标识位为 false,也就 是没有中断就一直执行线程方法
new Thread(new Runnable(){ while(!Thread.currentThread().isInterrupted()){ //执行线程方法 } }).start();
前边咱们提到了线程的六种状态,New Runnable Blocked Waiting Timed Waiting Terminated
,那么在这六种状态下调用线程中断的代码会怎样呢,New 和 Terminated 状态下,线程不会理会线程中断的请求,既不会设置标记位,在 Runnable
和 Blocked 状态下调用 interrupt 会将标志位设置位 true,在 Waiting 和 Timed Waiting 状态下会发生 InterruptedException
异常,针对这个异常咱们如何 处理?
1.在 catch 语句中经过 interrupt
设置中断状态,由于发生中断异常时,中断标志 位会被复位,咱们须要从新将中断标志位设置为 true,这样外界能够经过这个状 态判断是否须要中断线程
try{ .... }catch(InterruptedException e){ Thread.currentThread().interrupt(); }
2.更好的作法是,不捕获异常,直接抛出给调用者处理,这样更灵活
从 SUN 的官方文档能够得知,调用 Thread.stop()
方法是不安全的,这是由于当调 用 Thread.stop()
方法时,会发生下面两件事:
ThreadDeath
异常,在线程的 run()
方法内,任何一点都有可能抛出 ThreadDeath Error
,包括在 catch
或 finally
语句中。 1.地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地 址空间。
2.资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu 等,可是进 程之间的资源是独立的。
3.一个进程崩溃后,在保护模式下不会对其余进程产生影响,可是一个线程崩溃 整个进程都死掉。因此多进程要比多线程健壮。
4.进程切换时,消耗的资源大,效率不高。因此涉及到频繁的切换时,使用线程 要好于进程。一样若是要求同时进行而且又要共享某些变量的并发操做,只能用 线程不能用进程
5.执行过程:每一个独立的进程程有一个程序运行的入口、顺序执行序列和程序入 口。可是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程 执行控制。
6.线程是处理器调度的基本单位,可是进程不是。
7.二者都可并发执行。
未完待续......
更多内容的面试汇总PDF版本,含有BATJ.字节跳动面试专题,算法专题,高端技术专题,混合开发专题,java面试专题,Android,Java小知识,到性能优化.线程.View.OpenCV.NDK。
Android面试大全+视频教程+学习笔记