dispatchTouchEvent 分发事件 onInterceptTouchEvent 拦截事件只有viewgroup才有,view和activity没 onTouchEvent 处理点击事件java
Handler主要负责发送和接受消息,Looper负责不断轮询MessageQueue,有新的消息就交给Handler处理,若是轮询不到新的消息,那就自身就处于阻塞状态。 Handler简单图解linux
详解单个handler原理android
三个架构模式:面试
Mvc和mvp的最主要区别:
Mvc中model能够直接和view交互
mvp中model 与view 的交互由presenter完成
复制代码
mvc是指用户触发事件的时候,view层会发送指令到controller层,而后controller去通知model层更新数据,model层更新完数据后会直接在view层显示结果。 对android来讲 activity几乎承担了view层和controller层两种角色,而且和model层耦合严重,在逻辑复杂的界面维护起来很麻烦。算法
mvp模式下的activity只承担了view层的角色,controller的角色彻底由presenter负责,view层和presenter层的通讯经过接口实现,因此VP之间不存在耦合问题,view层与model也是彻底解耦了。 presenter复用度高,能够随意搬到任何界面。 mvp模式下还方便测试维护: 可是mvp的问题在于view层和presenter层是经过接口链接,在复杂的界面中,维护过多接口的成本很大。shell
Mvvm 的优势: 1,和mvp同样,方便进行单元测试 2,便于代码的移植设计模式
Mvvm的缺点 1,类会增多 2,viewModel会愈来愈庞大 3,调用复杂度增长数组
图片的三级缓存:也就是加载图片的时候首先从内存缓存中取,若是没有再从文件缓存中取, 若是文件缓存没有取到,就从网络下载图片而且加入内存和文件缓存 LRU:Least Recently Used,最近最少使用算法。当内存缓存达到设定的最大值 时将内存缓存中近期最少使用的对象移除,有效的避免了OOM的出现。缓存
LruCache中Lru算法:经过LinkedHashMap来实现的。LinkedHashMap继承于HashMap,它使用了一个双向链表来存储Map中的Entry顺序关系,这种顺序有两种, 一种是LRU顺序, 一种是插入顺序, 这能够由其构造函数安全
public LinkedHashMap(int initialCapacity,float loadFactor, boolean accessOrder)指定
复制代码
因此,对于get、put、remove等操做,LinkedHashMap除了要作HashMap作的事情,还作些调整Entry顺序链表的工做。 LruCache中将LinkedHashMap的顺序设置为LRU顺序来实现LRU缓存,每次调用get(也就是从内存缓存中取图片),则将该对象移到链表的尾端。调用put插入新的对象也是存储在链表尾端,这样当内存缓存达到设定的最大值时,将链表头部的对象(近期最少用到的)移除
部分源码解析:LruCache的初始化构造方法
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
//初始容量为零,0.75是加载因子,
表示容量达到最大容量的75%的时候会
把内存增长一半。最后这个参数相当重要
表示访问元素的排序方式,true表示按照
访问顺序排序,false表示按照插入的顺序排序
}
复制代码
LinkedHashMap底层数据结构由链表和哈希表组成。由链表保证元素有序。由哈希表保证元素惟一
1,按插入顺序排序(默认) 2,访问顺序排序(lru是这种排序) LinkedHashMap重写了父类HashMap的get方法,实际在调用父类getEntry()方法取得查找的元素后,再判断当排序模式accessOrder为true时,记录访问顺序,将最新访问的元素添加到双向链表的表头,并从原来的位置删除。因为的链表的增长、删除操做是常量级的,故并不会带来性能的损失
1,原理 HashMap的原理:hashmap本质数组加链表。根据key取得hash值,而后计算出数组下标,若是多个key对应到同一个下标,就用链表串起来,新插入的在前面 ConcurrentHashMap原理:在hashMap的基础上,ConcurrentHashMap将数据分为多个segment(相似HashTable),默认16个(concurrency level),而后每次操做对一个segment加锁,避免多线程锁得概率,提升并发效率
2,Hashmap的源码: 构造函数,空参或者单参时都会调用两个参数的
public HashMap(int initialCapacity, float loadFactor) {
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
init();
}
复制代码
有3个关键参数: capacity:容量,就是数组大小 loadFactor:比例,用于扩容 threshold:=capacity*loadFactor 最多容纳的Entry数,若是当前元素个数多于这个就要 扩容(capacity扩大为原来的2倍)
Get方法:根据key算hash值,再根据hash值取得数组下标,经过数组下标取出链表,遍历链表用equals取出对应key的value
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
复制代码
3,HashMap和ConcurrentHashMap,hashtable的区别
HashMap:线程不安全,效率高
ConcurrentHashMap:线程安全,效率高,默认提高16倍
Hashtable:线程安全,效率低
复制代码
四,SparseArray原理 SparseArray是android里为<Interger,Object>这样的Hashmap而专门写的类,目的是提升内存效率,其核心是折半查找函数(binarySearch)。注意内存二字很重要,由于它仅仅提升内存效率,而不是提升执行效率 它要比 HashMap 节省内存,结构比HashMap简单(SparseArray内部主要使用两个一维数组来保存数据,一个用来存key,一个用来存value)不须要额外的数据结构(主要是针对HashMap中的HashMapEntry而言的)。
当全部的服务都启动完毕后,SystemServer会打印出“Making services ready”,而后经过ActivityManager启动Home界面,并发送“ACTION_BOOT_COMPLETED”广播消息
app的两种启动方式
系统会从新建立一个新的进程分配给它,因此会先建立和初始化Application类,再建立和初始化MainActivity类(包括一系列的测量、布局、绘制),最后显示在界面上
进程在后台运行,因此热启动就不会走Application这步了,而是直接走MainActivity(包括一系列的测量、布局、绘制),因此热启动的过程只须要建立和初始化一个MainActivity就好了,而没必要建立和初始化Application,由于一个应用重新进程的建立到进程的销毁,Application只会初始化一次。
用户点击app后会通知 ActivityManagerService 启动应用的入口 Activity, ActivityManagerService 发现这个应用还未启动,则会通知 Zygote 进程孵化出应用进程,而后在这个应用进程里执行 ActivityThread 的 main 方法。应用进程接下来通知 ActivityManagerService 应用进程已启动,ActivityManagerService 保存应用进程的一个代理对象,这样 ActivityManagerService 能够经过这个代理对象控制应用进程,而后 ActivityManagerService 通知应用进程建立入口 Activity 的实例,并执行它的生命周期函数。
上面的启动流程是 Android 提供的机制,咱们只能在建立入口 Activity 的实例这里作文章,正常Main Activity 的启动流程:
-> Application 构造函数
-> Application.attachBaseContext()
-> Application.onCreate()
-> Activity 构造函数
-> Activity.setTheme()
-> Activity.onCreate()
-> Activity.onStart
-> Activity.onResume
-> Activity.onAttachedToWindow
-> Activity.onWindowFocusChanged
复制代码
统计app启动时长的两种方式
adb shell am start -w packagename/activity 输入adb shell am start -W
com.qcl/com.qcl.MainActivity获得下面结果
Activity: com.qcl/.MainActivity
ThisTime: 83
TotalTime: 83
WaitTime: 94
TotalTime是咱们真正的启动时间
复制代码
开始时间: 冷启动在Application的attachBaseContext 热启动在入口activity的onRestart中 结束时间:在入口activity的onWindowFocusChanged
基于上面的启动流程咱们尽可能作到以下几点
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:screenOrientation">portrait</item>
<item name="android:windowBackground">>@mipmap/splash</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
</style>
复制代码
设置透明Theme 经过把样式设置为透明,程序启动后不会黑屏而是整个透明了,等到界面初始化完才一次性显示出来
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:screenOrientation">portrait</item>
</style>
复制代码
Theme1 程序启动快,界面先显示背景图,而后再刷新其余界面控件。给人刷新不一样步感受。 Theme2 给人程序启动慢感受,界面一次性刷出来,刷新同步
当application在Linux平台开启时,系统会给这个application建立一个进程(process)来运行同时分配内存资源给该application,当程序结束运行时,该进程结束系统回收内存资源。 一个Application就是一个应用,在应用启动时,android会启动一个linux进程和一个主线程,这个进程以应用的包名命名,在默认的状况下,这个Application下的Activity、service、provider、receiver等组件都在这个进程中运行
1,convertView复用
2,viewholder使用
3,图片优化
4,getView()中不要写过多的逻辑代码,不要建立不少的对象,逻辑代码放在别的地方,
5,item的布局减小层级
6,经过getItemViewType实现复杂布局的复用
7,简单布局能够将listview的scrollingCache和animateCache属性设置false。
若是设置为true会提升显示效果,可是须要消耗更多内存和更长的初始化时间
复制代码
基本解决了ListView的一些缺陷,好比:须要手动重写ViewHolder,对于item的动画操做也须要很复杂的逻辑去实现等等 可是也有些缺点 不能简单的加头和尾 2. 不能简单的设置子item的点击事件 因此对于一些简单的列表仍是能够用listview的。Recylerview能够用于实现一些复杂的列表。
优化 1,简化item布局 2,图片优化
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机。 JVM在执行字节码时,实际上最终仍是把字节码解释成具体平台上的机器指令执行
(1)做用
JVM:保证Java语言跨平台
JRE:Java程序的运行环境
JDK:Java程序的开发环境
(2)关系
JDK:JRE+工具
JRE:JVM+类库
简单而言:使用JDK开发完成的Java程序,交给JRE去运行,由JVM来保证跨平台。
复制代码
方法区有如下特色
1,方法区是线程安全的。
二、方法区的大小没必要是固定的,JVM可根据应用须要动态调整。
三、方法区也可被垃圾收集,当某个类不在被使用(不可触及)时,JVM将卸载这个类,进行垃圾收集
复制代码
栈--主要存放引用和基本数据类型。
堆--用来存放 new 出来的对象实例和数组。
每一个方法被执行的时候都会同时建立一个栈帧(Stack Frame)用于存储局部变量表、操做栈、动态连接、方法出口等信息。 每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过
垃圾回收:主要是对gc堆即Young Generation(年轻代)块和Old Generation(年老代)块内存进行回收,YG用来放新产生的对象,通过几回回收还没回收掉的对象往OG中移动,对YG进行垃圾回收又叫作MinorGC,对 OG垃圾回收又叫MajorGC,两块内存回收互不干涉
更细的划分为Eden和2个survivor space(即幸存区)
Dalvik是Google公司本身设计用于Android平台的Java虚拟机
Dalvik和jvm的区别 Dalvik是基于寄存器的,运行dex文件 JVM是基于栈的,运行java字节码
Art:空间换时间 art模式须要在程序安装时进行预编译,将apk编译解析成机器码,运行速度相比dalvik模式快。 缺点:安装时间稍长,因为进行了预编译,因此会产生机器码,会占用存储空间。
通常状况下直接使用饿汉式就行了,若是明确要求要懒加载(lazy initialization)会倾向于使用静态内部类,若是涉及到反序列化建立对象时会试着使用枚举的方式来实现单例。
Public class Singleton{
Private static final Singleton instance=new Singleton();
Private Singleton(){}
Public static Singleton getInstance(){
Return instance;
}
}
复制代码
Public class Singleton{
Private static class SingletonHolder{
Private static final Singleton INSTANCE=new Singleton();
}
Private Singleton(){}
Public static final Singleton getInstance(){
Return SingletonHolder.INSTANCE;
}
}
复制代码
Public enum Singleton{
INSTANCE;
}
复制代码
多进程的意义? 提升CPU的使用率 多线程的意义? 提升应用程序的使用率
1,继承Thread类,
2,实现Runnable接口(推荐,方便的实现资源的共享)
3,经过Callable和Future建立线程
复制代码
3,start和run方法的区别 start会先启动线程,再由jvm调用run方法 run方法只是thread的一个普通方法调用,仍是在主线程里执行。
4,线程池 程序启动一个新线程成本比较高,由于它涉及到要与操做系统进行交互,而使用线程池能够很好的提升性能,尤为是当程序要建立大量生存期很短的线程时,更应该使用线程池。 线程池里的每个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。 JKD5以前,咱们手动实现本身的线程池,JDK5之后,java内置支持线程池。
代码演示 //建立一个线程池对象,控制要建立几个线程对象
ExecutorService pool=Executors.newFixedThreadPool(2);
//能够执行Runnable对象或者Callable对象的线程
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
//结束线程池
pool.shutdown();
复制代码
1)Lock是一个接口,jdk5后出现,而synchronized是Java中的关键字,synchronized是内置的语言实现;
2)synchronized在发生异常时,会自动释放线程占有的锁,所以不会致使死锁现象发生;而Lock在发生异常时,若是没有主动经过unLock()去释放锁,则极可能形成死锁现象,所以使用Lock时须要在finally块中释放锁;
3)Lock可让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不可以响应中断;
4)经过Lock能够知道有没有成功获取锁,而synchronized却没法办到。
5)Lock能够提升多个线程进行读操做的效率。
复制代码
在性能上来讲,若是竞争资源不激烈,二者的性能是差很少的,而当竞争资源很是激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。因此说,在具体使用时要根据适当状况选择
6,volatile关键字 volatile关键字修饰变量,用来确保将变量的更新操做通知到其余线程 在访问volatile变量时不会执行加锁操做,所以也就不会使执行线程阻塞,所以volatile变量是一种比sychronized关键字更轻量级的同步 一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰以后,那么就具有了两层语义: 1)保证了不一样线程对这个变量进行操做时的可见性,即一个线程修改了某个变量的值,这新值对其余线程来讲是当即可见的。 2)禁止进行指令重排序
7,ThreadLocal ThreadLocal类的做用:是为每一个线程都建立一个变量副本, 每一个线程均可以修改本身所拥有的变量副本, 而不会影响其余线程的副本. 其实这也是解决线程安全的问题的一种方法。在不少状况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性 ThreadLocal原理:在ThreadLocal类中有一个Map,用于存储每个线程的变量副本。
8,死锁 死锁就是指两个或者两个以上的线程在执行过程当中,因争夺资源产生的一种相互等待现象。(好比两我的吃饭,一人一根筷子)
嵌套的代码体现
if(x>20){
synchronized(a){
synchronized(b){...}
}
}else{
synchronized(b){
synchronized(a){...}
}
}
复制代码
AsyncTask是对Handler与线程池的封装,AsyncTask的本质是一个线程池,全部提交的异步任务都会在这个线程池中的工做线程内执行,当工做线程须要跟UI线程交互时,工做线程会经过向在UI线程建立的Handler传递消息的方式,调用相关的回调函数,从而实现UI界面的更新
1,SP是进程同步的吗?有什么方法作到同步? Android自己的SP能够支持多进程,可是SP不能保证多进程间同步。由于当多个进程同时而又高频的调用SP的commit方法时,就会致使文件被反复覆盖写入,而并无被及时读取,因此形成进程间数据的不一样步 Android的ContentProvider是支持进程同步的,能够利用ContentProvider进行进程的sp同步 进程间通讯的四种方式
1,Activity Activity的跨进程访问与进程内访问略有不一样。虽然它们都须要Intent对象,但跨进程访问并不须要指定Context对象和Activity的 Class对象,而须要指定的是要访问的Activity所对应的Action(一个字符串)。有些Activity还须要指定一个Uri(经过 Intent构造方法的第2个参数指定)。 在android系统中有不少应用程序提供了能够跨进程访问的Activity,例如,下面的代码能够直接调用拨打电话的Activity。 Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678" ); startActivity(callIntent);
2,Content Provider 能够跨进程访问其余应用程序中的数据(以Cursor对象形式返回),固然,也能够对其余应用程序的数据进行增、删、改操 做
3,广播 能够向android系统中全部应用程序发送广播,而须要跨进程通信的应用程序能够监听这些广播
4,AIDL Service和Content Provider相似,也能够访问其余应用程序中的数据,但不一样的是,Content Provider返回的是Cursor对象,而Service返回的是Java对象,这种能够跨进程通信的服务叫AIDL服务
1,android 色彩模式说明:
ALPHA_8: 每一个像素占用1byte内存。
ARGB_4444: 每一个像素占用2byte内存
ARGB_8888: 每一个像素占用4byte内存
RGB_565: 每一个像素占用2byte内存
复制代码
假设一张10241024,模式为ARGB_8888的图片,那么它占有的内存就是:10241024*4 = 4MB Android默认的色彩模式为ARGB_8888,一般咱们优化Bitmap时,当须要作性能优化或者防止OOM(Out Of Memory),咱们一般会使用Bitmap.Config.RGB_565这个配置,由于Bitmap.Config.ALPHA_8只有透明度,显示通常图片没有意义,Bitmap.Config.ARGB_4444显示图片不清楚,Bitmap.Config.ARGB_8888占用内存最多
if (!bmp.isRecycle()) {
bmp.recycle(); //回收图片所占的内存
bitmap = null;
system.gc(); //提醒系统及时回收
}
复制代码
虽然调用recycle()并不能保证当即释放占用的内存,可是能够加速Bitmap的内存的释放。 释放内存之后,就不能再使用该Bitmap对象了,若是再次使用,就会抛出异常。因此必定要保证再也不使用的时候释放。好比,若是是在某个Activity中使用Bitmap,就能够在Activity的onStop()或者onDestroy()方法中进行回收