-
1、引入AOP相关插件
在项目主目录当中引入AOP插件
dependencies{ classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4' }
接着在需要引入AOP的model引入:apply plugin: 'android-aspectjx'
-
2、运用
a、创建一个类ActivityAop,并给ActivityAop打上注解@Aspect,这样则表示ActivityAop类是要实现切面编程的类,该类则是用于处理触发切面的回调
b、编写方法method,并给方法打上@Pointcut注解切点,用于表示当Android执行到切点的方法时,执行method方法
//@Pointcut("execution(* @com.huazhen.HZQG.aop.anno.AuthorityRefresh *.onResume())") //@Pointcut("execution(@com.example.administrator.aspectjdemo.AspectJAnnotation * *(..))") @Pointcut("execution(* android.view.View$OnClickListener.onClick(..))") public void method(){ }
该方法只需要空实现即可,因为在后面的@Around注解当中,会对该方法进行处理;所配置的值为完整类名称+方法名
c、编写aroundMethod方法,打上@Around注解,里面的值则为以上method的方法名称
@Around("method()") public void arroundMethod(ProceedingJoinPoint joinPoint) throws Throwable { //joinPoint.proceed(); //ShenceStatisticsKt.shencestaView((View) joinPoint.getArgs()[0]); /** *这里执行想要method执行的操纵,如以上埋点例子 **/ }
Around是指JPoint执行前或执行后备触发,而around就替代了原JPoint,既注解执行了onClick方法,则会执行method方法,而method方法具体执行什么内容,则由arroundMethod执行。
总结:以上通俗一点的原理则是AOP在编译期时候,通过反射找到切点@Pointcut所配置的方法m,生成@Around当中的执行代码插入到m中,除了Around环绕方式插入,还有@Before、@After、@AfterReturning和@AfterThrowing等方式插入
#注:以上“**”表示是任意包名;“..”表示任意类型任意多个参数;
#参考文献:Android进阶系列之AOP面向切面编程
##2、glide缓存机制+拓展问题解答
-
glide缓存机制:
Glide采用的是三级缓存,内存–>磁盘–>网络\
作用:
A:内存缓存的主要作用 : 是防止应用重复将图片数据读取到内存当中,
B:磁盘缓存的主要作用 : 是防止应用重复从网络或其他地方重复下载和读取数据。
C:Glide内存缓存的实现自然也是使用的LruCache算法。并且还结合了一种弱引用的机制,共同完成了内存缓存功能
-
1、Glide的缓存资源分为两种
原图:原始图片
处理图:经过压缩和变形等处理后的图片
-
2、内存缓存策略
Glide默认会在内存中缓存处理图,可通过调用skipMemoryCache(true)来设置跳过内存缓存
-
3、磁盘缓存策略
a.ALL:缓存原图(SOURCE)和处理图(RESULT)
b.NONE:什么都不缓存
c.SOURCE:只缓存原图(SOURCE)
d.RESULT:只缓存处理图(RESULT) —默认值
-
4、组合策略
跟三级缓存顺序一样
-
-
拓展问题
-
1、加载大图片,列表多张图片加载,如ListView\GridView\RecycleView等
情况:加载大图或者使用以上类似控件显示图片时候,屏幕上滑动会不断的增加事件和加载图片,最终会导致OOM
处理:
-
加载大图时候会报OOM,就直接将图片进行压缩显示,步骤是获取图片大小,根据需求和项目确定图片最大压缩范围,再最终根据图片大小决定是直接把图片加载到内存当中还是加载一个压缩版的图片到内存当中显示。
-
列表加载多张图片时候,使用内存缓存技术+压缩图片+显示缩略图或者非高清图等,使用LruCache(V4包提供),主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。为了能够选择一个合适的缓存大小给LruCache, 有以下多个因素应该放入考虑范围内,如下:
A、你的设备可以为每个应用程序分配多大的内存?
B、设备屏幕上一次最多能显示多少张图片?有多少图片需要进行预加载,因为有可能很快也会显示在屏幕上?
C、你的设备的屏幕大小和分辨率分别是多少?一个超高分辨率的设备(例如 Galaxy Nexus) 比起一个较低分辨率的设备(例如 Nexus S),在持有相同数量图片的时候,需要更大的缓存空间。
D、图片的尺寸和大小,还有每张图片会占据多少内存空间。
E、图片被访问的频率有多高?会不会有一些图片的访问频率比其它图片要高?如果有的话,你也许应该让一些图片常驻在内存当中,或者使用多个LruCache 对象来区分不同组的图片。
F、你能维持好数量和质量之间的平衡吗?有些时候,存储多个低像素的图片,而在后台去开线程加载高像素的图片会更加的有效。
最终,具体情况由需求分析决定制定出一个合适的方案。
-
-
2、Glide生命周期
分析源码可知道,Glide对生命周期不需要做任何特殊处理,因为Glide和APP生命周期同步的;假如APP没有被干掉,那么Glide也是和Fragment和Activity同步的
-
##3、数据持久化(数据存储)
-
1、数据存储分类
网络存储、数据库存储、文件存储[外部文件存储(External Storage)、内部文件存储(Internal Storage)]、SharedPreferences、ContentProvider
-
1、文件存储
内部存储:内部文件存储则是文件为APP私有的,将通过文件的形式将数据保存于手机内部存储空间(磁盘)当中,当APP删除,内部文件也跟着删除,文件路径是:/data/data/…/files/
外部存储:外部文件则是保存在SD卡内,Android6.0以上要增加相关的文件读去权限,存储位置可自定
-
2、ContentProvider:内容提供者
解释:ContentProvider内容提供者本来不属于数据持久化范畴,但它却属于数据存储的范畴,它主要用于应用程序(APP)之间的数据交换;具体如何使用看参考文献:https://blog.csdn.net/shaochen2015821426/article/details/79748487
理解:内容提供者相当于生成一个公共数据表格TABLE,APP_A和APP_B之间通过TABLE之间进行增删改查数据
-
##4、Observable-Observer===观察者模式
-
1、概念
观察者模式(Observer Pattern)定义了对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新,观察者模式又叫做发布-订阅(Publish/Subscribe)模式。
-
2、使用
-
a、定义一个可被观察的发布类,该类继承于java.util.Observable
public class ForSocketEvent extends Observable{ //构造方法 public static ForSocketEvent getInstance(){} //自定义方法监听监听,更新观察者方法,作为观察者的监听 public void toListen(){ setChanged(); notifyObservers(new NotifyCmd(1,"巴拉巴拉")); } public class NotifyCmd { public final int type; public final Object data; NotifyCmd(NotifyType type, Object data) { this.type = type; this.data = data; } } }
-
b、定义一个观察者类,该类主要用于订阅发布类发布的内容,该类继承于java.util.Observer
public class ForSocketListener implements Observer { private ForSocketEvent mEvent; public ForSocketListner(){ mEvent = ForSocketEvent.getInstance(); //订阅观察者 mEvent.addObserver(this); } /** * 实现该方法,数据的更新从这里回调到UI去 **/ @Override public void update(Observable o, Object arg){ if (o instanceof ForSocketEvent) { ForSocketEvent.NotifyCmd cmd = (ForSocketEvent.NotifyCmd) arg; //做判断并通过接口或者其他方式将cmd当中的数据回调出去 } } }
-
c、有了a和b步骤,如何才能触发b当中的update方法
操作:ForSocketEvent.getInstance().toListen()
通过以上该操作,即可通知发布者数据发生了变动,发布者立马更新并刷新观察者,观察者当中已经addObserver订阅,就可接收到刷新的内容并执行update方法
-
##5、http需要三次握手吗,灵魂拷问
-
1、直接明了答案
HTTP是TCP/IP协议,而三次握手则是TCP协议建立过程中不可或缺的步骤;故而,http虽然只是发送和响应协议,但依然是需要三次握手建立连接。
-
2、三次握手分别是什么
第一次握手: 客服端向服务器端发送一个syc(同步包) 服务器是监听状态
第二次握手 服务器端接受到syc(包),并向客户端发送ack(确认包:表示我收到了你发送的syc包 了) 总共发送的包 syc+ack 服务器是接受状态
第三次握手 客户端收到syc+ack包,并且再向服务器端发送ack,表示我知道你收到我发送的包,此时 服务器端收到ack包服务器端表示 我知道你收到我发送的确定包了。此时是建立连接状态。
三次握手时序图
-
3、为何要三次握手
为了防止服务端开启一些无用的链接,网络传输是有延时的,中间可能隔着非常远的距离,通过光纤或者中间代理服务器等,客户端发送一个请求,服务端收到之后如果直接创建一个链接,返回内容给到客户端,因为网络传输原因,这个数据包丢失了,客户端就一直接收不到服务器返回的这个数据,超过了客户端设置的时间就关闭了,那么这时候服务端是不知道的,它的端口就会开着等待客户端发送实际的请求数据,服务这个开销也就浪费掉了
##6、okhttp请求能有多少个子线程
-
1、okhttp内部实现的是无边界限制的线程池
没有核心线程数
线程池可容纳:Integer.MAX_VALUE个线程
空闲线程被终止的等待时间:60s
-
2、okhttp默认支持并发5个相同ip地址的上传文件请求
-
3、异步调用流程
okhttp默认maxRequests=64个最大连接数,maxRequestsPerHost=5个同host的最大连接数
当有异步调用时,需要遵守当前运行的同步AsyncCall<maxRequests&&同一个Host对应的AsyncCall<maxRequestsPerHost才允许进入请求队列当中
-
4、同步调用流程
@Override public Response execute() throws IOException {}
同步请求总结:直接进入拦截器链流程进行请求,获取Response
#参考文献:OKHttp全解析系列(四) -- 线程池和消息队列
##7、数据库左右关联的区别
总结:
left join(左联接) 返回包括左表中的所有记录和右表中关联字段相等的记录
right join(右联接) 返回包括右表中的所有记录和左表中关联字段相等的记录
inner join(等值连接) 只返回两个表中关联字段相等的行
#参考文献: