进程间通信方式
线程间通信方式
我们知道线程是CPU调度的最小单位。在Android中主线程是不能够做耗时操作的,子线程是不能够更新UI的。而线程间通信的方式有很多,比如广播,Eventbus,接口回掉,在Android中主要是使用handler。handler通过调用sendmessage方法,将保存消息的Message发送到Messagequeue中,而looper对象不断的调用loop方法,从messageueue中取出message,交给handler处理,从而完成线程间通信。
如何保证数据的可见性,先学习下Java Memory Model内存模型:
JVM中存在主存区(Main Memory或Java Heap Memory),所有变量都是在主存中,对于所有线程进行共享.
每个线程又存在自己的工作内存(Working Memory),线程对所有变量的操作并非发生在主存区,而是发生在工作内存中.
所以我们会发现一个线程中修改了一些变量的值,但是在另外一个线程未必生效,在主内存中变量值也未必同步。
除了我们平常用synchronized锁定对象可保证对象值同步,我们使用volatile关键字可以标记变量,使用的是CPU级别的memory barrier的write,read指令保证数据同步,所以多个线程都能获取到一致的数据。
线程池
Android中常见的线程池有四种,FixedThreadPool、CachedThreadPool、ScheduledThreadPool、SingleThreadExecutor。
FixedThreadPool线程池是通过Executors的new FixedThreadPool方法来创建。它的特点是该线程池中的线程数量是固定的。即使线程处于闲置的状态,它们也不会被回收,除非线程池被关闭。当所有的线程都处于活跃状态的时候,新任务就处于队列中等待线程来处理。注意,FixedThreadPool只有核心线程,没有非核心线程。
CachedThreadPool线程池是通过Executors的newCachedThreadPool进行创建的。它是一种线程数目不固定的线程池,它没有核心线程,只有非核心线程,当线程池中的线程都处于活跃状态,就会创建新的线程来处理新的任务。否则就会利用闲置的线程来处理新的任务。线程池中的线程都有超时机制,这个超时机制时长是60s,超过这个时间,闲置的线程就会被回收。这种线程池适合处理大量并且耗时较少的任务。这里得说一下,CachedThreadPool的任务队列,基本都是空的。
ScheduledThreadPool线程池是通过Executors的newScheduledThreadPool进行创建的,它的核心线程是固定的,但是非核心线程数是不固定的,并且当非核心线程一处于空闲状态,就立即被回收。这种线程适合执行定时任务和具有固定周期的重复任务。
SingleThreadExecutor线程池是通过Executors的newSingleThreadExecutor方法来创建的,这类线程池中只有一个核心线程,也没有非核心线程,这就确保了所有任务能够在同一个线程并且按照顺序来执行,这样就不需要考虑线程同步的问题。
Activity的生命周期以及启动模式
生命周期如下图
启动模式分为四种
Service的两种启动方式及其区别
startService 启动后的方法是onStartCommand和bindService 启动后的方法是onBind
service的两种启动方式:Service的两种启动方式_service启动方式-CSDN博客
IntentService和Service的区别:IntentService和Service区别 - 简书
关于==和equals的区别和联系
1)对于==,比较的是值是否相等
(1)如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;
(2)如果作用于引用类型的变量,则比较的是所指向的对象的地址
2)对于equals方法,注意:equals方法不能作用于基本数据类型的变量,equals继承Object类,比较的是是否是 同一个对象
(1)如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
(2)诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
Thread和Runnable的区别以及Thread的start和run的区别
thread的几种状态:https://www.cnblogs.com/jhxxb/p/10821483.html
MVC MVP MVVM 的区别及特点
关于View的事件传递
可以参考这篇文章View事件传递机制 - 简书;
具体代码可看迅速理解Android事件传递 - 星际怪物 - ITeye博客
view的事件传递:图解 Android 事件分发机制 - 简书
String,StringBuffer,StringBuilder的区别
简述JNI
是java和c语言之间的桥梁,由于java是一种半解释语言,可以被反编译出来,一种重要涉及安全的代码就使用了C编程,再者很多底层功能调用C语言都实现了Java没必要重复造轮子,所以定义了JNI接口的实现
Android性能优化
详细可看:Android浅析之性能优化 - 知乎
Android 常见性能优化总结 - 简书
Java常用数据结构
HashMap HashTable区别
HashSet和HashMap的区别
*HashMap* *HashSet*
HashMap实现了Map接口 HashSet实现了Set接口
HashMap储存键值对 HashSet仅仅存储对象
使用put()方法将元素放入map中 使用add()方法将元素放入set中
HashMap中使用键对象来计算hashcode值 HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false
HashMap比较快,因为是使用唯一的键来获取对象 HashSet较HashMap来说比较慢
LinkedList ArrayList区别
Android的动画类型
主要分为帧动画,补间动画和属性动画;
属性动画和补间动画区别
Post和get的区别
内存泄露如何查看和解决
内存泄漏的例子:https://www.cnblogs.com/andashu/p/6440944.html
关于非静态内部类的持有 深入理解Java中为什么内部类可以访问外部类的成员_java中局部内部类为什么能获取父类的成员变量-CSDN博客
概念:有些对象只有有限的生命周期,当他们的任务完成之后,它们将被垃圾回收,如果在对象的生命周期本该结束的时候,这个对象还被一系列的引用,着就会导致内存泄露。
解决方法:使用开源框架LeakCanary检测针对性解决
常见的内存泄露有:
1)单例造成的内存泄露,例如单例中的Context生命周期大于本身Context生命周期
线程使用Hander造成的内存卸扣,当activity已经结束,线程依然在运行更新UI非静态类2)使用静态变量导致无法回收释放造成泄露
3)WebView网页过多造成内存泄露
4)资源未关闭造成泄露,例如数据库使用完之后关闭连接
深层拷贝和浅层拷贝的区别
浅层拷贝指的是对引用的复制例如list = originList,要想让原数据即originList在复制到list集合中并且改变其中的值后,原数据不受影响,这时就需要用到深层拷贝,即将原数据中的对象集合进行clone,这样即使对list中的数据进行改变的操作,也不会影响到originList的数据
Okhttp面试相关
如何使用okhttp进行网络异步请求,并根据请求结果刷新UI
第一步,创建一个OkHttpClient对象 OkHttpClient mClient = new OkHttpClient.Builder().build();
第二步,创建携带请求信息的Request对象 Request request = new Request.Builder().url("http://www.baidu.com").get().build();
第三步,创建Call对象 Call call = mClient.newCall(request);
第四步,call.enqueue()
需要注意的是,不能直接在Callback中更新UI,否则会报出异常
Okhttp面试简答-CSDN博客
Glide面试相关
读取的顺序是:Lru算法缓存、弱引用缓存、磁盘缓存
Android面试题:Glide_android glide原理面试-CSDN博客
类加载机制及原理
类加载机制:https://www.cnblogs.com/yixianyixian/p/8145506.html
JAVA虚拟机、Dalvik虚拟机和ART虚拟机简要对比
虚拟机简要对比 :JAVA虚拟机、Dalvik虚拟机和ART虚拟机简要对比_java虚拟机 dalvik虚拟机 art虚拟机-CSDN博客
常用的设计模式
1.单例模式:双重验证锁
public class SingleInstance {
private static volatile SingleInstance instance;
public static SingleInstance getInstance() {
//第一次校验
if (instance == null) {
synchronized (SingleInstance.class) {
//第二次校验
if (instance == null) {
instance = new SingleInstance();
}
}
}
return instance;
}
}
为什么需要两次判空
第一次校验:由于单例模式只需要创建一次实例,如果后面再次调用getInstance()方法时,则直接返回之前创建的实例,因此大部分时间不需要执行同步方法里面的代码,大大提高了性能。如果不加第一次校验的话,那跟上面的普通懒汉模式没什么区别,每次都要去竞争锁。
第二次校验:如果没有第二次校验,假设线程A执行了第一次校验后,判断为null,这时线程B也获取了CPU执行权,也执行了第一次校验,判断也为null。接下来线程B获得锁,创建实例。这时线程A又获得CPU执行权,由于之前已经进行了第一次校验,结果为null(不会再次判断),获得锁后,直接创建实例。结果就会导致创建多个实例。所以需要在同步代码里面进行第二次校验,如果实例为空,则进行创建。
2.适配器模式,如GridView,ListView,Recycleview对应的列表加载的适配器刷新模式
3.建造者模式,如AlertDialog的创建方法
4.观察者模式,如EventBus使用到的观察者模式以及BroadCastReceiver的观察者模式