类加载过程javascript
Java 中类加载分为 3 个步骤:加载、连接、初始化。前端
类加载器大体分为3类:启动类加载器、扩展类加载器、应用程序类加载器。java
所谓的双亲委派模型就是当加载一个类时,会优先使用父类加载器加载,当父类加载器没法加载时才会使用子类加载器去加载。这么作的目的是为了不类的重复加载。面试
HashMap 的内部能够看作数组+链表的复合结构。数组被分为一个个的桶(bucket)。哈希值决定了键值对在数组中的寻址。具备相同哈希值的键值对会组成链表。须要注意的是当链表长度超过阈值(默认是8)的时候会触发树化,链表会变成树形结构。算法
Java中引用类型分为四类:强引用、软引用、弱引用、虚引用。数组
http 是超文本传输协议,而 https 能够简单理解为安全的 http 协议。https 经过在 http 协议下添加了一层 ssl 协议对数据进行加密从而保证了安全。https 的做用主要有两点:创建安全的信息传输通道,保证数据传输安全;确认网站的真实性。浏览器
提到 https 的话首先要说到加密算法,加密算法分为两类:对称加密和非对称加密。缓存
1.客户端(一般是浏览器)先向服务器发出加密通讯的请求安全
AIDL 、广播、文件、socket、管道性能优化
PathClassLoader,只能加载系统中已经安装过的 apk
DexClassLoader,能够加载 jar/apk/dex,能够从 SD卡中加载未安装的 apk
Android中动画大体分为3类:帧动画、补间动画(View Animation)、属性动画(Object Animation)。
补间动画与属性动画的区别:
说到 Handler,就不得不提与之密切相关的这几个类:Message、MessageQueue,Looper。
Android 中的性能优化在我看来分为如下几个方面:内存优化、布局优化、网络优化、安装包优化。
Android的内存优化在我看来分为两点:避免内存泄漏、扩大内存,其实就是开源节流。
其实内存泄漏的本质就是较长生命周期的对象引用了较短生命周期的对象。
为何要扩大咱们的内存呢?有时候咱们实际开发中不可避免的要使用不少第三方商业的 SDK,这些 SDK 其实有好有坏,大厂的 SDK 可能内存泄漏会少一些,但一些小厂的 SDK 质量也就不太靠谱一些。那应对这种咱们没法改变的状况,最好的办法就是扩大内存。
扩大内存一般有两种方法:一个是在清单文件中的 Application 下添加largeHeap="true"这个属性,另外一个就是同一个应用开启多个进程来扩大一个应用的总内存空间。第二种方法其实就很常见了,比方说我使用过个推的 S DK,个推的 Service 其实就是处在另一个单独的进程中。
Android 中的内存优化总的来讲就是开源和节流,开源就是扩大内存,节流就是避免内存泄漏。
在Linux中,为了不一个进程对其余进程的干扰,进程之间是相互独立的。在一个进程中其实还分为用户空间和内核空间。这里的隔离分为两个部分,进程间的隔离和进程内的隔离。
既然进程间存在隔离,那其实也是存在着交互。进程间通讯就是 IPC,用户空间和内核空间的通讯就是系统调用。
Linux 为了保证独立性和安全性,进程之间不能直接相互访问,Android 是基于 Linux 的,因此也是须要解决进程间通讯的问题。
其实 Linux 进程间通讯有不少方式,好比管道、socket 等等。为何 Android 进程间通讯采用了Binder而不是 Linux
已有的方式,主要是有这么两点考虑:性能和安全
Binder 是基于 CS 架构的,有四个主要组成部分。
Binder 机制主要的流程是这样的:
LruCache 的核心原理就是对 LinkedHashMap 的有效利用,它的内部存在一个 LinkedHashMap 成员变量。值得咱们关注的有四个方法:构造方法、get、put、trimToSize。
其实到这里咱们能够总结一下,为何这个算法叫 最近最少使用 算法呢?原理很简单,咱们的每次 put 或者get均可以看作一次访问,因为 LinkedHashMap 的特性,会将每次访问到的元素放置到尾部。当咱们的内存达到阈值后,会触发 trimToSize 方法来删除 LinkedHashMap 首部的元素,直到当前内存小于 maxSize。为何删除首部的元素,缘由很明显:咱们最近常常访问的元素都会放置到尾部,那首部的元素确定就是 最近最少使用 的元素了,所以当内存不足时应当优先删除这些元素。
设计一个图片加载框架,确定要用到图片加载的三级缓存的思想。三级缓存分为内存缓存、本地缓存和网络缓存。
内存缓存 :将Bitmap缓存到内存中,运行速度快,可是内存容量小。
本地缓存 :将图片缓存到文件中,速度较慢,但容量较大。
网络缓存 :从网络获取图片,速度受网络影响。
若是咱们设计一个图片加载框架,流程必定是这样的:
上面是一些基本的概念,若是是具体的代码实现的话,大概须要这么几个方面的文件:
在咱们的手指触摸到屏幕的时候,事件实际上是经过 Activity -> ViewGroup -> View 这样的流程到达最后响应咱们触摸事件的 View。
说到事件分发,必不可少的是这几个方法:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent。接下来就按照Activity -> ViewGroup -> View 的流程来大体说一下事件分发机制。
咱们的手指触摸到屏幕的时候,会触发一个 Action_Down 类型的事件,当前页面的 Activity 会首先作出响应,也就是说会走到 Activity 的 dispatchTouchEvent() 方法内。在这个方法内部简单来讲是这么一个逻辑:
getWindow()方法返回了一个 Window 类型的对象,这个咱们都知道,在 Android 中,PhoneWindow 是Window 的惟一实现类。因此这句本质上是调用了``PhoneWindow中的superDispatchTouchEvent()。`
而在 PhoneWindow 的这个方法中实际调用了mDecor.superDispatchTouchEvent(event)。这个 mDecor 就是 DecorView,它是 FrameLayout 的一个子类,在 DecorView 中的 superDispatchTouchEvent() 中调用的是 super.dispatchTouchEvent()。到这里就很明显了,DecorView 是一个 FrameLayout 的子类,FrameLayout 是一个 ViewGroup 的子类,本质上调用的仍是 ViewGroup的dispatchTouchEvent()。
分析到这里,咱们的事件已经从 Activity 传递到了 ViewGroup,接下来咱们来分析下 ViewGroup 中的这几个事件处理方法。
在 ViewGroup 中的 dispatchTouchEvent()中的逻辑大体以下:
一般状况下 ViewGroup 的 onInterceptTouchEvent()都返回 false,也就是不拦截。这里须要注意的是事件序列,好比 Down 事件、Move 事件......Up事件,从 Down 到 Up 是一个完整的事件序列,对应着手指从按下到抬起这一系列的事件,若是 ViewGroup 拦截了 Down 事件,那么后续事件都会交给这个 ViewGroup的onTouchEvent。若是 ViewGroup 拦截的不是 Down 事件,那么会给以前处理这个 Down 事件的 View 发送一个 Action_Cancel 类型的事件,通知子 View 这个后续的事件序列已经被 ViewGroup 接管了,子 View 恢复以前的状态便可。
这里举一个常见的例子:在一个 Recyclerview 钟有不少的 Button,咱们首先按下了一个 button,而后滑动一段距离再松开,这时候 Recyclerview 会跟着滑动,并不会触发这个 button 的点击事件。这个例子中,当咱们按下 button 时,这个 button 接收到了 Action_Down 事件,正常状况下后续的事件序列应该由这个 button处理。但咱们滑动了一段距离,这时 Recyclerview 察觉到这是一个滑动操做,拦截了这个事件序列,走了自身的 onTouchEvent()方法,反映在屏幕上就是列表的滑动。而这时 button 仍然处于按下的状态,因此在拦截的时候须要发送一个 Action_Cancel 来通知 button 恢复以前状态。
事件分发最终会走到 View 的 dispatchTouchEvent()中。在 View 的 dispatchTouchEvent() 中没有 onInterceptTouchEvent(),这也很容易理解,View 不是 ViewGroup,不会包含其余子 View,因此也不存在拦截不拦截这一说。忽略一些细节,View 的 dispatchTouchEvent()中直接 return 了本身的 onTouchEvent()。若是 onTouchEvent()返回 true 表明事件被处理,不然未处理的事件会向上传递,直到有 View 处理了事件或者一直没有处理,最终到达了 Activity 的 onTouchEvent() 终止。
这里常常有人问 onTouch 和 onTouchEvent 的区别。首先,这两个方法都在 View 的 dispatchTouchEvent()中,是这么一个逻辑:
视图绘制的起点在 ViewRootImpl 类的 performTraversals()方法,在这个方法内实际上是按照顺序依次调用了 mView.measure()、mView.layout()、mView.draw()
View的绘制流程分为3步:测量、布局、绘制,分别对应3个方法 measure、layout、draw。
在 Android 中,Android 与js 的交互分为两个方面:Android 调用 js 里的方法、js 调用 Android 中的方法。
SparseArray,一般来说是 Android 中用来替代 HashMap 的一个数据结构。
准确来说,是用来替换key为 Integer 类型,value为Object 类型的HashMap。须要注意的是 SparseArray 仅仅实现了 Cloneable 接口,因此不能用Map来声明。
从内部结构来说,SparseArray 内部由两个数组组成,一个是 int[]类型的 mKeys,用来存放全部的键;另外一个是 Object[]类型的 mValues,用来存放全部的值。
最多见的是拿 SparseArray 跟HashMap 来作对比,因为 SparseArray 内部组成是两个数组,因此占用内存比 HashMap 要小。咱们都知道,增删改查等操做都首先须要找到相应的键值对,而 SparseArray 内部是经过二分查找来寻址的,效率很明显要低于 HashMap 的常数级别的时间复杂度。提到二分查找,这里还须要提一下的是二分查找的前提是数组已是排好序的,没错,SparseArray 中就是按照key进行升序排列的。
综合起来来讲,SparseArray 所占空间优于 HashMap,而效率低于 HashMap,是典型的时间换空间,适合较小容量的存储。
从源码角度来讲,我认为须要注意的是 SparseArray的remove()、put()和 gc()方法。
咱们知道内存中的 Bitmap 大小的计算公式是:长所占像素 * 宽所占像素 * 每一个像素所占内存。想避免 OOM 有两种方法:等比例缩小长宽、减小每一个像素所占的内存。
加载高清大图,好比清明上河图,首先屏幕是显示不下的,并且考虑到内存状况,也不可能一次性所有加载到内存。这时候就须要局部加载了,Android中有一个负责局部加载的类:BitmapRegionDecoder。使用方法很简单,经过BitmapRegionDecoder.newInstance()建立对象,以后调用decodeRegion(Rect rect, BitmapFactory.Options options)便可。第一个参数rect是要显示的区域,第二个参数是BitmapFactory中的内部类Options。