阿里百度腾讯常见java和安卓面试题汇总

安卓部分

一,view的事件分发机制

dispatchTouchEvent 分发事件 onInterceptTouchEvent 拦截事件只有viewgroup才有,view和activity没 onTouchEvent 处理点击事件java

  • 1,图解ACTION_DOWN 事件分发,若是面试的时候能把下面的图画出来。能增分很多 dispatchTouchEvent和 onTouchEvent的框里有个【true---->消费】的字,表示的意思是若是方法返回true,那么表明事件就此消费,不会继续往别的地方传了,事件终止

  • 2,红色的箭头表明ACTION_DOWN 事件的流向 蓝色的箭头表明ACTION_MOVE 和 ACTION_UP 事件的流向

二,Handler原理机制

Handler主要负责发送和接受消息,Looper负责不断轮询MessageQueue,有新的消息就交给Handler处理,若是轮询不到新的消息,那就自身就处于阻塞状态。 Handler简单图解linux

Handler铁三角

  • Handler android的消息机制就是指Handler机制,Handler机制的运行须要MeeageQueue和Looper的辅助。 λ MessageQueue:消息队列,用于将全部收到的消息以队列的形式进行排列, 并提供入队和出队的方法。在looper的构造函数中建立,所以一个Looper也就对应了一个MessageQueue. 经过enqueueMessage实现消息的入队,经过next方法实现出队
  • Looper 轮询器 做用:与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个- MessageQueue。Looper 经过loop()方法调用messagequeue的next方法,不断从 MessageQueue中去取消息

详解单个handler原理android

图解多个handler原理

Handler的建立流程

  • 一、首先Looper.prepare()在本线程中保存一个Looper实例,而后该实例中保存一个MessageQueue对象;由于Looper.prepare()在一个线程中只能调用一次,因此MessageQueue在一个线程中只会存在一个。
  • 二、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,而后回调msg.target.dispatchMessage(msg)方法。
  • 三、Handler的构造方法,会首先获得当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
  • 四、Handler的sendMessage方法,会给msg的target赋值为handler自身,而后加入MessageQueue中。
  • 五、在构造Handler实例时,咱们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。 好了,总结完成,你们可能还会问,那么在Activity中,咱们并无显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler能够成功建立呢,这是由于在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。

四,mvc,mvp,mvvm

三个架构模式:面试

  • MVC:Model-View-Controller,经典模式,很容易理解,主要缺点有两个: 1,View对Model的依赖,会致使View也包含了业务逻辑; 2,Controller会变得很厚很复杂。
  • MVP:Model-View-Presenter,MVC的一个演变模式,将Controller换成了Presenter, 主要为了解决上述第一个缺点,将View和Model解耦, 不过第二个缺点依然没有解决。
  • MVVM:Model-View-ViewModel,是对MVP的一个优化模式, 采用了双向绑定:View的变更,自动反映在ViewModel,反之亦然。 MVC, MVP, MMVM用来解决业务逻辑和视图之间的耦合
Mvc和mvp的最主要区别:
Mvc中model能够直接和view交互
mvp中model	与view	的交互由presenter完成
复制代码

Mvc,mvp,mvvm的优缺点

  • 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,调用复杂度增长数组

一,LRUCache原理和部分源码解析

图片的三级缓存:也就是加载图片的时候首先从内存缓存中取,若是没有再从文件缓存中取, 若是文件缓存没有取到,就从网络下载图片而且加入内存和文件缓存 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详解

LinkedHashMap底层数据结构由链表和哈希表组成。由链表保证元素有序。由哈希表保证元素惟一

LinkedHashMap的两种排序

1,按插入顺序排序(默认) 2,访问顺序排序(lru是这种排序) LinkedHashMap重写了父类HashMap的get方法,实际在调用父类getEntry()方法取得查找的元素后,再判断当排序模式accessOrder为true时,记录访问顺序,将最新访问的元素添加到双向链表的表头,并从原来的位置删除。因为的链表的增长、删除操做是常量级的,故并不会带来性能的损失

三,HashMap和ConcurrentHashMap的原理

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而言的)。

五,系统启动流程

Zygote进程 –> SystemServer进程 –> 各类系统服务 –> 应用进程

当全部的服务都启动完毕后,SystemServer会打印出“Making services ready”,而后经过ActivityManager启动Home界面,并发送“ACTION_BOOT_COMPLETED”广播消息

六,App启动、Application生命周期

app的两种启动方式

1,冷启动

系统会从新建立一个新的进程分配给它,因此会先建立和初始化Application类,再建立和初始化MainActivity类(包括一系列的测量、布局、绘制),最后显示在界面上

2,热启动

进程在后台运行,因此热启动就不会走Application这步了,而是直接走MainActivity(包括一系列的测量、布局、绘制),因此热启动的过程只须要建立和初始化一个MainActivity就好了,而没必要建立和初始化Application,由于一个应用重新进程的建立到进程的销毁,Application只会初始化一次。

App启动流程

用户点击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启动时长的两种方式

1,本地经过adb命令行

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是咱们真正的启动时间
复制代码
2,经过收集log(能够用来获取线上启动时间)

开始时间: 冷启动在Application的attachBaseContext 热启动在入口activity的onRestart中 结束时间:在入口activity的onWindowFocusChanged

app的启动优化:

基于上面的启动流程咱们尽可能作到以下几点

  • 1.Application的建立过程当中尽可能少的进行耗时操做
  • 2.若是用到SharePreference,尽可能在异步线程中操做
  • 3.减小布局的层次,而且生命周期回调的方法中尽可能减小耗时的操做
app启动碰见黑屏或者白屏问题
  • 1,产生缘由  其实显示黑屏或者白屏实属正常,这是由于还没加载到布局文件,就已经显示了window窗口背景,黑屏白屏就是window窗口背景
  • 2,解决办法 经过设置设置Style  (1)设置背景图Theme  经过设置一张背景图。 当程序启动时,首先显示这张背景图,避免出现黑屏
<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和Process进程

当application在Linux平台开启时,系统会给这个application建立一个进程(process)来运行同时分配内存资源给该application,当程序结束运行时,该进程结束系统回收内存资源。 一个Application就是一个应用,在应用启动时,android会启动一个linux进程和一个主线程,这个进程以应用的包名命名,在默认的状况下,这个Application下的Activity、service、provider、receiver等组件都在这个进程中运行

Application的生命周期

  • 一、onCreate() 在建立应用程序时建立
  • 二、onTerminate() 在模拟环境下执行。当终止应用程序对象时调用,不保证必定被调用,当程序是被内核终止以便为其余应用程序释放资源,那么将不会提醒,而且不调用应用程序的对象的onTerminate方法而直接终止进程。
  • 三、onLowMemory() 低内存时执行。好的应用程序通常会在这个方法里面释放一些没必要要的资源来应付当后台程序已经终止,前台应用程序内存还不够时的状况。 四、onConfigurationChanged(Configuration newConfig) 配置改变时触发这个方法。 五、onTrimMemory(int level)程序在进行内存清理时执行​

七,listview优化

1,convertView复用
2,viewholder使用
3,图片优化
4,getView()中不要写过多的逻辑代码,不要建立不少的对象,逻辑代码放在别的地方,
5,item的布局减小层级
6,经过getItemViewType实现复杂布局的复用
7,简单布局能够将listview的scrollingCache和animateCache属性设置false。
若是设置为true会提升显示效果,可是须要消耗更多内存和更长的初始化时间
复制代码

八,RecycleView

基本解决了ListView的一些缺陷,好比:须要手动重写ViewHolder,对于item的动画操做也须要很复杂的逻辑去实现等等 可是也有些缺点 不能简单的加头和尾  2. 不能简单的设置子item的点击事件 因此对于一些简单的列表仍是能够用listview的。Recylerview能够用于实现一些复杂的列表。

优化 1,简化item布局 2,图片优化

九,JVM内存模型

1,什么是jvm

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机。 JVM在执行字节码时,实际上最终仍是把字节码解释成具体平台上的机器指令执行

2,JDK,JRE,JVM的做用及关系

(1)做用
	JVM:保证Java语言跨平台
	JRE:Java程序的运行环境
	JDK:Java程序的开发环境
(2)关系
	JDK:JRE+工具
	JRE:JVM+类库
简单而言:使用JDK开发完成的Java程序,交给JRE去运行,由JVM来保证跨平台。
复制代码

3,java代码具体执行过程以下图

4,jvm内存结构图(即运行时数据区)

  • 程序计数器:是一块较小的内存空间,能够看做是当前线程所执行的字节码的 行号指示器。为了可以使得每一个线程都在线程切换后可以恢复在切换以前 的程序执行位置,每一个线程都须要有本身独立的程序计数器,而且不能互相 被干扰,独立存储。 若是正在执行的是一个java方法,记录的是正在执行的虚拟机字节码指令的地址 若是正在执行的是个native方法,这个计数器的值为空。
  • 虚拟机栈:为虚拟机执行java方法(字节码)服务。 这里咱们用的比较多的是局部变量表 局部变量表所需的内存空间在编译期间完成分配。当进入一个方法时,这个方法须要在帧中分配多大的局部变量空间是彻底肯定的。在方法运行期间不会改变局部变量表的大小。
  • 本地方法栈:为虚拟机执行本地方法服务。 本地方法栈与虚拟机栈发挥的做用很是类似。区别以下 虚拟机栈:为虚拟机执行java方法(字节码)服务 本地方法栈:为虚拟机使用到的native方法服务
  • 堆:是用来存储对象自己的以及数组(数组引用是存放在Java栈中的)。堆是被全部 线程共享的,在JVM中只有一个堆。是java虚拟机管理的内存最大的一块。 在虚拟机启动时建立,
  • 方法区:和堆同样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息, 常量,静态变量和即时编译器编译后的代码数据等。这个区域的内存回收主要是针对 常量池的回收和对类型的卸载。
方法区有如下特色 
1,方法区是线程安全的。
二、方法区的大小没必要是固定的,JVM可根据应用须要动态调整。
三、方法区也可被垃圾收集,当某个类不在被使用(不可触及)时,JVM将卸载这个类,进行垃圾收集
复制代码

堆和栈的主要区别:

栈--主要存放引用和基本数据类型。

堆--用来存放 new 出来的对象实例和数组。

线程和jvm

每一个方法被执行的时候都会同时建立一个栈帧(Stack Frame)用于存储局部变量表、操做栈、动态连接、方法出口等信息。 每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过

5,JVM执行程序的过程

  1. 加载.class文件
  2. 管理并分配内存
  3. 执行垃圾收集

6,jvm的生命周期

  • a) 启动。启动一个Java程序时,一个JVM实例就产生了,任何一个拥有public static void main(String[] args)函数的class均可以做为JVM实例运行的起点
  • b) 运行。main()做为该程序初始线程的起点,任何其余线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程一般由JVM本身使用,java程序也能够代表本身建立的线程是守护线程
  • c) 消亡。当程序中的全部非守护线程都终止时,JVM才退出;若安全管理器容许,程序也可使用Runtime类或者System.exit()来退出

十,GC的回收策略(垃圾回收策略机制)

垃圾回收:主要是对gc堆即Young Generation(年轻代)块和Old Generation(年老代)块内存进行回收,YG用来放新产生的对象,通过几回回收还没回收掉的对象往OG中移动,对YG进行垃圾回收又叫作MinorGC,对 OG垃圾回收又叫MajorGC,两块内存回收互不干涉

更细的划分为Eden和2个survivor space(即幸存区)

  • 1.eden空间: 大部分新对象在这里分配,有些大对象直接在年老区分配,这个区域进行回收后不少状况下会变空。
    1. two survivor 空间:即from survivor和to survivor。当eden区满时会将仍然存活的对象复制至其中一个survivor区,当这个区又满时将复制至另一个survivor区,以下图:

十一,画出 Android 的大致架构图

十二,Dalvik,ART和jvm

Dalvik是Google公司本身设计用于Android平台的Java虚拟机

Dalvik和jvm的区别 Dalvik是基于寄存器的,运行dex文件 JVM是基于栈的,运行java字节码

Art:空间换时间 art模式须要在程序安装时进行预编译,将apk编译解析成机器码,运行速度相比dalvik模式快。 缺点:安装时间稍长,因为进行了预编译,因此会产生机器码,会占用存储空间。

十三,阿里面试手写单例模式

通常状况下直接使用饿汉式就行了,若是明确要求要懒加载(lazy initialization)会倾向于使用静态内部类,若是涉及到反序列化建立对象时会试着使用枚举的方式来实现单例。

  • 1,饿汉式 会在加载类后一开始就被初始化,即便客户端没有调用 getInstance()方法。饿汉式的建立方式在一些场景中将没法使用:譬如 Singleton 实例的建立是依赖参数或者配置文件的
Public class Singleton{
Private static final Singleton instance=new Singleton();
Private Singleton(){}
Public static Singleton getInstance(){
Return instance;
}
}
复制代码
  • 2,静态内部类 这种写法仍然使用JVM自己机制保证了线程安全问题;因为 SingletonHolder 是私有的,除了 getInstance() 以外没有办法访问它,所以它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本
Public class Singleton{
Private static class SingletonHolder{
Private static final Singleton INSTANCE=new Singleton();
}
Private Singleton(){}
Public static final Singleton getInstance(){
Return SingletonHolder.INSTANCE;
}
}
复制代码
  • 3,枚举Enum 咱们能够经过EasySingleton.INSTANCE来访问实例,这比调用getInstance()方法简单多了。建立枚举默认就是线程安全的,因此不须要担忧double checked locking,并且还能防止反序列化致使从新建立新的对象
Public enum Singleton{
INSTANCE;
}
复制代码

十四,安卓源码中用到的设计模式

  • 1,单例模式 保证在应用程序中,一个类Class只有一个实例存在 软键盘管理的 InputMethodManager和安卓系统级别的服务一般用的单例模 式,如WindowsManagerService、ActivityManagerService,更经常使用的是一个 LayoutInflater的类
  • 2,责任链模式---View事件分发机制 它使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的 耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象 处理它为止 具体体如今View的onTouchEvent方法中返回值的设置, 若是返回false,那么意味着当前的View不会是该次的责任人,将不会对其 持有;若是返回true,此时View会持有该事件并再也不向外传递
  • 3,适配器模式----listview的BaseAdapter 将一个类的接口转换成客户但愿的另一个接口 以笔记本电源适配器为例,电源适配器将220V的电压转换到5v。
  • 4,观察者模式---将被观察者和观察者解耦 观察者模式是一种行为类模式,它定义对象间一种一对多的依赖关系,使得每当一个对 象改变状态,则全部依赖于它的对象都会获得通知并被自动更新 DataSetObserver就是一个观察者,它一旦发现BaseAdapter内部数据有变量,就会经过 回调方法DataSetObserver.onChanged和DataSetObserver.onInvalidated来通知 DataSetObserver的实现类。 好比安卓开源项目EventBus和rxjava的核心就是观察者模式
  • 5,工厂模式----BitmapFactory 静态工厂方法在Android中比较明显的例子应该就是BitmapFactory了,经过各类decodeXXX()就能够从不一样渠道得到Bitmap对象
  • 6,建造者模式----AlertDialog.Builder 若是一个类的构造须要不少参数,并且这些参数并不都是必须的,那么这种状况下就比较适合Builder模式。好比构建一个AlertDialog,标题、内容、取消按钮、肯定按钮、中立按钮,你可能只须要单独设置几个属性便可
  • 7,策略模式----动画的插值器 策略模式就至关于一个影碟机,你往里面插什么碟子,就能放出什么电影 以动画为例,设置不一样的插值器对象,就能够获得不一样的变化曲线

十五,多线程相关

  • 1,进程和线程的区别 进程:表示一个运行的程序 线程:进程(程序)的执行单元,执行路径

多进程的意义? 提升CPU的使用率 多线程的意义? 提升应用程序的使用率

  • 2,多线程的三种实现方式
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();
复制代码
  • 5,synchronized与Lock的区别 synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率 Lock和synchronized有如下几点不一样:
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的底层实现

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服务

十八,即时通信和推送的3种实现方案

  • 1,经过轮询:太low,还消耗性能,消耗流量
  • 2,长链接:这个方案能够解决由轮询带来的性能问题,可是仍是会消耗手机的电池 而且咱们本地的服务很容易被安卓系统在低内存时杀死。长链接须要依赖心跳机制
  • 3,采用MQTT协议实现Android推送:耗电量小 MQTT协议较之XMPP更为轻量级,其链接的创建与传输的开销都很是小,很是精简,很是适合大量节点在弱网络环境的场景,发布/订阅的模式也比较易于扩展 XMPP是PC时代的产物,其底层通信的数据格式的XML,数据冗余性过高(约70%),比较耗流量,而且在复杂的移动网络环境下会遇到各类各样的问题。

十九,Bitmap相关

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占用内存最多

  • 2,使用Bitmap时的一些注意事项 不用的Bitmap及时释放
if (!bmp.isRecycle()) {
    bmp.recycle();   //回收图片所占的内存
    bitmap = null;
    system.gc();  //提醒系统及时回收
}
复制代码

虽然调用recycle()并不能保证当即释放占用的内存,可是能够加速Bitmap的内存的释放。 释放内存之后,就不能再使用该Bitmap对象了,若是再次使用,就会抛出异常。因此必定要保证再也不使用的时候释放。好比,若是是在某个Activity中使用Bitmap,就能够在Activity的onStop()或者onDestroy()方法中进行回收

  • 3,圆形bitmap的实现原理 主要靠paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));这行代码实现圆形图片 SRC_IN这种模式,取两个绘制的效果叠加后的交集,第一个绘制的是个圆形,第二个绘制的是个Bitmap,因而交集为圆形,展示的是BItmap,就实现了圆形图片效果
相关文章
相关标签/搜索