首先来讲一下思路,设计的领域要广,然后求精(深入这个东西学习的是思路,要恰到好处的学到精髓);下面来陈列一下大纲:
- 引用模式
- 数据结构
- 数学之Math
- 进程
- 线程
- 高并发
- 冷启动流程
- activity视图
- 自定义view
- kotlin协程
- 图片内存管理
- 注解
- oom
- anr
1. 引用模式
引导:在很早之前引用模式只有引用和未引用两个,jvm会帮助我们回收垃圾,后面为了更好的垃圾回收确认对象,衍生了以下四个等级。
Object obj = new Object();
强引用:情愿抛终止程序异常也不会回收此对象;会把强引用放到堆内存;
new Handler(new WeakReference<>(activity));
弱引用:jvm会不定时gc,当gc则会回收;
new Handler(new SoftReference<>(activity));
软引用:当内存不足时才会回收;
new Handler(new PhantomReference<>(activity));
虚引用:不确定回收时间,不常用;
当activity被销毁时隔一段时间会触发gc,如果是强引用的话activity和handler相互引用则会导致jvm回收垃圾认为他还有用,则不会回收;以下是解决方法:
private MyHandler handler = new MyHandler(new WeakReference<>(this));
private static class MyHandler extends Handler {
private final WeakReference<Test> activity;
private MyHandler(WeakReference<Test> activity) {
this.activity = activity;
}
@Override
public void handleMessage(@NonNull Message msg) {
Test page = activity.get();
if (page != null) {
switch (msg.what) {
case 1:
page.refresh();
break;
default:
break;
}
}
}
}
这时候你就觉得对引用模式的认识就结束了吗?不,并没有!
下面是解绑view层的mvp代码:
override fun onDestroy() {
super.onDestroy()
mPresenter?.let {
lifecycle.removeObserver(it)
it.detachView()
}
}
override fun detachView() {
mView = null
}
当activity走onDestory时候添加到一个虚引用activity队列中,过一段时间触发gc,如果虚引用不为空,则这个activity未销毁,则判定为内存泄露,这时候新开一个进程对泄露的activity进行判空,即可定位对应位置;
2.数据结构
2.1 数组
- size固定
- 只能使用基本类型
- 速度快,占用内存小
2.2 集合
-
ArrayList 底层由数组构成,当需要扩容的时候,动态设置size,并使用Arrays.copy(list,size);同时ArrayList不是线程安全的;
-
SparseArray是android特有的一个集合类,由数组+数组构成,采用二分查找法查询,当数据量小的使用建议使用SparseArray,数据量大的时候建议ArrayList;
-
HashMap 由数组+链条组成;参数有长度16和扩容临界率0.75f,首先长度只能是2的n次方,如果参数传进去的是5那么会变成8;下面两种遍历方法最快也是常用的。
Map<Integer,String> map = new HashMap<Integer,String>(16,0.75f);
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.print("key: " + entry.getKey() + " value: " + entry.getValue() + " \n");
}
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> item = iterator.next();
System.out.print("key: " + item.getKey() + " value: " + item.getValue() + " \n");
}
- LinkedHashMap 存的时候慢,读取的时候快;
Map<Integer,String> map= new LinkedHashMap<>(16,0.75f,true);
2.3 二叉树 :抽象类思想,当层级关系达到8就会变成红黑树;可理解为json对象关系;
2.4 链条:抽象思想,数组+Object
2.5 队列:后进后出
Stack<Integer> sList = new Stack<>();
2.6 栈:先进先出,类似于activity栈原理;
Queue<Integer> list = new LinkedList<>()
3. 数学之Math
- 绝对值 abs
- 随机数 random [0,1)
(int) (Math.random() * 3);//从0到3的整数
Math.random()+3;//从3到4的double值
- 最大/小值 max/min
- 圆周率 PI
- sin/cos 直角三角sin a=a/c cos a=b/c
sin(30)=sin(30*PI/180)
- n次方 pow(x,n次方)
- 取整
- 平方/立方根 sqrt/cbrt
4. 进程
进程的优缺点
优点:
- 进程间是相互隔离的,相当于沙盒,故闪退不会影响彼此;可以给自己app添加一个子进程守护容易闪退的页面,例如融云在app中就是一个单独的进程;
缺点:
- 进程间通信困难,需要ibinder+aidl导致代码复杂度;两个进程要有交互方法;
- application多次创建
5. 线程
首先先来看下面一张图:
-
handler: android ui主线程和子线程信使跑腿
-
looper :
线程中间者,loop开始循环,会循环取messageQueue中的消息体给handler使用;不使用需要用quit退出循环;
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
- messageQueue:是一个容器队列;
- handlerthread:把looper和Thread进行封装,则减少代码不规范和线程导致的阻塞;
public class HandlerThread extends Thread {
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
}
- handler延时
- android线程为什么是单线程?这个问题也留给第6节
- 线程池:ThreadPoolExecutors(核心线程数,最大线程数,临界处理) 这里临界处理默认是报异常的
- 线程的正确使用
1.引用模式避免泄露
2.合理分配使用线程池充分利用cpu
3.切换线程是非常浪费性能的
6. 高并发
下面是一个单例模式,涉及到高并发,先来分析一下为什么这么写?
public class Singgle {
private static volatile Singgle singgle; //volatile 可见性和有序性 可以防止指令重排序 同时有相互联系也不会重排序
private Singgle() {
}
/**
* synchronized 原子性
* @return
*/
public static Singgle getInstance() {
if (singgle == null) {
synchronized (Singgle.class) {
if (singgle == null) singgle = new Singgle();
}
}
return singgle;
}
public String getName() {
return "flower";
}
}
这里是cpu双核+内存条,单核由高速缓存+处理器组成,可以来思考一下为什么和高并发有关?
- 原子性:只有一个操作
- 有序性:按照顺序执行
- 可见性:两个人在改一个东西 另外一个立马可见
- 指令重排序:代码经过编译器和处理器分析后会对代码执行顺序进行排序
for (int i = 0; i < 10000; i++)
System.out.print(num + "\n");
问题2:如何拓展解决并发?现在看以下示例
private Lock lock = new ReentrantLock();
private int num = 0;
for (int i = 0; i < 10000; i++) {
lock.lock();
try {
num++;
System.out.print(num + "\n");
} finally {
lock.unlock();
}
}
newSingleThreadContext是作用域,即告诉处理器我这个方法体是不能线程并发的,repeat里也是一个作用域,repeat内部实现了有序性,从而保证了原子性和有序性;有序性则说明变量num是在修改完毕后才执行下一次循环,避免了可见性;
val countContext = newSingleThreadContext("countContext")
GlobalScope.launch(countContext) {
withTimeoutOrNull(100) {
repeat(1000) {
num++
Log.e(TAG, "onCreate: $num")
handler.sendMessageDelayed(Message.obtain().apply {
data.putString("num", num.toString())
}, 100)
}
countContext.close()
}
}
7. 冷启动流程
8. activity视图
9. 自定义view
10. kotlin协程
11. 图片内存管理
12. 注解
13. oom
14. anr