站点图标 起风网

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

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

安卓部分

一,view的事件分发机制

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

image
image

二,Handler原理机制

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

image

Handler铁三角

详解单个handler原理

image

图解多个handler原理

image

Handler的创建流程

四,mvc,mvp,mvvm

三个架构模式:

Mvc和mvp的最主要区别:Mvc中model可以直接和view交互mvp中model   与view   的交互由presenter完成
image
image

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

五,系统启动流程

image

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/.MainActivityThisTime: 83TotalTime: 83WaitTime: 94TotalTime是我们真正的启动时间
2,通过收集log(可以用来获取线上启动时间)

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

app的启动优化:

基于上面的启动流程我们尽量做到如下几点

app启动遇见黑屏或者白屏问题
<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的生命周期

七,listview优化

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

八,RecycleView

基本解决了ListView的一些缺陷,比如:需要手动重写ViewHolder,对于item的动画操作也需要很复杂的逻辑去实现等等
但是也有些缺点
不能简单的加头和尾

  1. 不能简单的设置子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代码具体执行过程如下图

image

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

image
image
方法区有以下特点 1,方法区是线程安全的。2、方法区的大小不必是固定的,JVM可根据应用需要动态调整。3、方法区也可被垃圾收集,当某个类不在被使用(不可触及)时,JVM将卸载这个类,进行垃圾收集

堆和栈的主要区别:

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

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

线程和jvm

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

image

5,JVM执行程序的过程

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

6,jvm的生命周期

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

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

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

image

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

image

十二,Dalvik,ART和jvm

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创建线程
代码演示    //创建一个线程池对象,控制要创建几个线程对象        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。所以说,在具体使用时要根据适当情况选择

        嵌套的代码体现        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同步
进程间通信的四种方式

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

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

if (!bmp.isRecycle()) {    bmp.recycle();   //回收图片所占的内存    bitmap = null;    system.gc();  //提醒系统及时回收}

虽然调用recycle()并不能保证立即释放占用的内存,但是可以加速Bitmap的内存的释放。
释放内存以后,就不能再使用该Bitmap对象了,如果再次使用,就会抛出异常。所以一定要保证不再使用的时候释放。比如,如果是在某个Activity中使用Bitmap,就可以在Activity的onStop()或者onDestroy()方法中进行回收

文章转载于:https://www.jianshu.com/p/f7a30554fe55

原著是一个有趣的人,若有侵权,请通知删除

退出移动版