学习树

一、已学习:
  1. HashMap:

  2. 多线程:

  3. Android 消息机制:
  4. final关键字,为什么匿名内部类使用局部引用要用final?

    现在我们知道了,是由于一个拷贝的动作,使得内外两个变量无法实时同步,其中一方修改,另外一方都无法同步修改,因此要加上final限制变量不能修改。

    那么为什么要拷贝呢,不拷贝不就没那么多事了吗?

    这时候就得考虑一下Java虚拟机的运行时数据区域了,dog变量是位于方法内部的,因此dog是在虚拟机栈上,也就意味着这个变量无法进行共享,匿名内部类 也就无法直接访问,因此只能通过值传递的方式,传递到匿名内部类中

    从JDK1.8开始,编译器不要求自由变量一定要声明为final,如果这个变量在后面的使用中没有发生变化,就可以通过编译,Java称这种情况为“effectively final”

  5. RxJava:

  6. JDK8新特性(在笔记文件夹里有文档):

    • Lambda 函数式接口:Function/Predicate/Consumer/Supplier
    • Stream :forEach/filter/map/count/limit/skip/concat/两种获取Stream流的方式
    • 方法引用 : 通过对象名引用成员变量/类名引用静态成员/super引用父类的成员/this引用本类的成员/类的 构造器引用/数组的构造器引用
  7. Android主流三方库源码分析

  8. 数据库框架设计(网易移动架构 -> 数据库架构设计):

    • 这个课程讲述的是通过反射的方式,但是反射耗费性能,GreeDao通过freeMarker使用模板的方式,通过使用自身的插件配套相应的freemarker模板生成所需的静态代码,避免了反射等消耗性能的操作。
二、待学习:
  1. 内存泄漏:

  2. Gradle:

  3. Github 有关学习:

  1. 剑指Offer(第二版)所有面试题总结

  2. JavaGuide

  3. 《Java面试进阶指南》

  4. RxJava:

  5. Android复习资料整理

  6. jsonchao :https://juejin.im/post/5caf0f89f265da038145c66e https://github.com/JsonChao/Awesome-Android-Interview

    • GC机制、基本的热修复原理、简单的算法题和智力题,比如冒泡、查找
    • JAVA并发、JVM等知识,还有数据库、SQL语句、性能优化、海量Top k
三、第三方库:
  1. 大图加载方案:
    • implementation ‘me.panpf:sketch:2.7.1-rc01’(已使用)
    • implementation ‘com.koushikdutta.ion:ion:2.+’
    • implementation ‘com.shizhefei:LargeImageView:1.1.0’
    • implementation ‘com.github.SherlockGougou:BigImageViewPager:v4_4.1.0’
    • implementation ‘com.davemorrissey.labs:subsampling-scale-image-view:3.10.0’
    • implementation ‘com.github.piasy:BigImageViewer:1.5.7’
四、Android开发艺术探索
第二章 IPC机制
  1. 序列化和反序列化

    Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象可以被写到数据库或文件中,也可用于网络传输,一般当我们使用缓存cache(内存空间不够有可能会本地存储到硬盘)或远程调用rpc(网络传输)的时候,经常需要让我们的实体类实现Serializable接口,目的就是为了让其可序列化。

    当然,序列化后的最终目的是为了反序列化,恢复成原先的Java对象,要不然序列化后干嘛呢,所以序列化后的字节序列都是可以恢复成Java对象的,这个过程就是反序列化。

  2. 静态成员变量属于类,不属于对象,所以不会参与序列化过程。对象的跨进程传输本质上都是反序列化的过程,这就是为什么AIDL中的自定义对象都要实现Parcelable的原因。

  3. Binder是Android中的一个类,它继承了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在Linux中没有;从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WorkManager等)和ManagerService的桥梁;从Android应用层来说,Binder是客服端和服务端通信的媒介,当bindService的时候,服务端会返回一个包含服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。

  4. Android 中的IPC方式

    • 使用Bundle:通过Intent中附加extras来传递信息
    • 使用文件共享:通过共享文件的方式来共享数据
    • 使用Messenger
    • 使用AIDL:通过Binder方式来跨进程通信
    • 使用ContentProvider:ContentProvider支持跨进程访问
    • 使用Socket:Socket通过网络通信实现数据传递
    • BroadcastReceiver:广播是一种被动跨进程通信方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。
第三章 View的事件体系
  1. 弹性滑动:
    • 使用Scroller
    • 通过动画
    • 使用延时策略
第四章 View的工作原理
  1. View的工作流程主要是指measure、layout、draw这三大流程,即测量、布局、绘制。其中measure确定View的测量宽/高,layout确定View的最终宽/高和四个顶点的位置,而draw则将View绘制到屏幕上。

  2. 直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content就相当于使用match_parent。

  3. 在某些极端情况下,系统可能需要多次measure才能确定最终的测量宽/高,在这种情形下,在onMeasure方法中拿到的测量宽/高可能是不准确的。一个比较好的习惯是在onLayout方法中去获取View的测量宽/高或者最终宽/高。

    在onCreate、onStart、onResume中均无法正确得到某个View的宽/高信息,这是因为View的measure过程和Activity的生命周期方法不是同步执行的,因此无法保证Activity执行了onCreate、onStart、onResume时某个View已经测量完毕。

    四种方法解决这个问题:

    • Activity/View#onWindowFocusChanged.

      1
      2
      3
      4
      5
      6
      7
      8
      @Override
      public void onWindowFocusChanged(boolean hasFocus) {
      super.onWindowFocusChanged(hasFocus);
      if (hasFocus) {
      int width = view.getMeasuredWidth();
      int height = view.getMeasuredHeight();
      }
      }
    • View.post(runnable)

      1
      2
      3
      4
      5
      6
      7
      view.post(new Runnable() {
      @Override
      public void run() {
      int width = view.getMeasuredWidth();
      int height = view.getMeasuredHeight();
      }
      });
    • ViewTreeObserver

      1
      2
      3
      4
      5
      6
      7
      8
      view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {
      view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
      int width = view.getMeasuredWidth();
      int height = view.getMeasuredHeight();
      }
      });
    • View.measure(int widthMeasureSpec, int heightMeasureSpec)

      通过手动对View进行measure来得到View的宽/高。要分情况处理,根据View的LayoutParams来分:

      1. matche_parent无法measure出具体的宽/高,直接放弃。

      2. 具体的数值(dp/px),比如宽高都是100px,如下measure:

        1
        2
        3
        4
        int wideMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
        int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
        view.measure(wideMeasureSpec,heightMeasureSpec);
        Log.d(TAG, "measureView, width= " + view.getMeasuredWidth() + " height= " + view.getMeasuredHeight());
      3. wrap_content

        1
        2
        3
        4
        int wideMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
        int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(1<<30)-1, View.MeasureSpec.AT_MOST);
        view.measure(wideMeasureSpec,heightMeasureSpec);
        Log.d(TAG, "measureView, width= " + view.getMeasuredWidth() + " height= " + view.getMeasuredHeight());
  4. 自定义View避免内存泄露:

    • 只要针对View中含有线程或者动画的情况:当View退出或者不可见时,记得及时停止该View包含的线程和动画,否则会造成内存泄露的问题。
    • 启动或者停止线程/动画的方式:
      • a.启动线程/动画:使用view.onAttachedToWindow(),因为该方法的调用时机是当包含View的Activity启动的时刻。
      • b.停止线程/动画:使用view.onDetachedFromWindow(),因为该方法调用的时机是当包含View的Activity退出或当前View被remove的时刻。
第五章 理解RemoteViews
  1. PendingIntent支持三种待定意图:
    • 启动Activity
    • 启动Service
    • 发送广播BroadcastReciever
五、算法与数据结构
一. 玩转数据结构
  1. 线性结构:
    • 数组
    • 队列
    • 链表
    • 哈希表
    • 等等
  2. 树结构:
    • 二叉树
    • 二分搜索树
    • AVL
    • 红黑树
    • Treap
    • Splay
    • Trie
    • 线段树(字典树、前缀树)
    • K-D树
    • 并查集
    • 哈夫曼树
    • 等等
  3. 图结构:
    • 邻接矩阵
    • 邻接表
    • 等等
  4. 大量的算法,以数据结构为基石
  5. 数组基础:
    • 数组最大的优点:快速查询。scores[2]。
    • 数组最好应用于“索引有语义”的情况 。
    • 但并非所有有语义的索引都适用于数组,比如:身份证号 110126198845345683,需要开辟很大的内存空间。
  6. 泛型:不可以使用基本数据类型,但是可以使用基本数据类型的包装类。自动拆箱,自动装箱。
  7. 动态数组、栈、队列:底层依托静态数组,靠resize解决容量问题。
  8. 链表:真正的动态数据结构。
    • 优点:真正的动态,不需要处理固定容量问题。
    • 缺点:丧失了随机访问的能力。
  9. 极客时间有关链表的题目推荐:

    • 单链表反转 (Leetcode-206)
    • 链表中环的检测(Leetcode-141)
    • 两个有序的链表合并 (Leetcode-21)
    • 删除链表倒数第 n 个结点(Leetcode-19)
    • 求链表的中间结点(Leetcode-876)
  10. 堆:

  • 将n个元素逐个插入到一个空堆中,时间复杂度是O(nlogn)。因为每次添加一个元素,往上堆化的过程是O(logn),n个元素就是O(nlogn)。

  • heapify的方式,时间复杂度是O(n)。

二. 玩转算法
  1. 二分查找法:

    • 对于有序序列,才能使用二分查找法
  2. 数组:

    • 双指针

    • 双索引技术(滑动窗口)

六、我对问题的理解
  1. GC机制:

    首先

七、深入理解Java虚拟机
第一章:走进Java
  1. 从Android5.0开始,Dalvik虚拟机全面替代ART虚拟机。
八、动脑:移动架构
  1. TreadLocal的工作原理:
    • 定义:ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。
九、资源索引:
  1. Android系统源码8.0:
  2. 优秀技术博客:

  3. Android 学习笔记:

  4. Git可视化练习工具:

  5. SQL:

十. 书籍
  1. Darren:
    • 《正念的奇迹》