志在指尖
用双手敲打未来

分享几个重要的Android知识

面试,无非都是问上面这些问题(挺多的–!),延聘中高级的安卓开发会往深的去问,并且会问一延伸二。以下我先提出几点要点,是面试官根本必问的问题,请必定要去了解!
基础知识–四大组件(生命周期,运用场景,怎么启动)
java基础–数据结构,线程,mvc结构
通讯–网络衔接(HttpClient,HttpUrlConnetion),Socket
数据耐久化–SQLite,SharedPreferences,ContentProvider
功用优化–布局优化,内存优化,电量优化
安全–数据加密,代码混杂,WebView/Js调用,https
UI–动画
其他–JNI,AIDL,Handler,Intent等
开源结构–Volley,Gilde,RxJava等(简历上写你会的,用过的)
拓宽–Android6.0/7.0/8.0/9.0特性,kotlin言语,I/O大会
急急忙忙投简历,赶面试,还不如沉淀一两天时刻,再过一遍以上内容。想保险拿到一个offer,最好能了解完结原理,并且知道运用场景了。不要去背!要去了解!面试官听了一天这些内容是很厌恶的,最好能说出一些自己的见地。
需求免费领取Android面试材料和源码解析视频,请看主页末尾有图
Java中引证类型的差异,详细的运用场景
1.Java中引证类型分为四类:强引证、软引证、弱引证、虚引证。
强引证:强引证指的是经过new目标创立的引证,废物收回器即便是内存不足也不会收回强引证指向的目标。
软引证:软引证是经过SoftRefrence完结的,它的生命周期比强引证短,在内存不足,抛出OOM之前,废物收回器会收回软引证引证的目标。软引证常见的运用场景是存储一些内存灵敏的缓存,当内存不足时会被收回。
弱引证:弱引证是经过WeakRefrence完结的,它的生命周期比软引证还短,GC只需扫描到弱引证的目标就会收回。弱引证常见的运用场景也是存储一些内存灵敏的缓存。
虚引证:虚引证是经过FanttomRefrence完结的,它的生命周期最短,随时或许被收回。假如一个目标只被虚引证引证,咱们无法经过虚引证来拜访这个目标的任何特色和办法。它的作用仅仅是确保目标在finalize后,做某些作业。虚引证常见的运用场景是盯梢目标被废物收回的活动,当一个虚引证关联的目标被废物收回器收回之前会收到一条体系告诉。
2.Exception和Error的差异
Exception和Error都承继于Throwable,在Java中,只需Throwable类型的目标才能被throw或许catch,它是反常处理机制的根本组成类型。
Exception和Error体现了Java对不同反常状况的分类。Exception是程序正常运转中,能够预料的意外状况,或许并且应该被捕获,进行相应的处理。
Error是指在正常状况下,不大或许出现的状况,绝大部分Error都会使程序处于非正常、不可恢复的状况。已然是非正常,所以不便于也不需求捕获,常见的OutOfMemoryError便是Error的子类。
Exception又分为checkedException和uncheckedException。checkedException在代码里有必要显式的进行捕获,这是编译器检查的一部分。uncheckedException也便是运转时反常,相似空指针反常、数组越界等,一般是能够防止的逻辑错误,详细依据需求来判别是否需求捕获,并不会在编译器强制要求。
3.volatile
一般提到volatile,就不得不提到内存模型相关的概念。咱们都知道,在程序运转中,每条指令都是由CPU履行的,而指令的履行进程中,势必涉及到数据的读取和写入。程序运转中的数据都寄存在主存中,这样会有一个问题,因为CPU的履行速度是要远高于主存的读写速度,所以直接从主存中读写数据会下降CPU的功率。为了处理这个问题,就有了高速缓存的概念,在每个CPU中都有高速缓存,它会事先从主存中读取数据,在CPU运算之后在适宜的时分刷新到主存中。
这样的运转形式在单线程中是没有任何问题的,但在多线程中,会导致缓存共同性的问题。举个简略的比方:i=i+1,在两个线程中履行这句代码,假定i的初始值为0。咱们期望两个线程运转后得到2,那么有这样的一种状况,两个线程都从主存中读取i到各自的高速缓存中,这时分两个线程中的i都为0。在线程1履行完毕得到i=1,将之刷新到主存后,线程2开始履行,因为线程2中的i是高速缓存中的0,所以在履行完线程2之后刷新到主存的i仍旧是1。
所以这就导致了对同享变量的缓存共同性的问题,那么为了处理这个问题,提出了缓存共同性协议:当CPU在写数据时,假如发现操作的是同享变量,它会告诉其他CPU将它们内部的这个同享变量置为无效状况,当其他CPU读取缓存中的同享变量时,发现这个变量是无效的,它会从新从主存中读取最新的值。
在Java的多线程开发中,有三个重要概念:原子性、可见性、有序性。
原子性:一个或多个操作要么都不履行,要么都履行。
可见性:一个线程中对同享变量(类中的成员变量或静态变量)的修正,在其他线程立即可见。
有序性:程序履行的次序依照代码的次序履行。
把一个变量声明为volatile,其实便是确保了可见性和有序性。
可见性我上面现已说过了,在多线程开发中是很有必要的。这个有序性仍是得说一下,为了履行的功率,有时分会发生指令重排,这在单线程中指令重排之后的输出与咱们的代码逻辑输出仍是共同的。但在多线程中就或许发生问题,volatile在必定程度上能够防止指令重排。
volatile的原理是在生成的汇编代码中多了一个lock前缀指令,这个前缀指令相当于一个内存屏障,这个内存屏障有3个作用:
确保指令重排的时分不会把屏障后的指令排在屏障前,确保不会把屏障前的指令排在屏障后。
修正缓存中的同享变量后立即刷新到主存中。
当履行写操作时会导致其他CPU中的缓存无效。
4.网络相关面试题
http状况码
http与https的差异?https是怎么作业的?
http是超文本传输协议,而https能够简略了解为安全的http协议。https经过在http协议下增加了一层ssl协议对数据进行加密然后确保了安全。https的作用首要有两点:树立安全的信息传输通道,确保数据传输安全;承认网站的真实性。
5.Http与https的差异首要如下:
https需求到CA恳求证书,很少免费,因而需求必定的费用
http是明文传输,安全性低;而https在http的基础上经过ssl加密,安全性高
二者的默许端口不相同,http运用的默许端口是80;https运用的默许端口是443
6.https的作业流程
提到https的话首要要提到加密算法,加密算法分为两类:对称加密和非对称加密。
对称加密:加密宽和密用的都是相同的秘钥,长处是速度快,缺陷是安全性低。常见的对称加密算法有DES、AES等等。
非对称加密:非对称加密有一个秘钥对,分为公钥和私钥。一般来说,私钥自己持有,公钥能够公开给对方,长处是安全性比对称加密高,缺陷是数据传输功率比对称加密低。选用公钥加密的信息只需对应的私钥能够解密。常见的非对称加密包括RSA等。
在正式的运用场景中一般都是对称加密和非对称加密结合运用,运用非对称加密完结秘钥的传递,然后运用对称秘钥进行数据加密宽和密。二者结合既确保了安全性,又提高了数据传输功率。
7.https的详细流程如下:
7.1.客户端(一般是浏览器)先向服务器宣布加密通讯的恳求
支撑的协议版别,比方TLS1.0版
一个客户端生成的随机数random1,稍后用于生成”对话密钥”
支撑的加密办法,比方RSA公钥加密
支撑的紧缩办法
7.2.服务器收到恳求,然后呼应
承认运用的加密通讯协议版别,比方TLS1.0版别。假如浏览器与服务器支撑的版别不共同,服务器封闭加密通讯
一个服务器生成的随机数random2,稍后用于生成”对话密钥”
承认运用的加密办法,比方RSA公钥加密
服务器证书
7.3.客户端收到证书之后会首要会进行验证
首要验证证书的安全性
验证经过之后,客户端会生成一个随机数pre-mastersecret,然后运用证书中的公钥进行加密,然后传递给服务器端
①.服务器收到运用公钥加密的内容,在服务器端运用私钥解密之后取得随机数pre-mastersecret,然后依据radom1、radom2、pre-mastersecret经过必定的算法得出一个对称加密的秘钥,作为后边交互进程中运用对称秘钥。一起客户端也会运用radom1、radom2、pre-mastersecret,和相同的算法生成对称秘钥。
②.然后再后续的交互中就运用上一步生成的对称秘钥对传输的内容进行加密宽和密。
8.TCP三次握手流程
Android面试题
1.进程间通讯的办法有哪几种
AIDL、播送、文件、socket、管道
2.播送静态注册和动态注册的差异
动态注册播送不是常驻型播送,也便是说播送跟随Activity的生命周期。留意在Activity完毕前,移除播送接收器。静态注册是常驻型,也便是说当运用程序封闭后,假如有信息播送来,程序也会被体系调用自动运转。
当播送为有序播送时:优先级高的先接收(不分静态和动态)。同优先级的播送接收器,动态优先于静态
同优先级的同类播送接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
当播送为默许播送时:无视优先级,动态播送接收器优先于静态播送接收器。同优先级的同类播送接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后册的。
Android功用优化工具运用(这个问题建议配合Android中的功用优化)
Android中常用的功用优化工具包括这些:AndroidStudio自带的AndroidProfiler、LeakCanary、BlockCanary
Android自带的AndroidProfiler其实就很好用,AndroidProfiler能够检测三个方面的功用问题:CPU、MEMORY、NETWORK。
LeakCanary是一个第三方的检测内存走漏的库,咱们的项目集成之后LeakCanary会自动检测运用运转期间的内存走漏,并将之输出给咱们。
BlockCanary也是一个第三方检测UI卡顿的库,项目集成后Block也会自动检测运用运转期间的UI卡顿,并将之输出给咱们。
3.Android中的类加载器
PathClassLoader,只能加载体系中现已装置过的apk
DexClassLoader,能够加载jar/apk/dex,能够从SD卡中加载未装置的apk
4.Android中的动画有哪几类,它们的特色和差异是什么
Android中动画大致分为3类:帧动画、补间动画(ViewAnimation)、特色动画(ObjectAnimation)。
帧动画:经过xml装备一组图片,动态播放。很少会运用。
补间动画(ViewAnimation):大致分为旋转、通明、缩放、位移四类操作。很少会运用。
特色动画(ObjectAnimation):特色动画是现在运用的最多的一种动画,它比补间动画愈加强大。特色动画大致分为两种运用类型,分别是ViewPropertyAnimator和ObjectAnimator。前者适宜一些通用的动画,比方旋转、位移、缩放和通明,运用办法也很简略经过View.animate()即可得到ViewPropertyAnimator,之后进行相应的动画操作即可。后者适宜用于为咱们的自定义控件增加动画,当然首要咱们应该在自定义View中增加相应的getXXX()和setXXX()相应特色的getter和setter办法,这儿需求留意的是在setter办法内改动了自定义View中的特色后要调用invalidate()来刷新View的制作。之后调用ObjectAnimator.of特色类型()回来一个ObjectAnimator,调用start()办法启动动画即可。
5.补间动画与特色动画的差异:
补间动画是父容器不断的制作view,看起来像移动了作用,其实view没有变化,还在原地。
是经过不断改动view内部的特色值,真正的改动view。
6.Handler机制
提到Handler,就不得不提与之密切相关的这几个类:Message、MessageQueue,Looper。
Message。Message中有两个成员变量值得重视:target和callback。target其实便是发送音讯的Handler目标,callback是当调用handler.post(runnable)时传入的Runnable类型的使命。post工作的实质也是创立了一个Message,将咱们传入的这个runnable赋值给创立的Message的callback这个成员变量。
MessageQueue。音讯行列很明显是寄存音讯的行列,值得重视的是MessageQueue中的next()办法,它会回来下一个待处理的音讯。
Looper。Looper音讯轮询器其实是衔接Handler和音讯行列的中心。首要咱们都知道,假如想要在一个线程中创立一个Handler,首要要经过Looper.prepare()创立Looper,之后还得调用Looper.loop()敞开轮询。咱们侧重看一下这两个办法。
prepare()。这个办法做了两件事:首要经过ThreadLocal.get()获取当时线程中的Looper,假如不为空,则会抛出一个RunTimeException,意思是一个线程不能创立2个Looper。假如为null则履行下一步。第二步是创立了一个Looper,并经过ThreadLocal.set(looper)。将咱们创立的Looper与当时线程绑定。这儿需求提一下的是音讯行列的创立其实就发生在Looper的构造办法中。
loop()。这个办法敞开了整个工作机制的轮询。它的实质是敞开了一个死循环,不断的经过MessageQueue的next()办法获取音讯。拿到音讯后会调用msg.target.dispatchMessage()来做处理。其实咱们在提到Message的时分提到过,msg.target其实便是发送这个音讯的handler。这句代码的实质便是调用handler的dispatchMessage()。
Handler。上面做了这么多衬托,总算到了最重要的部分。Handler的剖析侧重在两个部分:发送音讯和处理音讯。
发送音讯。其实发送音讯除了sendMessage之外还有sendMessageDelayed和post以及postDelayed等等不同的办法。但它们的实质都是调用了sendMessageAtTime。在sendMessageAtTime这个办法中调用了enqueueMessage。在enqueueMessage这个办法中做了两件事:经过msg.target=this完结了音讯与当时handler的绑定。然后经过queue.enqueueMessage完结了音讯入队。
处理音讯。音讯处理的中心其实便是dispatchMessage()这个办法。这个办法里面的逻辑很简略,先判别msg.callback是否为null,假如不为空则履行这个runnable。假如为空则会履行咱们的handleMessage办法。
7.Android功用优化
Android中的功用优化在我看来分为以下几个方面:内存优化、布局优化、网络优化、装置包优化。
内存优化:下一个问题便是。
7.1布局优化:布局优化的实质便是削减View的层级。常见的布局优化计划如下
在LinearLayout和RelativeLayout都能够完结布局的状况下优先选择RelativeLayout,能够削减View的层级
将常用的布局组件抽取出来运用<include>标签
经过<ViewStub>标签来加载不常用的布局
运用<Merge>标签来削减布局的嵌套层次
7.2网络优化:常见的网络优化计划如下
尽量削减网络恳求,能够兼并的就尽量兼并
防止DNS解析,依据域名查询或许会耗费上百毫秒的时刻,也或许存在DNS绑架的风险。能够依据业务需求选用增加动态更新IP的办法,或许在IP办法拜访失利时切换到域名拜访办法。
大量数据的加载选用分页的办法
网络数据传输选用GZIP紧缩
参加网络数据的缓存,防止频繁恳求网络
上传图片时,在必要的时分紧缩图片
7.3装置包优化:装置包优化的中心便是削减apk的体积,常见的计划如下
运用混杂,能够在必定程度上削减apk体积,但实践作用微乎其微
削减运用中不必要的资源文件,比方图片,在不影响APP作用的状况下尽量紧缩图片,有必定的作用
在运用了SO库的时分优先保存v7版别的SO库,删掉其他版别的SO库。原因是在2018年,v7版别的SO库能够满足市面上绝大多数的要求,或许八九年前的手机满足不了,但咱们也没必要去适配老掉牙的手机。实践开发中削减apk体积的作用是十分显著的,假如你运用了许多SO库,比方说一个版别的SO库总共10M,那么只保存v7版别,删掉armeabi和v8版别的SO库,总共能够削减20M的体积。
8.Android内存优化
Android的内存优化在我看来分为两点:防止内存走漏、扩展内存,其实便是开源节省。
其实内存走漏的实质便是较长生命周期的目标引证了较短生命周期的目标。
常见的内存走漏:
单例形式导致的内存走漏。最常见的比方便是创立这个单例目标需求传入一个Context,这时分传入了一个Activity类型的Context,因为单例目标的静态特色,导致它的生命周期是从单例类加载到运用程序完毕为止,所以即便现已finish掉了传入的Activity,因为咱们的单例目标仍然持有Activity的引证,所以导致了内存走漏。处理办法也很简略,不要运用Activity类型的Context,运用Application类型的Context能够防止内存走漏。
静态变量导致的内存走漏。静态变量是放在办法区中的,它的生命周期是从类加载到程序完毕,能够看到静态变量生命周期是十分久的。最常见的因静态变量导致内存走漏的比方是咱们在Activity中创立了一个静态变量,而这个静态变量的创立需求传入Activity的引证this。在这种状况下即便Activity调用了finish也会导致内存走漏。原因便是因为这个静态变量的生命周期几乎和整个运用程序的生命周期共同,它一向持有Activity的引证,然后导致了内存走漏。
非静态内部类导致的内存走漏。非静态内部类导致内存走漏的原因是非静态内部类持有外部类的引证,最常见的比方便是在Activity中运用Handler和Thread了。运用非静态内部类创立的Handler和Thread在履行延时操作的时分会一向持有当时Activity的引证,假如在履行延时操作的时分就完毕Activity,这样就会导致内存走漏。处理办法有两种:第一种是运用静态内部类,在静态内部类中运用弱引证调用Activity。第二种办法是在Activity的onDestroy中调用handler.removeCallbacksAndMessages来撤销延时工作。
运用资源未及时封闭导致的内存走漏。常见的比方有:操作各种数据流未及时封闭,操作Bitmap未及时recycle等等。
运用第三方库未能及时解绑。有的三方库供给了注册宽和绑的功用,最常见的便是EventBus了,咱们都知道运用EventBus要在onCreate中注册,在onDestroy中解绑。假如没有解绑的话,EventBus其实是一个单例形式,他会一向持有Activity的引证,导致内存走漏。相同常见的还有RxJava,在运用Timer操作符做了一些延时操作后也要留意在onDestroy办法中调用disposable.dispose()来撤销操作。
特色动画导致的内存走漏。常见的比方便是在特色动画履行的进程中退出了Activity,这时View目标仍然持有Activity的引证然后导致了内存走漏。处理办法便是在onDestroy中调用动画的cancel办法撤销特色动画。
WebView导致的内存走漏。WebView比较特殊,即便是调用了它的destroy办法,仍然会导致内存走漏。其实防止WebView导致内存走漏的最好办法便是让WebView所在的Activity处于另一个进程中,当这个Activity完毕时杀死当时WebView所在的进程即可,我记住阿里钉钉的WebView便是别的敞开的一个进程,应该也是选用这种办法防止内存走漏。
扩展内存,为什么要扩展咱们的内存呢?有时分咱们实践开发中不可防止的要运用许多第三方商业的SDK,这些SDK其实有好有坏,大厂的SDK或许内存走漏会少一些,但一些小厂的SDK质量也就不太靠谱一些。那应对这种咱们无法改动的状况,最好的办法便是扩展内存。重要的Android知识
扩展内存一般有两种办法:一个是在清单文件中的Application下增加largeHeap=”true”这个特色,另一个便是同一个运用敞开多个进程来扩展一个运用的总内存空间。第二种办法其实就很常见了,比方说我运用过个推的SDK,个推的Service其实便是处在别的一个独自的进程中。
Android中的内存优化总的来说便是开源和节省,开源便是扩展内存,节省便是防止内存走漏。
9.Binder机制
在Linux中,为了防止一个进程对其他进程的干扰,进程之间是彼此独立的。在一个进程中其实还分为用户空间和内核空间。这儿的阻隔分为两个部分,进程间的阻隔和进程内的阻隔。
已然进程间存在阻隔,那其实也是存在着交互。进程间通讯便是IPC,用户空间和内核空间的通讯便是体系调用。
Linux为了确保独立性和安全性,进程之间不能直接彼此拜访,Android是依据Linux的,所以也是需求处理进程间通讯的问题。
其实Linux进程间通讯有许多办法,比方管道、socket等等。为什么Android进程间通讯选用了Binder而不是Linux已有的办法,首要是有这么两点考虑:功用和安全
功用。在移动设备上对功用要求是比较严苛的。Linux传统的进程间通讯比方管道、socket等等进程间通讯是需求复制两次数据,而Binder则只需求一次。所以Binder在功用上是优于传统进程通讯的。
安全。传统的Linux进程通讯是不包括通讯双方的身份验证的,这样会导致一些安全性问题。而Binder机制自带身份验证,然后有用的提高了安全性。
Binder是依据CS架构的,有四个首要组成部分。
Client。客户端进程。
Server。服务端进程。
ServiceManager。供给注册、查询和回来署理服务目标的功用。
Binder驱动。首要担任树立进程间的Binder衔接,进程间的数据交互等等底层操作。
Binder机制首要的流程是这样的:
服务端经过Binder驱动在ServiceManager中注册咱们的服务。
客户端经过Binder驱动查询在ServiceManager中注册的服务。
ServiceManager经过Binder驱动回来服务端的署理目标。
客户端拿到服务端的署理目标后即可进行进程间通讯。
9.LruCache的原理
LruCache的中心原理便是对LinkedHashMap的有用运用,它的内部存在一个LinkedHashMap成员变量。值得咱们重视的有四个办法:构造办法、get、put、trimToSize。
构造办法:在LruCache的构造办法中做了两件事,设置了maxSize、创立了一个LinkedHashMap。这儿值得留意的是LruCache将LinkedHashMap的accessOrder设置为了true,accessOrder便是遍历这个LinkedHashMap的输出次序。true代表依照拜访次序输出,false代表按增加次序输出,因为一般都是依照增加次序输出,所以accessOrder这个特色默许是false,但咱们的LruCache需求按拜访次序输出,所以显式的将accessOrder设置为true。
get办法:实质上是调用LinkedHashMap的get办法,因为咱们将accessOrder设置为了true,所以每调用一次get办法,就会将咱们拜访的当时元素放置到这个LinkedHashMap的尾部。
put办法:实质上也是调用了LinkedHashMap的put办法,因为LinkedHashMap的特性,每调用一次put办法,也会将新参加的元素放置到LinkedHashMap的尾部。增加之后会调用trimToSize办法来确保增加后的内存不超越maxSize。
trimToSize办法:trimToSize办法的内部其实是敞开了一个while(true)的死循环,不断的从LinkedHashMap的首部删去元素,直到删去之后的内存小于maxSize之后运用break跳出循环。
其实到这儿咱们能够总结一下,为什么这个算法叫最近最少运用算法呢?原理很简略,咱们的每次put或许get都能够看做一次拜访,因为LinkedHashMap的特性,会将每次拜访到的元素放置到尾部。当咱们的内存抵达阈值后,会触发trimToSize办法来删去LinkedHashMap首部的元素,直到当时内存小于maxSize。为什么删去首部的元素,原因很明显:咱们最近常常拜访的元素都会放置到尾部,那首部的元素肯定便是最近最少运用的元素了,因此当内存不足时应当优先删去这些元素。
10.DiskLruCache原理
规划一个图片的异步加载结构
规划一个图片加载结构,肯定要用到图片加载的三级缓存的思想。三级缓存分为内存缓存、本地缓存和网络缓存。
内存缓存:将Bitmap缓存到内存中,运转速度快,但是内存容量小。
本地缓存:将图片缓存到文件中,速度较慢,但容量较大。
网络缓存:从网络获取图片,速度受网络影响。
假如咱们规划一个图片加载结构,流程必定是这样的:
拿到图片url后首要从内存中查找BItmap,假如找到直接加载。
内存中没有找到,会从本地缓存中查找,假如本地缓存能够找到,则直接加载。
内存和本地都没有找到,这时会从网络下载图片,下载到后会加载图片,并且将下载到的图片放到内存缓存和本地缓存中。
上面是一些根本的概念,假如是详细的代码完结的话,大概需求这么几个方面的文件:
首要需求确认咱们的内存缓存,这儿一般用的都是LruCache。
确认本地缓存,一般用的是DiskLruCache,这儿需求留意的是图片缓存的文件名一般是url被MD5加密后的字符串,为了防止文件名直接露出图片的url。
内存缓存和本地缓存确认之后,需求咱们创立一个新的类MemeryAndDiskCache,当然,名字随意起,这个类包括了之前提到的LruCache和DiskLruCache。在MemeryAndDiskCache这个类中咱们定义两个办法,一个是getBitmap,另一个是putBitmap,对应着图片的获取和缓存,内部的逻辑也很简略。getBitmap中按内存、本地的优先级去取BItmap,putBitmap中先缓存内存,之后缓存到本地。
在缓存策略类确认好之后,咱们创立一个ImageLoader类,这个类有必要包括两个办法,一个是展现图片displayImage(url,imageView),另一个是从网络获取图片downloadImage(url,imageView)。在展现图片办法中首要要经过ImageView.setTag(url),将url和imageView进行绑定,这是为了防止在列表中加载网络图片时会因为ImageView的复用导致的图片错位的bug。之后会从MemeryAndDiskCache中获取缓存,假如存在,直接加载;假如不存在,则调用从网络获取图片这个办法。从网络获取图片办法许多,这儿我一般都会运用OkHttp+Retrofit。当从网络中获取到图片之后,首要判别一下imageView.getTag()与图片的url是否共同,假如共同则加载图片,假如不共同则不加载图片,经过这样的办法防止了列表中异步加载图片的错位。一起在获取到图片之后会经过MemeryAndDiskCache来缓存图片。
11.Android中的工作分发机制
在咱们的手指触摸到屏幕的时分,工作其实是经过Activity->ViewGroup->View这样的流程抵达最终呼应咱们触摸工作的View。
提到工作分发,必不可少的是这几个办法:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent。接下来就依照Activity->ViewGroup->View的流程来大致说一下工作分发机制。
咱们的手指触摸到屏幕的时分,会触发一个Action_Down类型的工作,当时页面的Activity会首要做出呼应,也便是说会走到Activity的dispatchTouchEvent()办法内。在这个办法内部简略来说是这么一个逻辑:
调用getWindow.superDispatchTouchEvent()。
假如上一步回来true,直接回来true;不然就return自己的onTouchEvent()。
这个逻辑很好了解,getWindow().superDispatchTouchEvent()假如回来true代表当时工作现已被处理,无需调用自己的onTouchEvent;不然代表工作并没有被处理,需求Activity自己处理,也便是调用自己的onTouchEvent。
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()中的逻辑大致如下:
经过onInterceptTouchEvent()判别当时ViewGroup是否阻拦工作,默许的ViewGroup都是不阻拦的;
假如阻拦,则return自己的onTouchEvent();
假如不阻拦,则依据child.dispatchTouchEvent()的回来值判别。假如回来true,则returntrue;不然return自己的onTouchEvent(),在这儿完结了未处理工作的向上传递。
一般状况下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()中,是这么一个逻辑:
假如touchListener不为null,并且这个View是enable的,并且onTouch回来的是true,满足这三个条件时会直接returntrue,不会走onTouchEvent()办法。
上面只需有一个条件不满足,就会走到onTouchEvent()办法中。所以onTouch的次序是在onTouchEvent之前的。
12.View的制作流程
视图制作的起点在ViewRootImpl类的performTraversals()办法,在这个办法内其实是依照次序顺次调用了mView.measure()、mView.layout()、mView.draw()
View的制作流程分为3步:丈量、布局、制作,分别对应3个办法measure、layout、draw。
丈量阶段。measure办法会被父View调用,在measure办法中做一些优化和准备作业后会调用onMeasure办法进行实践的自我丈量。onMeasure办法在View和ViewGroup做的作业是不相同的:
View。View中的onMeasure办法会核算自己的尺度并经过setMeasureDimension保存。
ViewGroup。ViewGroup中的onMeasure办法会调用一切子View的measure办法进行自我丈量并保存。然后经过子View的尺度和方位核算出自己的尺度并保存。
布局阶段。layout办法会被父View调用,layout办法会保存父View传进来的尺度和方位,并调用onLayout进行实践的内部布局。onLayout在View和ViewGroup中做的作业也是不相同的:
View。因为View是没有子View的,所以View的onLayout里面什么都不做。
ViewGroup。ViewGroup中的onLayout办法会调用一切子View的layout办法,把尺度和方位传给他们,让他们完结自我的内部布局。
制作阶段。draw办法会做一些调度作业,然后会调用onDraw办法进行View的自我制作。draw办法的调度流程大致是这样的:
制作背景。对应drawBackground(Canvas)办法。
制作主体。对应onDraw(Canvas)办法。
制作子View。对应dispatchDraw(Canvas)办法。
制作滑动相关和前景。对应onDrawForeground(Canvas)。
Android源码中常见的规划形式以及自己在开发中常用的规划形式
13.Android与js是怎么交互的
在Android中,Android与js的交互分为两个方面:Android调用js里的办法、js调用Android中的办法。
Android调js。Android调js有两种办法:
WebView.loadUrl(“javascript:js中的办法名”)。这种办法的长处是很简练,缺陷是没有回来值,假如需求拿到js办法的回来值则需求js调用Android中的办法来拿到这个回来值。
WebView.evaluateJavaScript(“javascript:js中的办法名”,ValueCallback)。这种办法比loadUrl好的是能够经过ValueCallback这个回调拿到js办法的回来值。缺陷是这个办法Android4.4才有,兼容性较差。不过放在2018年来说,市面上绝大多数App都要求最低版别是4.4了,所以我认为这个兼容性问题不大。
js调Android。js调Android有三种办法:
WebView.addJavascriptInterface()。这是官方处理js调用Android办法的计划,需求留意的是要在供js调用的Android办法上加上@JavascriptInterface注解,以防止安全漏洞。这种计划的缺陷是Android4.2曾经会有安全漏洞,不过在4.2今后现已修复了。相同,在2018年来说,兼容性问题不大。
重写WebViewClient的shouldOverrideUrlLoading()办法来阻拦url,拿到url后进行解析,假如契合双方的规则,即可调用Android办法。长处是防止了Android4.2曾经的安全漏洞,缺陷也很明显,无法直接拿到调用Android办法的回来值,只能经过Android调用js办法来获取回来值。
重写WebChromClient的onJsPrompt()办法,同前一个办法相同,拿到url之后先进行解析,假如契合双方规则,即可调用Android办法。最终假如需求回来值,经过result.confirm(“Android办法回来值”)即可将Android的回来值回来给js。办法的长处是没有漏洞,也没有兼容性限制,一起还能够便利的获取Android办法的回来值。其实这儿需求留意的是在WebChromeClient中除了onJsPrompt之外还有onJsAlert和onJsConfirm办法。那么为什么不选择另两个办法呢?原因在于onJsAlert是没有回来值的,而onJsConfirm只需true和false两个回来值,一起在前端开发中prompt办法根本不会被调用,所以才会选用onJsPrompt。
14.热修复原理
Activity启动进程
SparseArray原理
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()办法。
remove()。SparseArray的remove()办法并不是直接删去之后再紧缩数组,而是即将删去的value设置为DELETE这个SparseArray的静态特色,这个DELETE其实便是一个Object目标,一起会将SparseArray中的mGarbage这个特色设置为true,这个特色是便于在适宜的时分调用自身的gc()办法紧缩数组来防止浪费空间。这样能够提高功率,假如将来要增加的key等于删去的key,那么会即将增加的value掩盖DELETE。
gc()。SparseArray中的gc()办法跟JVM的GC其实彻底没有任何关系。gc()办法的内部实践上便是一个for循环,将value不为DELETE的键值对往前移动掩盖value为DELETE的键值对来完结数组的紧缩,一起将mGarbage置为false,防止内存的浪费。
put()。put办法是这么一个逻辑,假如经过二分查找在mKeys数组中找到了key,那么直接掩盖value即可。假如没有找到,会拿到与数组中与要增加的key最接近的key索引,假如这个索引对应的value为DELETE,则直接把新的value掩盖DELETE即可,在这儿能够防止数组元素的移动,然后提高了功率。假如value不为DELETE,会判别mGarbage,假如为true,则会调用gc()办法紧缩数组,之后会找到适宜的索引,将索引之后的键值对后移,刺进新的键值对,这个进程中或许会触发数组的扩容。
14.图片加载怎么防止OOM
咱们知道内存中的Bitmap巨细的核算公式是:长所占像素*宽所占像素*每个像素所占内存。想防止OOM有两种办法:等比例缩小长宽、削减每个像素所占的内存。
等比缩小长宽。咱们知道Bitmap的创立是经过BitmapFactory的工厂办法,decodeFile()、decodeStream()、decodeByteArray()、decodeResource()。这些办法中都有一个Options类型的参数,这个Options是BitmapFactory的内部类,存储着BItmap的一些信息。Options中有一个特色:inSampleSize。咱们经过修正inSampleSize能够缩小图片的长宽,然后削减BItmap所占内存。需求留意的是这个inSampleSize巨细需求是2的幂次方,假如小于1,代码会强制让inSampleSize为1。
削减像素所占内存。Options中有一个特色inPreferredConfig,默许是ARGB_8888,代表每个像素所占尺度。咱们能够经过将之修正为RGB_565或许ARGB_4444来削减一半内存。
15.大图加载
加载高清大图,比方清明上河图,首要屏幕是显现不下的,并且考虑到内存状况,也不或许一次性全部加载到内存。这时分就需求部分加载了,Android中有一个担任部分加载的类:BitmapRegionDecoder。运用办法很简略,经过BitmapRegionDecoder.newInstance()创立目标,之后调用decodeRegion(Rectrect,BitmapFactory.Optionsoptions)即可。第一个参数rect是要显现的区域,第二个参数是BitmapFactory中的内部类Options。
16.Android三方库的源码剖析
因为源码剖析篇幅太大,所以这儿之贴出我的源码剖析的链接(掘金)。
17.OkHttp
OkHttp源码剖析
18.Retrofit
Retrofit源码剖析1
Retrofit源码剖析2
Retrofit源码剖析3
19.RxJava
RxJava源码剖析
20.Glide
Glide源码剖析
21.EventBus
EventBus源码剖析
大致是这么一个流程:
register:
获取订阅者的Class目标
运用反射查找订阅者中的工作处理办法调集
遍历工作处理办法调集,调用subscribe(subscriber,subscriberMethod)办法,在subscribe办法内:
假如工作承继性为true,遍历这个Map类型的stickEvents,经过isAssignableFrom办法判别当时工作是否是遍历工作的父类,假如是则发送工作
假如工作承继性为false,经过stickyEvents.get(eventType)获取工作并发送
假如工作类型调集为空则创立一个新的调集,这一步意图是延迟调集的初始化
拿到工作类型调集后将新的工作类型参加到调集中
假如Subscription调集为空则创立一个新的调集,这一步意图是延迟调集的初始化
拿到Subscription调集后遍历这个调集,经过比较工作处理的优先级,将新的Subscription目标参加适宜的方位
经过subscriberMethod获取处理的工作类型eventType
将订阅者subscriber和办法subscriberMethod绑在一起构成一个Subscription目标
经过subscriptionsByEventType.get(eventType)获取Subscription调集
经过typesBySubscriber.get(subscriber)获取工作类型调集
判别当时工作类型是否是sticky
假如当时工作类型不是sticky(粘性工作),subscribe(subscriber,subscriberMethod)到此终结
假如是sticky,判别EventBus中的一个工作承继性的特色,默许是true
post:
postSticky
将工作参加到stickyEvents这个Map类型的调集中
调用post办法
post
工作承继性为true,找到当时工作一切的父类型并调用postSingleEventForEventType办法发送工作
工作承继性为false,只发送当时工作类型的工作
在postToSubscription中分为四种状况
POSTING,调用invokeSubscriber(subscription,event)处理工作,实质是method.invoke()反射
MAIN,假如在主线程直接invokeSubscriber处理;反之经过handler切换到主线程调用invokeSubscriber处理工作
BACKGROUND,假如不在主线程直接invokeSubscriber处理工作;反之敞开一条线程,在线程中调用invokeSubscriber处理工作
ASYNC,敞开一条线程,在线程中调用invokeSubscriber处理工作
在postSingleEventForEventType中,经过subscriptionsByEventType.get(eventClass)获取Subscription类型调集
遍历这个调集,调用postToSubscription发送工作
将工作参加当时线程的工作行列中
经过while循环不断从工作行列中取出工作并调用postSingleEvent办法发送工作
在postSingleEvent中,判别工作承继性,默许为true
unregister:
删去subscriptionsByEventType中与订阅者相关的一切subscription
删去typesBySubscriber中与订阅者相关的一切类型
22.数据结构与算法
手写快排
手写归并排序
手写堆以及堆排序
说一下排序算法的差异(时刻复杂度和空间复杂度)
写在最终
最终我想说:关于程序员来说,要学习的知识内容、技能有太多太多,要想不被环境淘汰就只需不断提高自己,从来都是咱们去习惯环境,而不是环境来习惯咱们!
作者:小小小小怪兽_666
链接:https://www.jianshu.com/p/9384e6a159f8
来历:简书
著作权归作者一切。商业转载请联系作者取得授权,非商业转载请注明出处。

未经允许不得转载:IT技术网站 » 分享几个重要的Android知识
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载