考核问题

工厂模式(Factory Method)

常用的工厂模式是静态工厂,利用static方法,作为一种类似于常见的工具类Utils等辅助效果,一般情况下工厂类不需要实例化。

单例模式(Singleton)

在内部创建一个实例,构造器全部设置为private,所有方法均在该实例上改动,在创建上要注意类的实例化只能执行一次,可以采用许多种方法来实现,如Synchronized关键字,或者利用内部类等机制来实现。

观察者模式:

在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。

生产消费者模型

生产者消费者模型具体来讲,就是在一个系统中,存在生产者和消费者两种角色,他们通过内存缓冲区进行通信,生产者生产消费者需要的资料,消费者把资料做成产品。

设计模式遵循的原则有6个:

1、开闭原则(Open Close Principle)

对扩展开放,对修改关闭。

2、里氏代换原则(Liskov Substitution Principle)

只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。

3、依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

使用多个隔离的借口来降低耦合度。

5、迪米特法则(最少知道原则)(Demeter Principle)

一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。继承实际上破坏了类的封装性,超类的方法可能会被子类修改。

网络编程

  1. 网络连接为什么要设置连接超时时间、读取时间?
    (1)为了更好的用户体验,避免用户长时间等待连接成功或数据读取完成;
    (2)提高性能,省电,网络访问比较耗费硬件资源、耗电;
    (3)降低服务器端的压力,用户在等待的过程中,可能会频繁尝试重复连接。

  2. 网络下载需要注意什么?
    (1)下载文件的安全性,比如MD5验证;(MD5并不能验证文件的安全性)
    (2)下载过程中网络中断,对下载文件重命名成临时文件,下次继续下载(断点续传),下载完成之后改成正确的文件名。

  3. 注意设置请求超时时间、读取超时时间、闲置超时时间??

  4. 注意整个网络请求(例如HttpURLConnection)都必须使用try-catch-finally包裹;同时注意在finally中调用disconnect来断开连接

动画开发:

概念:

帧动画:将一张张单独的图片进行连续播放,从而在视觉上产生一种动画的效果;有点类似于某些软件制作gif动画的方式

补间动画:补间动画作用的最小元素为View,补间动画的执行并不会真正改变控件的属性值;默认情况下,补间动画的执行速率都是先加速后减速,插值器用来控制动画的执行速率,通过插值器可以修改动画的执行速率。

属性动画:属性动画可以通过直接更改 View 的属性来实现 View 动画。

灭屏等操作时对动画的处理?

灭屏情况下要停止动效;滑动到不可见的情况下也要停止动效

事件分发

什么是事件分发?

从手触摸屏幕开始产生的一系列MotionEvent事件,将事件传到某一具体View的位置,叫做事件分发。

dispatchTouchEvent:时间传递给当前View成功后,会调用此函数

onInterceptTouchEvent:判断是否拦截当前事件

onTouchEvent:处理点击事件;

ACTION_DOWN、ACTION_MOVE、ACTION_UP

View和ViewGroup事件分发流程

考核问题
dispatchTouchEvent(只能往下传递)
可以消费事件
如果返回true,则自己消费掉事件终止传递
如果返回false,不消费事件,交由父的onTouchEvent做处理
如果返回super,不消费事件,将事件派发给onInterceptTouchEvent做处理。

onInterceptTouchEvent(只能往下传递)
不能消费事件
如果返回true,将事件派发给自己的onTouchEvent做处理
如果返回false/super,将事件派发给子的dispatchTouchEvent做处理

onTouchEvent(只能往上传递)
可以消费事件
如果返回true,则自己消费掉事件,终止传递;
如果返回false/super,将事件派发给父的onTouchEvent做处理

大家可以看到,最终消费掉事件的位置只有两个,dispatchTouchEvent和onTouchEvent返回true的时候,而且在它们返回为false的时候,都是将事件交给上层的onTouchEvent来处理,它们一个在onInterceptTouchEvent前,一个在onInterceptTouchEvent后,而onInterceptTouchEvent只是将事件进行分流,这样就构成了这张android事件传递图。

从顶层开始,到顶层结束
事件冲突的解决方式

自定义View

VIew的绘制流程:

View的绘制流程从ViewRootImpl的performTraversals方法开始,在performTraversals方法中会调用performMeasure、performLayout、performDraw三个方法来遍历完成整棵视图树的绘制。

onMeasure:测量View,这个方法可能会多次调用,View的大小由父View和子View同时决定(比如子View的高度设置大于父View);

onLayout:对View进行布局,确定View的位置;(设置View的4个顶点的位置),父View先调用onLayout方法确定父View位置,再调用子View的onLayout方法确定子View的位置。

onDraw:对View进行绘制,在此方法避免耗时操作,大内存对象的分配,大内存分配会导致GC,GC会导致虚拟机暂停,造成卡顿和抖动。

布局优化

include:布局重用(并不能进行布局优化)
merge:排除多余的一层ViewGroup容器
ViewStub:是一个不可见的View类,用于在运行时按需懒加载资源,只有在代码中调用了viewStub.inflate()或者viewStub.setVisible(View.visible)方法时才内容才变得可见

过度绘制

在多层次重叠的UI结构里面,如果不可见的UI也在做绘制的操作,会导致某些像素区域被绘制了多次。这样就会浪费大量的CPU以及GPU资源;

用不同的颜色,来表明一个像素点位被重复绘制的次数;
在开发者设置中打开显示过度绘制区域

过度绘制如何优化?

使用约束布局,减少层级;使用ViewSub、merge;设置View透明度

(1)精简布局的层级,使用merge。注意:include不能精简层级,提高绘制性能,只是代码重用;
(2)重复设置View的背景,重复设置alpha值;
(3)暗色背景的性能比亮色背景的性能要好;
(4)在布局层级相同的情况下,使用ConstraintLayout的性能大于使用RelativeLayout,如果布局层级太多可以考虑自定义view。

Handler运行机制:(需要主线程(或者哪个线程)进行UI操作(或某操作),则调用主线程Handler.sendMessage;将Message消息通过主线程的Handler传递到主线程的MessageQueue中;处理消息需要通过Looper依次取出消息队列的消息回调dispatchMessage方法将消息传递给Handler的handleMessage方法;)

首先在UI线程我们创建了一个Handler实例对象,无论是匿名内部类还是自定义类生成的Handler实例对象,我们都需要对handleMessage方法进行重写,在handleMessage方法中我们可以通过参数msg来写接受消息过后UIi线程的逻辑处理,接着我们创建子线程,在子线程中需要更新UI的时候,新建一个Message对象,并且将消息的数据记录在这个消息对象Message的内部,比如arg1,arg2,obj等,然后通过前面的Handler实例对象调用sendMessge方法把这个Message实例对象发送出去,之后这个消息会被存放于MessageQueue中等待被处理,此时MessageQueue的管家Looper正在不停的把MessageQueue存在的消息取出来,通过回调dispatchMessage方法将消息传递给Handler的handleMessage方法,最终前面提到的消息会被Looper从MessageQueue中取出来传递给handleMessage方法,最终得到处理。这就是Handler机制整个的工作流程。

①Handler:在android中负责发送和处理消息,通过它可以实现其他支线线程与主线程之间的消息通讯。

②Thread:Java进程中执行运算的最小单位,亦即执行处理机调度的基本单位。某一进程中一路单独运行的程序。

③HandlerThread:一个继承自Thread的类HandlerThread,Android中没有对Java中的Thread进行任何封装,而是提供了一个继承自Thread的类HandlerThread类,这个类对Java的Thread做了很多便利的封装。

———————————————————我是分割线—————————————————————

其实这个问题,最主要的关注点还是落在了HandlerThread类上,那么这个类到底有什么作用,所谓的便利封装又体现在哪里?

观察HandlerThread的官方文档的两句:①Thread. Handy class for starting a new thread that has a looper.②The looper can then be used to create handler classes.

释义:HandlerThread对象start后可以获得其Looper对象,并且使用这个Looper对象实例Handler,之后Handler就可以运行在其他线程中了。

Andriod提供了 Handler 和 Looper 来满足线程间的通信。 Handler 先进先出原则。 Looper 类用来管理特定线程内对象之间的消息交换 (MessageExchange) 。

1)Looper: 一个线程可以产生一个 Looper 对象,由它来管理此线程里的 MessageQueue( 消息队列 ) 和对消息进行循环。

2)Handler: 你可以构造 Handler 对象来与 Looper 沟通,以便 push 新消息到 MessageQueue 里 ; 或者接收 Looper 从 Message Queue 取出 所送来的消息。

  1. Message Queue( 消息队列 ): 用来存放线程放入的消息。

  2. Message:是线程间通讯的消息载体。两个码头之间运输货物,Message充当集装箱的功能,里面可以存放任何你想传递的消息。

看到这里就明白了为什么:如果一个线程要处理消息,那么它必须拥有自己的Looper,并不是Handler在哪里创建,就可以在哪里处理消息。

注:对应关系Thread(1):Looper(1):MessageQueen(1):Handler(n).

Srevice

Srevice的启动是通过Intent来启动,如下:
Intenet startIntent = new Intent(this, MySrevice.class);
startService(intent);

停止服务:
Intenet stopIntent = new Intent(this, MySrevice.class);
stopService(intent);

Srevice的绑定也是通过Intent绑定,如下:
Intenet bindIntent = new Intent(this, MySrevice.class);
bindService(bindIntent, connction, BIND_AUTO_CREATE);

解绑服务:
unbindService(connction);

注意:onServiceDisconnected只有在异常解绑的时候才会调用;

Fragment的坑点?

(1)commit和commitAllowingStateLoss方法的区别,最终都调用了BackStackRecord类的commitInternal(boolean allowStateLoss)方法,allowStateLoss:是否允许fragment的状态丢失,commit传的是false,commitAllowingStateLoss传的是true(即允许fragment的状态丢失),如果使用commit方法,系统会调用checkStateLoss()方法检查状态,如果已经保存就会抛出异常can not perform this action after onSaveInstanceState;
(2)异步操作时,getActivity可能为空。getActivity()必须在生命周期onAttach与onDetach之间调用才有效。如果系统内存不足、切换横竖屏、app长时间在后台运行,Activity都可能会被系统回收然后重建,但Fragment并不会随着Activity的回收而被回收,创建的所有Fragment会被保存到Bundle里面,从而导致Fragment丢失对应的Activity。

使用线程池注意什么?

概念:
管理多个线程,避免线程的创建销毁,控制线程的数量
根据线程应用数量衡量是否需要线程池

注意点:
不要每次新建一个线程池,使用权线程池
线程命名规范,方便排查问题

不同线程池的使用场景

考核问题

线程池对任务的处理策略:

核心线程与非核心线程的概念:
核心线程 :固定线程数 可闲置 不会被销毁 ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程

非核心线程数:非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收