学习笔记(二)
自定义控件
继承现有的控件,如textview,edittext,
继承容器控件,一般是几个控件组合起来组成一个新的控件,把它们之间的逻辑封装起来,对外通过回调提供新的功能。
继承view。自定义View一般有三个步骤,首先是测量,然后布局,最后绘制,对应的三个方法是onMeasure(),onLayout(),onDraw()。
网络优化:
1、用Gzip来压缩请求和回应,以减少传输数据量;
2、图片加载,后台提供缩略图和原图的路径,比如头像和用户上传的图片,在用户浏览时加载缩略图,当用户点击查看大图时加载原图;
3、弱网络测试和优化,设置模拟器。断点续传,新建一个类记录下载文件的url,开始位置,长度,当前进度,把文件下载的进度保存进数据库。下载文件时先从数据库查询是否有下载记录,如果有就继续下载,如果没有,就新建线程,先获取文件的大小,然后在本地新建一个同样大小的文件,通过randomAccessFile来写进下载内容,同时更新下载进度。
静态代理和动态代理的区别
静态代理由程序员编写代理类代码。动态代理类,在程序运行时,运用反射机制动态创建而成。
synchronized 和volatile 关键字的区别
1.volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
2.volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
3.volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
4.volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
5.volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
非静态内部类会持有外部对象的引用,而静态内部类不会持有外部对象的引用。因为非静态内部类方法的调用需要依赖外部对象的创建。
MVP和MVC的区别和优缺点
MVP中View层和Model层没有直接通信,而是通过Persenter来间接通信
MVC中View层和Model层直接通信。
MVP优点:
1、使用MVP代码架构,对代码实施分层管理。
2、如果界面发生变化,只要修改View就可以了,不需要修改Persenter和Model层。
3、同样如果逻辑层或数据层需要修改,只修改对应的Persenter和Model就可以了。
4、Activity职责更明确
MVP缺点:
1、增加了代码类的数量。
2、由于进行了代码三层划分,函数的调用栈变深了,如果开发人没没有很清楚地了解每一层的功能,可能会导致其他层次的代码乱入,从而没有达到MVP充分解藕各层的目的。
注解
元注解:注解的注解
@Target 指定注解所适用的对象范围
@Retention 注解的生命周期 RetentionPolicy.SOURCE, RetentionPolicy.CLASS, RetentionPolicy.RUNTIME
RetentionPolicy.SOURCE(源码注解),注解保留在源代码中,但是编译的时候会被编译器所忽略。编译时不处理注解,但自定义注解处理器时可以处理此注解 。使用场景:使用@IntDef代替枚举。
RetentionPolicy.CLASS(编译时注解),这是默认的policy。注解会被保留在class文件中,但是在运行时期间就不会识别这个注解。使用场景:可以自定义注解处理器来处理注解生成更多源码。三种注解都可以通过注解处理器来处理。
RetentionPolicy.RUNTIME(运行时注解),注解会被保留在class文件中,同时运行时期间也会被识别。使用场景:使用反射机制获取注解信息。
多进程使用场景
1、类似音乐类、跑步健身类、手机管家类等长时间需要在后台运行的应用。
2、为了加大一个应用程序可使用的内存,可以通过多进程来获得多份内存空间。
这些应用的特点就是,当用户切到别的应用,或者关掉手机屏幕的时候,应用本身的核心模块还在正常运行,提供服务。如果因为手机内存过低,或者是进程重要性降低,导致应用被杀掉,后台服务停止,对于这些应用来说,就是灭顶之灾。合理利用多进程,将核心后台服务模块和其他UI模块进行分离,保证应用能更稳定的提供服务,从而提升用户体验。
多进程还有一种非常有用的场景,就是多模块应用。比如我做的应用大而全,里面肯定会有很多模块,假如有地图模块、大图浏览、自定义WebView等等(这些都是吃内存大户),一个成熟的应用一定是多模块化的。首先多进程开发能为应用解决了OOM问题,因为Android对内存的限制是针对于进程的,所以,当我们需要加载大图之类的操作,可以在新的进程中去执行,避免主进程OOM。
动画
1、补间动画 通过透明度、缩放、移动、旋转的变化来实现动画效果。
2、帧动画 多张图片按一定顺序播放形成动画。
3、属性动画 在一定时间内,通过不断地对值进行改变,并使用该值来改变view的属性,从而实现动画效果。
属性动画和补间动画的区别
1、补间动画不改变view的属性,只改变视角效果,属性动画可改变view的属性。
2、补间动画只能实现透明度、缩放、移动和旋转的动画效果,属性动画可以实现更丰富的动画效果。
应用的启动过程
冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用。
冷启动的特点:因为系统会重新创建一个新的进程分配给它,所以会创建和初始化Application,在创建和初始化它的Launch Activity(onCreate onMesure onLayout,ondraw),最后展示在界面上
热启动:当启动应用时,后台存在该应用的进程(back键,home键,应用退出,但是没有销毁),从已有的进程中启动
热启动的特点:从已有的进程中启动,不需要创建和初始化Application ,直接创建和初始化它的Launch Activity
两者区别:后台进程是否存在相应的进程,创建进程是耗时操作
冷启动的情况(分配进程->创建和初始化Application->创建和初始化Launch Activity)
一、Launcher通过Binder进程间通信机制通知ActivityManagerService, 它要启动一个Activity;
二、ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态;
三、 Launcher通过Binder进程间通信机制通知ActivityManagerService,它已经准备就绪进入Paused状态, 于是ActivityManagerService就创建一个新的进程,用来启动一个ActivityThread实例, 即将要启动的Activity就是在这个ActivityThread实例中运行;
四、 ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService, 以便以后ActivityManagerService能够通过这个Binder对象和它进行通信;
五、 ActivityManagerService通过Binder进程间通信机制通知ActivityThread, 现在一切准备就绪,它可以真正执行Activity的启动操作了。
activity的启动过程
Activity的启动过程分从桌面启动App的根Activity,和当前Activity启动Activity。
Activity的启动过程,执行了四次的IPC。第一次由发起方调用,activity调用startActivityForResult()启动新的activity时,通过ActivityManager进行一次IPC,发送到ActivityManagerService。ActivityManagerService给当前Activity发送暂停的信号,这也是一个IPC过程,ActivityThread通过Handler来处理这个暂停操作。Activity暂停后,会第三次执行IPC通知ActivityManagerService可以启动新的Activity。ActivityManagerService第四次执行IPC通过进程启动Activity。启动activity的一个重要工具是instrumentation。它主要负责activity的创建,启动和生命周期的回调。
Glide的优势
1、可配置度高,自适应度高
2、支持多种数据源,本地,网络,assets,gif在glide是支持的
3、高效缓存,支持memory和disk图,默认使用二级缓存
4、生成周期集成到glide
5、高效处理bitmap:使用bimtp pool复用bitmap
6、图片加载过程可以监听
TCP 协议 https://blog.csdn.net/javasxl/article/details/82774803
TCP的三次握手
第一次握手,客户端发起连接,请求报文的SYN(同步)位置1,客户端进入SYN_SENT(同步发送)状态
第二次握手,服务端接收到连接请求,如果同意连接,就发送确认报文,SYN位置1,ACK位置1,服务端进入SYN_RCVD(同步接收)状态
第三次握手,客户端接收到确认报文,客户端发送确认报文,ACK位置1,这时客户端进入建立连接状态。服务端接收到报文也进入建立连接状态。三次握手完成
为什么要进入三次握手
为了防止失效了的请求连接又传到服务端,而产生错误。客户端发送的请求连接报文在网点滞留了,因而客户端认为报文失效,就又发起第二次请求连接,这次服务端接收到报文,并建立了连接。这时滞留的请求报文到达服务端,服务端也返回确认报文,但由于客户端认为该报文已失效,就不会对这个报文进行应答,也就没有建立连接。
四次挥手
客户端和服务端都可以主动发起断开。
以客户端为例,第一次挥手,客户端发送FIN报文,FIN位置1,客户端进入FIN_WAIT_1(结束等待1)状态
第二次挥手,服务端接收到FIN报文,返回应答报文,ACK位置1,客户端接收到应答报文后进入FIN_WAIT_2状态
这时服务端还有数据发送给客户端,把这些数据发送完后
第三次挥手,服务端发送FIN报文,FIN位置1,ACK位置1,服务端进入LAST_ACK(最后确认)状态
第四次挥手,客户端收到FIN报文,返回应答报文,ACK位置1,客户端进入TIME_WAIT状态,等待2个MSL后进入CLOSE状态,服务端应答报文,进入CLOSE状态。1个MSL(最长报文段寿命)为2分钟,2个MSL为4分钟。
为什么要等待2个MSL
客户端发送FIN确认报文后,无法确认服务端是否接收到报文。如果服务端没有接收到这个报文,就会重新发送FIN请求报文,然后客户端重新发关确认报文。这个客户端等待的时间就为2个MSL。
为什么说TCP是可靠的连接
因为TCP的三次握手过程,很大程度防止了错误连接的产生,所以TCP是可靠的。接收方是按顺序接收数据。
为什么说TCP是面向的连接
因为TCP在传输数据前要建立连接,结束传输也要断开连接。
UDP是无连接的、不可靠的一种数据传输协议,TCP是面向连接的。
UDP 是数据格式基于数据报,TCP 是面向字节流。
UDP 是不处理堵塞,应用需要发,就会发送。TCP 还拥有堵塞控制,TCP 会根据网络环境调整发包的频率。
UDP 支持多播和广播
TCP协议所需资源多,TCP首部需20个字节(不算可选项),UDP首部字段只需8个字节。
TCP有流量控制和拥塞控制,UDP没有,网络拥堵不会影响发送端的发送速率
Socket 是对 TCP/IP 协议族的一种封装,是应用层与TCP/IP协议族通信的中间软件抽象层。有基于TCP的流套接字,和基于UDP的数据报套接字
长连接
HTTP数据传输完成了还保持TCP连接不断开
Binder机制
Binder是进程间通信的一种方式。Binder通信采用C/S架构,包括Client,Server,ServiceManager和Binder驱动。Client为使用服务的进程,Server为提供服务的进程,ServiceManager的作用是作为Client和Server的连接桥梁,Client可以通过ServiceManager获取Server中Binder实体的引用,从而使用Server提供的服务。Client,Server,ServiceManager运行在用户空间,Binder驱动运行在内核空间。前三者通过通过Binder驱动进行交互。
Client调用bindService()方法,通过binder从ServiceManager找到相应的服务端,并返回服务端的binder对象,client就是通过这个binder与服务端通信。client调用binder的transact()方法,把数据写入data变量,服务端通过onTransact()方法接收数据,处理完后把结果写入reply,回传给Client。
如何提高代码质量
尽量不要过于复杂的嵌套。可以使用<include>,<merge>,<ViewStub>。使用viewStub可以实现延时加载(懒加载)
注意内存泄露和内存溢出的发生,同时使用LeakCanary查找内存泄漏的地方。
使用monkey工具来对app进行压力测试。
使用合适的设计模式,达到代码低耦合。
插件化的原理
这个功能核心的地方是DexClassloader。使用DexClassLoader加载我们生成的插件,然后通过反射得到AssetsManager,再生成Resources,这三个步骤就可以访问到插件中的类和资料文件。然后创建一个Activity作为宿主,改造里面的一些方法,比如生命同期相关的方法,使之调用插件里的对应的方法。当中要注意的是不能调用系统的setContetnView()来设置界面,要使用插件的resources取得布局ID,并实例化,最后setContentView()。根据需求,我们的APP要支持多种语言,那原来的一些功能就需要改成,比如说中文的搜索算法就不能用于英文。我的做法是把原来算法的类替换成新的类。系统是有个PathClassLoader的,他的父ClassLoader是BootClassLoader,我在他们之间插入一个自定义的ClassLoader,根据ClassLoader的双亲委托机制,PathClassLoader加载类之前会调用父CloassLoader的loadClass()方法,如果没有加载过父ClassLoader会调用findClass方法加载。利用这个机制,在自定义的ClassLoader,复写findClass()方法,把原来要加载的类名替换成新的类名,并从插件中加载,这样就达到替换的作用。同时这个框架支持多个插件。
eventbus
注册过程第一步是获得类中监听的方法列表。使用了注解+反射的方案。调用register()方法时,通过传入的object获得Class,从METHOD_CACHE获取方法列表,这个列表记录了这个类里的方法名,线程模式,参数类型,是否粘性方法,和优化级,如果找到就返回列表,没有找到就通过反射,找到这个类里用@Subscribe修饰的方法,并存入列表中返回。
第二步是把列表中的方法登记到被监听类的列表中。获得被监听类的监听列表,并把该方法插入到监听列表中。
第三步,如果监听方法是粘性方法,就从缓存中查看是否有粘性数据可以处理,有则使用该方法处理数据。
post。先得到event的类,再从监听的MAP中获得处理event的列表。然后根据5种ThreadMode,选择不同的处理方式。POSTING就是直接执行,MAIN和MAIN_ORDERED都是在主线程执行,不同点在于如果调用者和执行者都在主线程,MAIN是直接执行,而MAIN_ORDERED是把处理方式和数据封装成MESSAGE,然后通过HANDLER发送到消息队列,等待执行,这样MAIN方式会阻塞event的调用者调用处理方法,MAIN_ORDERED是event调用者统一把处理方法延后处理,不会阻塞调用者。BACKGROUND和ASYNC的处理虽然都是使用了线程池,的不同在于,BACKGROUND是插入到执行的队列后会判断线程池是否在处理event,如果不是的话,执行线程池的excute方法。如果是的话,线程池会从队列中取出这个事件并处理,ASYNC会把每个event封装成runnable并通过线程池执行。
unregister。选择根据object找到event的类,再根据event的类找到处理方法,判断处理方法里的subcribe和object是否一致,是的话就删除。最后清除保存object,event的MAP。