0
点赞
收藏
分享

微信扫一扫

android高级开发进阶之路

天行五煞 2022-04-08 阅读 110

首先来讲一下思路,设计的领域要广,然后求精(深入这个东西学习的是思路,要恰到好处的学到精髓);下面来陈列一下大纲:

  • 引用模式
  • 数据结构
  • 数学之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

举报

相关推荐

0 条评论