Android动态换肤实现原理解析,详细解说

## 1.说下你所知道的设计模式与使用场景 建造者模式: 观察者模式: 代理模式: 门面模式: 单例模式: 生产者消费者模式: ## 2.Java语言的特点与OOP思想 这个通过对比来描述,比如面向对象和面向过程的对比,针对这两种思想的对比,还可以举个开发中的例子,比如播放器的实现,面向过程的实现方式就是将播放视频的这个功能分解成多个过程,比如,加载视频地址,获取视频信息,初始化解码器,选择合适的解码器进行解码,读取解码后的帧进行视频格式转换和音频重采样,然后读取帧进行播放,这是一个完整的过程,这个过程中不涉及类的概念,而面向对象最大的特点就是类,封装继承和多态是核心,同样的以播放器为例,一面向对象的方式来实现,将会针对每一个功能封装出一个对象,每一个功能对应一个对象,由这个对象来完成对应的功能,并且遵循单一职责原则,一个对象只做它相关的事情 ## 3.说下java中的线程创建方式,线程池的工作原理。 Java中有三种创建线程的方式,或者说四种 1.继承Thread类实现多线程 2.实现Runnable接口 3.实现Callable接口 4.通过线程池 线程池的工作原理: 线程池可以减少创建和销毁线程的次数,从而减少系统资源的消耗,当一个任务提交到线程池时. a. 首先判断核心线程池中的线程是否已经满了,如果没满,则创建一个核心线程执行任务,否则进入下一步. b. 判断工作队列是否已满,没有满则加入工作队列,否则执行下一步. c. 判断线程数是否达到了最大值,如果不是,则创建非核心线程执行任务,否则执行饱和策略,默认抛出异常. ## 4.说下handler原理 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性能有过多的消耗。 ## 5.内存泄漏的场景和解决办法 **1.非静态内部类的静态实例** 非静态内部类会持有外部类的引用,如果非静态内部类的实例是静态的,就会长期的维持着外部类的引用,组织被系统回收,解决办法是使用静态内部类 **2.多线程相关的匿名内部类和非静态内部类** 匿名内部类同样会持有外部类的引用,如果在线程中执行耗时操作就有可能发生内存泄漏,导致外部类无法被回收,直到耗时任务结束,解决办法是在页面退出时结束线程中的任务 **3.Handler内存泄漏** Handler导致的内存泄漏也可以被归纳为非静态内部类导致的,Handler内部message是被存储在MessageQueue中的,有些message不能马上被处理,存在的时间会很长,导致handler无法被回收,如果handler是非静态的,就会导致它的外部类无法被回收, 解决办法是 1.使用静态handler,外部类引用使用弱引用处理 2.在退出页面时移除消息队列中的消息 **4.Context导致内存泄漏** 根据场景确定使用Activity的Context还是Application的Context,因为二者生命周期不同,对于不必须使用Activity的Context的场景(Dialog),一律采用Application的Context,单例模式是最常见的发生此泄漏的场景,比如传入一个Activity的Context被静态类引用,导致无法回收 **5.静态View导致泄漏** 使用静态View可以避免每次启动Activity都去读取并渲染View,但是静态View会持有Activity的引用,导致无法回收 解决办法是在Activity销毁的时候将静态View设置为null(View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity) **6.WebView导致的内存泄漏** WebView只要使用一次,内存就不会被释放,所以WebView都存在内存泄漏的问题,通常的解决办法是为WebView单开一个进程,使用AIDL进行通信,根据业务需求在合适的时机释放掉 **7.资源对象未关闭导致** 如Cursor,File等,内部往往都使用了缓冲,会造成内存泄漏,一定要确保关闭它并将引用置为null **8.集合中的对象未清理** 集合用于保存对象,如果集合越来越大,不进行合理的清理,尤其是入股集合是静态的 **9.Bitmap导致内存泄漏** bitmap是比较占内存的,所以一定要在不使用的时候及时进行清理,避免静态变量持有大的bitmap对象 **10.监听器未关闭** 很多需要register和unregister的系统服务要在合适的时候进行unregister,手动添加的listener也需要及时移除 ## 6.如何避免OOM? **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可以复用一块内存,这样可以提高性能 d.StringBuilder替代String: 在有些时候,代码中会需要使用到大量的字符串拼接的操作,这种时候有必要考虑使用StringBuilder来替代频繁的“+” e.避免在类似onDraw这样的方法中创建对象,因为它会迅速占用大量内存,引起频繁的GC`甚至内存抖动 f.减少内存泄漏也是一种避免OOM的方法 ### 最后 为了方便有学习需要的朋友,我把资料都整理成了视频教程(实际上比预期多花了不少精力),由于篇幅有限,都放在了我的GitHub上,点击即可免费获取! **[Androidndroid架构视频+BAT面试专题PDF+学习笔记](https://github.com/a120464/Android-P7/blob/master/Android%E5%BC%80%E5%8F%91%E4%B8%8D%E4%BC%9A%E8%BF%99%E4%BA%9B%EF%BC%9F%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)** ![](http://www.icode9.com/i/li/?n=2&i=images/20210708/1625675768700233.jpg) 当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。 * 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!! * 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。 当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,没有人能随随便便成功。 **加油,共勉。**
上一篇:Android岗面试必问!熬夜肝完这份Framework笔记


下一篇:Android面试:Handler八大问题汇总,2021金三银四面试季