onStart()是activity界面被显示出来的时候执行的,但不能与它交互; onResume()是当该activity与用户能进行交互时被执行,用户能够得到activity的焦点,可以与用户交互。html
startActivity最终都会调用startActivityForResult,经过ActivityManagerProxy调用system_server进程中ActivityManagerService的startActvity方法,若是须要启动的Activity所在进程未启动,则调用Zygote孵化应用进程,进程建立后会调用应用的ActivityThread的main方法,main方法调用attach方法将应用进程绑定到ActivityManagerService(保存应用的ApplicationThread的代理对象)并开启loop循环接收消息。ActivityManagerService经过ApplicationThread的代理发送Message通知启动Activity,ActivityThread内部Handler处理handleLaunchActivity,依次调用performLaunchActivity,handleResumeActivity(即activity的onCreate,onStart,onResume)。
深刻理解Activity启动流程java
Android平台上虚拟机运行的是Dex字节码,一种对class文件优化的产物,传统Class文件是一个Java源码文件会生成一个.class文件,而Android是把全部Class文件进行合并,优化,而后生成一个最终的class.dex,目的是把不一样class文件重复的东西只需保留一份,若是咱们的Android应用不进行分dex处理,最后一个应用的apk只会有一个dex文件。 Android中经常使用的有两种类加载器,DexClassLoader和PathClassLoader,它们都继承于BaseDexClassLoader。区别在于调用父类构造器时,DexClassLoader多传了一个optimizedDirectory参数,这个目录必须是内部存储路径,用来缓存系统建立的Dex文件。而PathClassLoader该参数为null,只能加载内部存储目录的Dex文件。因此咱们能够用DexClassLoader去加载外部的apk。android
Android是基于事件驱动的,即全部Activity的生命周期都是经过Handler事件驱动的。loop方法中会调用MessageQueue的next方法获取下一个message,当没有消息时,基于Linux pipe/epoll机制会阻塞在loop的queue.next()中的nativePollOnce()方法里,并不会消耗CPU。git
IdleHandler是一个回调接口,能够经过MessageQueue的addIdleHandler添加实现类。当MessageQueue中的任务暂时处理完了(没有新任务或者下一个任务延时在以后),这个时候会回调这个接口,返回false,那么就会移除它,返回true就会在下次message处理完了的时候继续回调。github
同步屏障能够经过MessageQueue.postSyncBarrier函数来设置。该方法发送了一个没有target的Message到Queue中,在next方法中获取消息时,若是发现没有target的Message,则在必定的时间内跳过同步消息,优先执行异步消息。再换句话说,同步屏障为Handler消息机制增长了一种简单的优先级机制,异步消息的优先级要高于同步消息。在建立Handler时有一个async参数,传true表示此handler发送的时异步消息。ViewRootImpl.scheduleTraversals方法就使用了同步屏障,保证UI绘制优先执行。web
View的绘制从ActivityThread类中Handler的处理RESUME_ACTIVITY事件开始,在执行performResumeActivity以后,建立Window以及DecorView并调用WindowManager的addView方法添加到屏幕上,addView又调用ViewRootImpl的setView方法,最终执行performTraversals方法,依次执行performMeasure,performLayout,performDraw。也就是view绘制的三大过程。
measure过程测量view的视图大小,最终须要调用setMeasuredDimension方法设置测量的结果,若是是ViewGroup须要调用measureChildren或者measureChild方法进而计算本身的大小。
layout过程是摆放view的过程,View不须要实现,一般由ViewGroup实现,在实现onLayout时能够经过getMeasuredWidth等方法获取measure过程测量的结果进行摆放。 draw过程先是绘制背景,其次调用onDraw()方法绘制view的内容,再而后调用dispatchDraw()调用子view的draw方法,最后绘制滚动条。ViewGroup默认不会执行onDraw方法,若是复写了onDraw(Canvas)方法,须要调用 setWillNotDraw(false);清楚不须要绘制的标记。
Android视图绘制流程彻底解析,带你一步步深刻了解View(二)算法
MeasureSpec表明一个32位int值,高两位表明SpecMode(测量模式),低30位表明SpecSize(具体大小)。 SpecMode有三类:json
首先getMeasureWidth()方法在measure()过程结束后就能够获取到了,而getWidth()方法要在layout()过程结束后才能获取到。另外,getMeasureWidth()方法中的值是经过setMeasuredDimension()方法来进行设置的,而getWidth()方法中的值则是经过视图右边的坐标减去左边的坐标计算出来的。数组
相同点:三个方法都有刷新界面的效果。 不一样点:invalidate和postInvalidate只会调用onDraw()方法;requestLayout则会从新调用onMeasure、onLayout、onDraw。
调用了invalidate方法后,会为该View添加一个标记位,同时不断向父容器请求刷新,父容器经过计算得出自身须要重绘的区域,直到传递到ViewRootImpl中,最终触发performTraversals方法,进行开始View树重绘流程(只绘制须要重绘的视图)。
调用requestLayout方法,会标记当前View及父容器,同时逐层向上提交,直到ViewRootImpl处理该事件,ViewRootImpl会调用三大流程,从measure开始,对于每个含有标记位的view及其子View都会进行测量onMeasure、布局onLayout、绘制onDraw。
Android View 深度分析requestLayout、invalidate与postInvalidate
为何使用Binder?
概念 进程隔离 进程空间划分:用户空间(User Space)/内核空间(Kernel Space) 系统调用:用户态与内核态
原理 跨进程通讯是须要内核空间作支持的。传统的 IPC 机制如管道、Socket 都是内核的一部分,所以经过内核支持来实现进程间通讯天然是没问题的。可是 Binder 并非 Linux 系统内核的一部分,那怎么办呢?这就得益于 Linux 的动态内核可加载模块(Loadable Kernel Module,LKM)的机制;模块是具备独立功能的程序,它能够被单独编译,可是不能独立运行。它在运行时被连接到内核做为内核的一部分运行。这样,Android 系统就能够经过动态添加一个内核模块运行在内核空间,用户进程之间经过这个内核模块做为桥梁来实现通讯。
在 Android 系统中,这个运行在内核空间,负责各个用户进程经过 Binder 实现通讯的内核模块就叫 Binder 驱动(Binder Dirver)。
那么在 Android 系统中用户进程之间是如何经过这个内核模块(Binder 驱动)来实现通讯的呢?难道是和前面说的传统 IPC 机制同样,先将数据从发送方进程拷贝到内核缓存区,而后再将数据从内核缓存区拷贝到接收方进程,经过两次拷贝来实现吗?显然不是,不然也不会有开篇所说的 Binder 在性能方面的优点了。
这就不得不通道 Linux 下的另外一个概念:内存映射。
Binder IPC 机制中涉及到的内存映射经过 mmap() 来实现,mmap() 是操做系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系创建后,用户对这块内存区域的修改能够直接反应到内核空间;反以内核空间对这段区域的修改也能直接反应到用户空间。
一次完整的 Binder IPC 通讯过程一般是这样:
Binder通信模型 Binder是基于C/S架构的,其中定义了4个角色:Client、Server、Binder驱动和ServiceManager。
ServiceManager是一个单独的进程,那么Server与ServiceManager通信是靠什么呢? 当Android系统启动后,会建立一个名称为servicemanager的进程,这个进程经过一个约定的命令BINDERSETCONTEXT_MGR向Binder驱动注册,申请成为为ServiceManager,Binder驱动会自动为ServiceManager建立一个Binder实体。而且这个Binder实体的引用在全部的Client中都为0,也就说各个Client经过这个0号引用就能够和ServiceManager进行通讯。Server经过0号引用向ServiceManager进行注册,Client经过0号引用就能够获取到要通讯的Server的Binder引用。 写给 Android 应用工程师的 Binder 原理剖析
一篇文章了解相见恨晚的 Android Binder 进程间通信机制
Serializable是Java提供的一个序列化接口,是一个空接口,用于标示对象是否能够支持序列化,经过ObjectOutputStrean及ObjectInputStream实现序列化和反序列化的过程。注意能够为须要序列化的对象设置一个serialVersionUID,在反序列化的时候系统会检测文件中的serialVersionUID是否与当前类的值一致,若是不一致则说明类发生了修改,反序列化失败。所以对于可能会修改的类最好指定serialVersionUID的值。
Parcelable是Android特有的一个实现序列化的接口,在Parcel内部包装了可序列化的数据,能够在Binder中自由传输。序列化的功能由writeToParcel方法来完成,最终经过Parcel的一系列write方法完成。反序列化功能由CREAOR来完成,其内部标明了如何建立序列化对象和数组,并经过Parcel的一系列read方法来完成反序列化的过程。
Fragment可见状态改变时会被调用setUserVisibleHint()方法,能够经过复写该方法实现Fragment的懒加载,但须要注意该方法可能在onVIewCreated以前调用,须要确保界面已经初始化完成的状况下再去加载数据,避免空指针。
Fragment的懒加载
缓存区别:
优势 RecylerView提供了局部刷新的接口,经过局部刷新,就能避免调用许多无用的bindView。 RecyclerView的扩展性更强大(LayoutManager、ItemDecoration等)。
Android中的Dalvik虚拟机相较于Java虚拟机针对手机的特色作了不少优化。
Dalvik基于寄存器,而JVM基于栈。在基于寄存器的虚拟机里,能够更为有效的减小冗余指令的分发和减小内存的读写访问。
Dalvik通过优化,容许在有限的内存中同时运行多个虚拟机的实例,而且每个 Dalvik应用做为一个独立的Linux进程执行。
java虚拟机运行的是java字节码。(java类会被编译成一个或多个字节码.class文件,打包到.jar文件中,java虚拟机从相应的.class文件和.jar文件中获取相应的字节码) Dalvik运行的是自定义的.dex字节码格式。(java类被编译成.class文件后,会经过一个dx工具将全部的.class文件转换成一个.dex文件,而后dalvik虚拟机会从其中读取指令和数据)
Android开发之浅谈java虚拟机和Dalvik虚拟机的区别
查看当前链接的设备:adb devices 安装应用:adb install -r <apk_path> -r表示覆盖安装 卸载apk:adb uninstall
APK主要由如下几部分组成:
其中占据较大内存的是res资源、lib、class.dex,所以咱们能够从下面的几个方面下手:
lint
工具来检测没有使用到的资源,或者在gradle中配置shrinkResources
来删除包括库中全部的无用的资源,须要配合proguard压缩代码使用。这里须要注意项目中是否存在使用getIdentifier方式获取资源,这种方式相似反射lint及shrinkResources没法检测状况。若是存在这种方式,则须要配置一个keep.xml来记录使用反射获取的资源。压缩代码和资源缓存的响应头:
Cache-control:标明缓存的最大存活时常; Date:服务器告诉客户端,该资源的发送时间; Expires:表示过时时间(该字段是1.0的东西,当cache-control和该字段同时存在的条件下,cache-control的优先级更高); Last-Modified:服务器告诉客户端,资源的最后修改时间; 还有一个字段,这个图没给出,就是E-Tag:当前资源在服务器的惟一标识,可用于判断资源的内容是否被修改了。 除以上响应头字段之外,还需了解两个相关的Request请求头:If-Modified-since、If-none-Match。这两个字段是和Last-Modified、E-Tag配合使用的。大体流程以下: 服务器收到请求时,会在200 OK中回送该资源的Last-Modified和ETag头(服务器支持缓存的状况下才会有这两个头哦),客户端将该资源保存在cache中,并记录这两个属性。当客户端须要发送相同的请求时,根据Date + Cache-control来判断是否缓存过时,若是过时了,会在请求中携带If-Modified-Since和If-None-Match两个头。两个头的值分别是响应中Last-Modified和ETag头的值。服务器经过这两个头判断本地资源未发生变化,客户端不须要从新下载,返回304响应。
OkHttpClient经过newCall能够将一个Request构建成一个Call,Call表示准备被执行的请求。Call调用executed或enqueue会调用Dispatcher对应的方法在当前线程或者一步开始执行请求,通过RealInterceptorChain得到最终结果,RealInterceptorChain是一个拦截器链,其中依次包含如下拦截器:
Retrofit采用动态代理,建立声明service接口的实现对象。当咱们调用service的方法时候会执行InvocationHandler的invoke方法。在这方法中:首先,经过method把它转换成ServiceMethod,该类是对声明方法的解析,能够进一步将设定参数变成Request ;而后,经过serviceMethod, args获取到okHttpCall 对象,实际调用okhttp的网络请求方法就在该类中,而且会使用serviceMethod中的responseConverter对ResponseBody转化;最后,再把okHttpCall进一步封装成声明的返回对象(默认是ExecutorCallbackCall,将本来call的回调转发至UI线程)。
Retrofit2使用详解及从源码中解析原理
Retrofit2 彻底解析 探索与okhttp之间的关系
在Activity中,定义一个Observable(Subject),在不一样的生命周期发射不一样的事件; 经过compose操做符(内部实际上仍是依赖takeUntil操做符),定义了上游数据,当其接收到Subject的特定事件时,取消订阅; Subject的特定事件并不是是ActivityEvent,而是简单的boolean,它已经内部经过combineLast操做符进行了对应的转化。
程序在启动的时候,并不会一次性加载程序所要用的全部class文件,而是根据程序的须要,经过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存以后,才能被其它class所引用。因此ClassLoader就是用来动态加载class文件到内存当中用的。
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中准备、验证、解析3个部分统称为链接(Linking)。
Java中存在3种类加载器: (1) Bootstrap ClassLoader : 将存放于<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,而且是虚拟机识别的(仅按照文件名识别,如 rt.jar 名字不符合的类库即便放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器没法被Java程序直接引用 。 (2) Extension ClassLoader : 将<JAVA_HOME>\lib\ext目录下的,或者被java.ext.dirs系统变量所指定的路径中的全部类库加载。开发者能够直接使用扩展类加载器。 (3) Application ClassLoader : 负责加载用户类路径(ClassPath)上所指定的类库,开发者可直接使用。 每一个ClassLoader实例都有一个父类加载器的引用(不是继承关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)自己没有父类加载器,可是能够用作其余ClassLoader实例的父类加载器。 当一个ClassLoader 实例须要加载某个类时,它会试图在亲自搜索这个类以前先把这个任务委托给它的父类加载器,这个过程是由上而下依次检查的,首先由顶层的类加载器Bootstrap ClassLoader进行加载,若是没有加载到,则把任务转交给Extension ClassLoader加载,若是也没有找到,则转交给AppClassLoader进行加载,仍是没有的话,则交给委托的发起者,由它到指定的文件系统或者网络等URL中进行加载类。尚未找到的话,则会抛出CLassNotFoundException异常。不然将这个类生成一个类的定义,并将它加载到内存中,最后返回这个类在内存中的Class实例对象。
JVM在判断两个class是否相同时,不只要判断两个类名是否相同,还要判断是不是同一个类加载器加载的。
在JDK1.6,JDK1.7中,HashMap采用数组+链表实现,即便用链表处理冲突,同一hash值的链表都存储在一个链表里。可是当位于一个链表中的元素较多,即hash值相等的元素较多时,经过key值依次查找的效率较低。而JDK1.8中,HashMap采用位数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减小了查找时间。
当链表数组的容量超过初始容量*加载因子(默认0.75)时,再散列将链表数组扩大2倍,把原链表数组的搬移到新的数组中。为何须要使用加载因子?为何须要扩容呢?由于若是填充比很大,说明利用的空间不少,若是一直不进行扩容的话,链表就会愈来愈长,这样查找的效率很低,扩容以后,将原来链表数组的每个链表分红奇偶两个子链表分别挂在新链表数组的散列位置,这样就减小了每一个链表的长度,增长查找效率。
HashMap是非线程安全的,HashTable、ConcurrentHashMap是线程安全的。 HashMap的键和值都容许有null存在,而HashTable、ConcurrentHashMap则都不行。 由于线程安全、哈希效率的问题,HashMap效率比HashTable、ConcurrentHashMap的都要高。 HashTable里使用的是synchronized关键字,这实际上是对对象加锁,锁住的都是对象总体,当Hashtable的大小增长到必定的时候,性能会急剧降低,由于迭代时须要被锁定很长的时间。 ConcurrentHashMap引入了分割(Segment),能够理解为把一个大的Map拆分红N个小的HashTable,在put方法中,会根据hash(paramK.hashCode())来决定具体存放进哪一个Segment,若是查看Segment的put操做,咱们会发现内部使用的同步机制是基于lock操做的,这样就能够对Map的一部分(Segment)进行上锁,这样影响的只是将要放入同一个Segment的元素的put操做,保证同步的时候,锁住的不是整个Map(HashTable就是这么作的),相对于HashTable提升了多线程环境下的性能,所以HashTable已经被淘汰了。
Java中HashMap底层实现原理(JDK1.8)源码分析
Fail-Fast是Java集合的一种错误检测机制。当遍历集合的同时修改集合或者多个线程对集合进行结构上的改变的操做时,有可能会产生fail-fast机制,记住是有可能,而不是必定。其实就是抛出ConcurrentModificationException 异常。
集合的迭代器在调用next()、remove()方法时都会调用checkForComodification()方法,该方法主要就是检测modCount == expectedModCount ? 若不等则抛出ConcurrentModificationException 异常,从而产生fail-fast机制。modCount是在每次改变集合数量时会改变的值。
Java程序中wait 和 sleep都会形成某种形式的暂停,它们能够知足不一样的须要。wait()方法用于线程间通讯,若是等待条件为真且其它线程被唤醒时它会释放锁,而 sleep()方法仅仅释放CPU资源或者让当前线程中止执行一段时间,但不会释放锁。
Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终须要转化为汇编指令在CPU上执行。 volatile是轻量级的synchronized(volatile不会引发线程上下文的切换和调度),它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另一个线程能读到这个修改的值。
因为内存访问速度远不及CPU处理速度,为了提升处理速度,处理器不直接和内存进行通讯,而是先将系统内存的数据读到内部缓存后在进行操做,但操做完不知道什么时候会写到内存。普通共享变量被修改以后,何时被写入主存是不肯定的,当其余线程去读取时,此时内存中可能仍是原来的旧值,所以没法保证可见性。若是对声明了volatile的变量进行写操做,JVM就会想处理器发送一条Lock前缀的指令,表示将当前处理器缓存行的数据写回到系统内存。
不安全。volatile只能保证可见性,并不能保证原子性。i++实际上会被分红多步完成:1)获取i的值;2)执行i+1;3)将结果赋值给i。volatile只能保证这3步不被重排序,多线程状况下,可能两个线程同时获取i,执行i+1,而后都赋值结果2,实际上应该进行两次+1操做。
可使用java.util.concurrent.atomic包下的原子类,如AtomicInteger。
其实现原理是采用CAS自旋操做更新值。CAS即compare and swap的缩写,中文翻译成比较并交换。CAS有3个操做数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改成B,不然什么都不作。自旋就是不断尝试CAS操做直到成功为止。
Java中每一个对象均可以做为锁:
当一个线程试图访问同步代码块时,它首先必须获得锁,退出或抛出异常时必须释放锁。synchronized用的锁是存在Java对象头里的MarkWord,一般是32bit或者64bit,其中最后2bit表示锁标志位
Java SE1.6为了减小得到锁和释放锁带来的性能消耗,引入了偏向锁和轻量级锁,在1.6中锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几种状态会随着竞争状况逐渐升级。锁能够升级但不能降级。
偏向锁获取过程:
好处:1)下降资源消耗;2)提升相应速度;3)提升线程的可管理性。 线程池的实现原理:
这题考的实际上是多线程同步的问题。这种状况能够可使用thread.join();join方法会阻塞直到thread线程终止才返回。更复杂一点的状况也可使用CountDownLatch,CountDownLatch的构造接收一个int参数做为计数器,每次调用countDown方法计数器减一。作数据处理的线程调用await方法阻塞直到计数器为0时。
interrupted() 和 isInterrupted()的主要区别是前者会将中断状态清除然后者不会。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来 检查中断状态时,中断状态会被清零。而非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛 出InterruptedException异常的方法都会将中断状态清零。不管如何,一个线程的中断状态有有可能被其它线程调用中断来改变。
同步的懒加载虽然是线程安全的,可是致使性能开销。所以产生了双重检查锁定。但双重检查锁定存在隐藏的问题。instance = new Instance()
实际上会分为三步操做:1)分配对象的内存空间;2)初始化对象;3)设置instance指向刚分配的内存地址;因为指令重排序,2和3的顺序并不肯定。在多线程的状况下,第一个线程执行了1,3,此时第二个线程判断instance不为null,但实际上操做2尚未执行,第二个线程就会得到一个还未初始化的对象,直接使用就会形成空指针。
解决方案是用volatile修饰instance,在JDK 1.5增强了volatile的语意以后,用volatile修饰instance就阻止了2和3的重排序,进而避免上述状况的发生。
另外一种方式则是使用静态内部类:
public class Singleton {
private static class InstanceHolder {
public static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return InstanceHolder.instance;
}
}
复制代码
其原理是利用类初始化时会加上初始化锁确保类对象的惟一性。
ThreadLocal即线程变量,它为每一个使用该变量的线程提供独立的变量副本,因此每个线程均可以独立地改变本身的副本,而不会影响其它线程所对应的副本。从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。ThreadLocal的实现是以ThreadLocal对象为键。任意对象为值得存储结构。这个结构被附带在线程上,也就是说一个线程能够根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。
数据竞争的定义:在一个线程写一个变量,在另外一个线程读同一个变量,并且写和读没有经过同步来排序。
JM屏蔽各类硬件和操做系统的内存访问差别,以实现让Java程序在各类平台下都能达到一致的内存访问效果。
线程之间的共享变量存储在主内存中,每一个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本。本地内存是一个抽象概念,它涵盖了缓存、写缓存区、寄存器以及其余的硬件和编译器优化。 在执行程序时,为了提升性能,编译器和处理器经常会对指令作重排序。在多线程中重排序会对程序的执行结果有影响。
JSR-133内存模型采用happens-before的概念来阐述操做之间的内存可见性。happens-before会限制重排序以知足规则。 主要的happens-before规则有以下: