Java
Java虚拟机内存分布:
Java虚拟机开启每个进程的时候都会为当前的进程分配对应的内存空间,这些内存空间中包含一下五个方面:
堆内存 存放对象的实例
方法区 类信息,常量,静态变量,及时编译器编译后的代码
程序计数器 存放着下一条执行指令 ,虚拟机通过修改这个数据来选取下一条要执行的命令是分支 跳转 循环 异常处理 线程恢复等
虚拟机栈 方法的栈帧(局部变量表,操作数栈,动态链接,方法出口等)
本地方法栈 属于native层的虚拟机栈
前两个属于线程共享的,后面三个是属于线程私有的.
Java垃圾回收算法
标记--清理 先对内存标记一遍标明那些对象是可用的那些对象是无用的,之后对无用的对象进行清理.
标记--整理 跟标记清理的第一步一样先进行标记,之后将可用对象整理到内存的一端,之后对这个边界之外的内存进行回收.
复制--清理 将可用对象复制到另一块内存区域中,将之前的一块内存区域进行回收.
分代收集 新生代用的复制清理(Eden 两个Survivor) 老年代标记整理
Java 中怎么判断对象是有效的
引用计数法
可达性分析法
GCRoots一般有哪些:虚拟机栈中栈帧里面局部变量表里面对象的引用,方法区静态属性引用的对象,方法区中常量引用的对象,native方法引用的对象.
Java类加载机制
双亲委培机制 当一个类加载器收到加载一个类的请求的时候,首先不会自己去加载这个类而是将交由父类加载器去加载这个类 ,如果父类加载器没有完成这个类加载的时候,当前的类加载器才会加载这个类. 优点是避免重复加载+避免核心类被破坏.
类加载器的组成:
启动类加载器(Bootstrap) 加载的是 lib下面的类库
标准扩展类加载器(Extension) 加载的是lib/ext 下面的类库
系统类加载器(System) 加载的是系统类路径中指定的类
Android中使用的类加载器

类加载机制在Android中主要的使用方面就是对类的热修复,后台给Android一个已经修复好的class文件,在Android中通过类加载机制实现对本地有缺陷的类的替换,具体实现是通过反射将我们要修复的类插入到加载列表的首部,这样加载类的时候会优先加载我们想要修复的类.
Android 屏幕适配
今日头条适配方案通过修改系统暴露出来的DisplayMetrics中的density属性,density属性将会影响dp与px之间的转换,修改了density就会修改dp转px之后的值.原理就是将当前的屏幕宽或者高的像素等分对应的分数列如360或420 1dp占有多少px就固定了.
LinearLayout 的权重适配 ConstraintLayout 比例适配 RelativeLayout 位置适配
dimens文件适配 在不同的values文件下面生成不同的dimens文件是同一dp对应不同的px
.9图
RecylerView的加载优化
如果RecyclerView当前的Item是大小是固定不变的,可以通过setHasFixedSize(boolean b)设置是不是需要重新测量和排版item. 如果用户操作只影响到当前的条目,那么可以使用RecyclerView的Adapter中的单条刷新进行条目更新. 方法是notifyItemChanged() 设置条目点击事件的时候实现OnItemTouchListener这个接口,同时使用手势检测器GestureDetectorCompat用于检测当前手势是长按或单击等操作,并对外抛出方法实现点击事件回调.最后使用RecyclerView的addOnItemTouchListener注册监听. RecyclerView 与 ListView之间的区别 RecyclerView 四级缓存 ListView 二级缓存 mAttachedScrap mChangedScrap mAttachedScrap中存放的是没有从RecyclerView中分离的Holder mChangedScrap是存放数据改变的Holder mCachedViews 已经从RecyclerView中解绑的ViewHolder mViewCacheExtension 用户自定义的默认为空 mRecyclerPool mAttachedScrap mChangedScrap以及mCachedViews中的Holder 不需要重新进行 RecyclerView 缓存的是Holder ListView缓存的是View RecyclerView自定义实现上拉加载下拉刷新
Android View绘制原理
Activity中的attach方法使Activity 与当前的Window进行绑定
setContentView 使Window与DecorView进行绑定,同时将代码中定义好的布局加载进DecorView中id为content的ViewGroup中
当执行到handleResumeActivity方法中的时候通过WindowManger将DecorView与ViewRootImpl关联到一起.
当ViewRootImpl与DecorView通过setView进行关联的时候,在ViewRootImpl类的setView方法中会去请求requestLayout
requestLayout方法会scheduleTraversals方法,这个方法会像Choreographer中注册一个Runnable这个当底层消息帧传递过来之后Choreographer会执行这个Runnable中的run方法,这个方法调用doTraversals方法,doTraversals调用的performTraversals方法进行视图树的解析.
Android 中getDimension、getDimensionPixelOffset、getDimensionPixelSize之间的区别
三者都是用来获取尺寸大小的获取的尺寸大小的单位是px,不同的是getDimension获取的是具体的px值返回值类型是float的其他两个返回值是int类型,剩下的两个的区别是getDimensionPixelOffset获取的是向下取整之后的数据,getDimensionPixelSize是获取四舍五入之后的数据。
Android View绘制流程
先绘制背景 绘制内容 绘制子View 绘制装饰 (例如滚动条)
Android 事件分发机制
涉及三个主要方法 dispatchTouchEvent onInterceptTouchEvent onTouchEvent 原理 屏幕事件通过Activity传递给Window再由window传递给DecorView这个时候整个事件的分发流程就已经开始执行了,当事件传递到我们编写的xml布局时候首先是最外层的ViewGroup收到事件消息,当ViewGroup收到事件消息的时候首先会交由dispatchTouchEvent进行判断是不是需要进行下发,dispatchTouchEvent判断事件是否下发根据就是onInterceptTouchEvent中对这个事件是否进行拦截处理,如果在onInterceptTouchEvent中对这个事件进行拦截那么这个事件就会交由本ViewGroup的onTouchEvent进行事件的处理;如果不进行拦截那么这个事件就会交由下层的View或者ViewGroup进行处理.但是这个拦截是根据情况而定的如果当前事件不是down事件的话,子View可以调用当前ViewGroup的requestDisallowInterceptTouchEvent方法请求父View不对当前的事件进行拦截处理.这是屏幕事件被处理的流程;如果当前的事件下发到了最底层的View,并且最底层的View没有对当前事件进行处理那么这事件就会一层一层向上传递如果传递到最上面的View依然没有处理这个事件那么这个事件就属于垃圾事件(自己命名的)那么这一层一层的向上传递的时候是不是什么都不做了呢不是,如果向上传递的过程中会调用onTouchEvent方法判断这个事件是不是进行处理如果处理的话那么这个事件还是有控件消费的不能称为垃圾事件. Android 事件分发 cancel事件是怎么产生的 什么时候触发 cancel事件的产生前提是当前的View之前有处理触摸事件,如果当前的View之前没有处理过触摸事件的话那么不会产生cancel事件,触发的时间测试:手指刚滑动出当前View的触摸范围时才会产生这个事件.
Android Handler 机制
Handler机制中的四个重要组成部分 Handler 消息的发起和接收方 Looper 消息的获取 MessageQueue 消息的存储 Message 实际的消息
Android 中进程保活
一个像素的Activity 当手机锁屏时开启一个像素的Activity,当手机锁屏开启之后将一个像素的Activity finish掉 一像素的Activity设置主要是设置当前Activity的窗体的大小通过设置Window中的attributes属性
提升当前进程的等级,将其提升为前台进程,使用的方法就是开启一个通知.
JobService和JobScheduler 利用JobScheduler 的定时功能执行双服务守护功能,同时使用两个服务进行相互调用并且发送通知提升当前进程的优先级.
OKHttp设计原理
OKHttp设计是分层处理不同的业务的逻辑,主要利用的是责任链模式来实现核心设计,这个责任链模式主要是用于数据的请求工作,在OkHttp中拦截器链包含一些基本的核心拦截器同时用户也可以自定义拦截器完成自己的相关处理,基本的拦截器有重试拦截器、BridgeIntercept (转换拦截器)、缓存拦截器、链接拦截器、呼叫与接收拦截器
OKHttp中的包含的设计模式
责任链模式
建造者模式
单列模式 创建SocketFactory的时候SocketFactory使用的是单例模式创建SocketFactory
OkHttp中的数据缓存
是根据请求URL在本地中找是不是有缓存找到缓存后判断缓存是不是有效可用缓存,如果是返回响应数据,如果不是请求服务器交给服务器判断当前请求的数据是不是没有改变如果返回的是304状态码表明本地的数据与服务器的数据一致那么本地的数据还是可用,如果返回200表明需要将服务器返回的数据解析返回给客户端。
Glide设计原理
Glide中使用到的设计模式
单例 Glide对象创建的时候使用的是单例模式
建造者模式 Glide 对象创建的时候通过builder建造出来的
简单工厂模式 创建ViewTarget的时候根据传递进来的参数判断创建什么类型的Target
适配器模式 ViewTarget的各种子类.
工厂方法模式 创建LoadData时候.
Glide 的缓存原理
内存缓存
内存缓存中的LruCache
内存缓存中的弱引用 使用弱引用缓存主要是防止当前使用的图片过多,而Lru中放不下这么多的图片导致图片被回收掉.
磁盘缓存
Glide 怎么能监听到生命周期
Glide 创建的时候会创建一个Fragment通过Fragment与Activity之间的绑定实现了对生命周期的监听.
Android 中的动画
属性动画
通过修改View中指定的属性值,调用invalidate等刷新方法通知View根据修改的属性进行重绘.
view动画(补间动画)
通过View中的draw方法取出对当前View的相关修改进行绘制,修改的数据存放在Transformation中.
帧动画
属性动画与View动画的原理
View动画和属性动画都是由Choreographer对底层绘制帧进行 监听,不同的是View动画是跟View树的测量排版绘制等一同进行的,实际上是当View进行draw(绘制)的时候通过获取Transformation中的相关属性在draw的时候进行相关的操作;属性动画不是跟View树的 测量等操作绑定在一起的,而是单独的一个回调监听着底层绘制帧,并且在这个监听中对View的相关属性进行动态设置,等到下一次绘制帧来了之后会将修改的属性进行绘制展示.(记住是当前帧绘制上一帧修改的属性)
Android ViewPager+Fragment Fragment重叠的问题
这种情况导致Fragment 出现重叠一般是由于布局重复加载导致的,这个时候可以通过对当前Fragment中要添加的布局进行判断来处理,处理方法便是使用成员变量记录当前要加载的布局,通过获取当前布局的parent,判断parent 是不是为空如果不为空的情况下需要让parent移除当前的布局对象.之后重新加载.
public class TestFragment extend Fragment {
protected View onCreateView(LayoutInflater inflater,ViewGroup container,
Bundle savedInstanceState)
......
ViewParent parent = rootView.getParent();
if(parent != null) {
parent.removeView(rootView);
}
......
return rootView;
}
Android 进程之间通信 Binder 机制 Messenger AIDL ContentProvider
ButterKnife 原理
ARouter 原理
EventsBus 原理
WebSocket
WebSocket是一个双通道的应用层协议,用于实现服务器与客户端进行长连接.
WebSocket最开始建立连接的时候需要通过Http协议需要使用到Tcp的三次握手建立连接,建立成功之后就会使用WebSocket自己的协议.
WebSocket可以通过心跳帧保持长连接,通过监听WebSocket的连接状态进行断线重连.
Android打包流程
将AndroidManifest.xml和布局文件编译生成对应的R.java文件
处理aidl文件,通过aidl工具解析接口定义文件生成对应的java文件供接口程序调用
编译项目源码生成.class文件源码中包含第一步和第二步生成的java文件
转换.class文件生成.dex文件通过dx工具将.class 文件转换成.dex文件
打包生成APK文件,通过apkbuilder将.dex文件和资源文件(编译过的和没有编译过的(图片))打包生成Apk文件
对Apk文件进行签名
对签名后的APK文件进行对齐处理,对齐的主要过程是将APK包中所有的资源文件距离文件起始偏移为4字节整数倍,这样通过内存映射访问apk文件时的速度会更快。
对齐的作用就是减少运行时内存的使用。
synchronized对象锁与类锁之间的区别:
对象锁标明的是持有对象实现的锁状态,类锁标明的是持有类实现的锁状态,持有对象实现的锁有可能因为对象创建多个而导致不能实现完全锁定的状态,而使用类锁的话则能避免这个问题,因为类在加载的时候只会加载一次标明这个类在整个应用中只会有一个.
public class SynchronizeClazz {
private final ReentrantLock
reentrantLock;
private ArrayList<Thread>
threadList = new ArrayList<>();
static int num = 0;
public SynchronizeClazz() {
reentrantLock = new ReentrantLock();
}
public void printf() {
//
if (reentrantLock.isLocked())
{
threadList.add(Thread.currentThread());
} else {
reentrantLock.lock();
for (int i = 0;i<100;i++){
String name = Thread.currentThread().getName();
long id =
Thread.currentThread().getId();
System.err.println(i+"\tthreadName="+name+"\tthreadId"+id);
}
reentrantLock.unlock();
//通知执行
if (threadList.size()
> 0) {
Thread thread = threadList.get(0);
thread.run();
threadList.remove(0);
}
}
}
public synchronized void testObjectSync() {
//对象锁
for (;num <= 10; num++) {
String name = Thread.currentThread().getName();
long id = Thread.currentThread().getId();
System.err.println(num+"threadName="+name+"\tthreadId"+id);
}
}
public void testClassSync() {
//类锁
synchronized (SynchronizeClazz.class) {
for (;num <= 10; num++) {
String name = Thread.currentThread().getName();
long id = Thread.currentThread().getId();
System.err.println(num+"threadName="+name+"\tthreadId"+id);
}
}
}
}
SynchronizeClazz synchronizeClazz2 = new SynchronizeClazz();
Thread thread4 = new Thread(new Runnable() {
@Override
public void run() {
synchronizeClazz2.testClassSync();
}
}, "线程4");
SynchronizeClazz synchronizeClazz3 = new SynchronizeClazz();
Thread thread5 = new Thread(new Runnable() {
@Override
public void run() {
synchronizeClazz3.testClassSync();
}
},"线程5");
// Thread thread3 = new Thread(runnable);
System.err.println("调用的线程4"+thread4.getName());
thread4.start();
System.err.println("调用的线程5"+thread5.getName());
thread5.start();
调用的线程4线程4
调用的线程5线程5
0threadName=线程4 threadId11
1threadName=线程4 threadId11
2threadName=线程4 threadId11
3threadName=线程4 threadId11
4threadName=线程4 threadId11
5threadName=线程4 threadId11
6threadName=线程4 threadId11
7threadName=线程4 threadId11
8threadName=线程4 threadId11
9threadName=线程4 threadId11
10threadName=线程4 threadId11
0threadName=线程5 threadId12
1threadName=线程5 threadId12
2threadName=线程5 threadId12
3threadName=线程5 threadId12
4threadName=线程5 threadId12
5threadName=线程5 threadId12
6threadName=线程5 threadId12
7threadName=线程5 threadId12
8threadName=线程5 threadId12
9threadName=线程5 threadId12
10threadName=线程5 threadId12
通过上面的类锁测试可以看出使用类锁,即使是不同的对象使用同一个方法的话也会根据持有锁的顺序执行.
synchronizeClazz = new SynchronizeClazz();
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronizeClazz.testObjectSync();
}
};
Thread thread1 = new Thread(runnable,synchronizeClazz.toString());
SynchronizeClazz synchronizeClazz1 = new SynchronizeClazz();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronizeClazz1.testObjectSync();
}
}, synchronizeClazz1.toString());
// Thread thread3`® = new Thread(runnable);
System.err.println("调用的线程1"+thread1.getName());
thread1.start();
System.err.println("调用的线程2"+thread2.getName());
thread2.start();
调用的线程1com.practice.zfr.face.SynchronizeClazz@511d50c0
调用的线程2com.practice.zfr.face.SynchronizeClazz@60e53b93
0threadName=com.practice.zfr.face.SynchronizeClazz@511d50c0 threadId11
1threadName=com.practice.zfr.face.SynchronizeClazz@511d50c0 threadId11
0threadName=com.practice.zfr.face.SynchronizeClazz@60e53b93 threadId12
2threadName=com.practice.zfr.face.SynchronizeClazz@511d50c0 threadId11
1threadName=com.practice.zfr.face.SynchronizeClazz@60e53b93 threadId12
3threadName=com.practice.zfr.face.SynchronizeClazz@511d50c0 threadId11
2threadName=com.practice.zfr.face.SynchronizeClazz@60e53b93 threadId12
4threadName=com.practice.zfr.face.SynchronizeClazz@511d50c0 threadId11
3threadName=com.practice.zfr.face.SynchronizeClazz@60e53b93 threadId12
5threadName=com.practice.zfr.face.SynchronizeClazz@511d50c0 threadId11
4threadName=com.practice.zfr.face.SynchronizeClazz@60e53b93 threadId12
6threadName=com.practice.zfr.face.SynchronizeClazz@511d50c0 threadId11
5threadName=com.practice.zfr.face.SynchronizeClazz@60e53b93 threadId12
7threadName=com.practice.zfr.face.SynchronizeClazz@511d50c0 threadId11
6threadName=com.practice.zfr.face.SynchronizeClazz@60e53b93 threadId12
8threadName=com.practice.zfr.face.SynchronizeClazz@511d50c0 threadId11
7threadName=com.practice.zfr.face.SynchronizeClazz@60e53b93 threadId12
9threadName=com.practice.zfr.face.SynchronizeClazz@511d50c0 threadId11
8threadName=com.practice.zfr.face.SynchronizeClazz@60e53b93 threadId12
10threadName=com.practice.zfr.face.SynchronizeClazz@511d50c0 threadId11
9threadName=com.practice.zfr.face.SynchronizeClazz@60e53b93 threadId12
10threadName=com.practice.zfr.face.SynchronizeClazz@60e53b93 threadId12
通过上面的对对象锁的测试发现,如果是使用对象锁的话,不同的对象调用同种方法会出现锁不住的情况.