近四十场面试汇聚成的超全Android常见面试题总结

本文整理了身边朋友近四十场的面试问题,并在文章中加以自己的理解以及看见的部分精彩评论,所有文章并非自己原创

面试题:

一、谈谈自定义LayoutManager的流程?

  • 确定Itemview的LayoutParamsgenerateDefaultLayoutParams
  • 确定所有itemview在recyclerview的位置,并且回收和复 用itemviewonLayoutChildren
  • 添加滑动canScrollVertically

二、什么是 RemoteViews?使用场景有哪些?

RemoteViews翻译过来就是远程视图.顾名思义,RemoteViews不是当前进程的View,是属于SystemServer进程.应用程序与RemoteViews之间依赖Binder实现了进程间通信.

用法:通常是在通知栏

//1.创建RemoteViews实例
RemoteViews mRemoteViews=new
RemoteViews("com.example.remoteviewdemo", R.layout.remoteview_layout);
//2.构建一个打开Activity的PendingIntent Intent
intent=new
Intent(MainActivity.this,MainActivity.class);
PendingIntent
mPendingIntent=PendingIntent.getActivity(Main
Activity.this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
//3.创建一个Notification
mNotification = new
Notification.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(mPendingIntent)
.setContent(mRemoteViews)
.build();
//4. 获 取 NotificationManager
manager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE
);
Button button1 = (Button)
findViewById(R.id.button1);
button1.setOnClickListener(new
OnClickListener() {
@Override
public void onClick(View v) {
//弹出通知
manager.notify(1, mNotification);
}
});

三、谈一谈获取View宽高的几种方法?

  • OnGlobalLayoutListener获取
  • OnPreDrawListener获取
  • OnLayoutChangeListener获取
  • 重写View的onSizeChanged()
  • 使用View.post()方法

四、谈一谈插值器和估值器?

  • 插值器,根据时间(动画时常)流逝的百分比来计算属
    性变化的百分比。系统默认的有匀速,加减速,减速插值器。
  • 估值器,通过上面插值器得到的百分比计算出具体变化
    的值。系统默认的有整型,浮点型,颜色估值器
  • 自定义只需要重写他们的evaluate方法就可以了。

五、getDimension、getDimensionPixelOffset 和getDimensionPixelSize 三者的区别?

相同点:单位为dp/sp时,都会乘以density,单位为px则不乘

不同点:

  • getDimension返回的是float值
  • getDimensionPixelSize,返回的是int值,float转成int时,四舍五入
  • getDimensionPixelOffset,返回的是int值,float转int时,向下取整(即忽略小数值)

六、关于LayoutInflater,它是如何通过inflate 方法获取到具体View的?

系统通过LayoutInflater.from创建出布局构造器,inflate 方法中,最后会掉用createViewFromTag这里他会去判断两个参数factory2 和factory 如果都会空就会系统自己去创建view, 并且通过一个xml解析器,获取标签名字,然后判断是<Button还是xxx.xxx.xxView.然后走createView通过拼接得到全类名路径,反射创建出类。

七、谈谈RecyclerView的缓存机制?

scrap viewCacherecyclerPool:
scrap 是当前展示的缓存, 在onlayout时候 缓存viewCache 是屏幕外看不见的缓存, 可以吧viewCache设置大点, 空间换时间 避免段距离内快速滑动卡顿以上两种缓存是不走 createView和 onbind recyclerPool 比较特殊他是会走 onbind的,他可以被多个recyclerView共享内部的item,实际用途是:多个recyclerView之间共享item,应用在 垂直recyclerView 内嵌水平recyclerView,或者viewpager中多个recyclerView

八、请谈谈View.inflate和LayoutInflater.inflate的区别?

实际上没有区别

  • View.inflate实际上是对LayoutInflater.inflate做了一层包装,在功能上,LayoutInflate功能更加强大。
  • View.inflate实际上最终调用的还是LayoutInflater.inflate(@LayoutRes int resource,@nullable ViewGroup root)三个参数的方法,这里如果传入的root如果不为空,那么解析出来的View会被添加到这个ViewGroup当中去。
  • 而LayoutInflater.inflate方法则可以指定当前View是否需要添加到ViewGroup中去

九、请谈谈invalidate()和postInvalidate() 方法的区别和应用场景?

  • invalidate()用来重绘UI,需要在UI线程调用。
  • postInvalidate()也是用来重新绘制UI,它可以在UI线程调 用,也可以在子线程中调用,postInvalidate()方法内部通过Handler发送了一个消息将线程切回到UI线程通知重新绘制,并不是说postInvalidate()可以在子线程更新UI,本质上还是在UI线程发生重绘,只不过我们使用postInvalidate()它内部会帮我们切换线程

十、谈一谈SurfaceView与TextureView的使用场景和用法?

  • 频繁绘制和对帧率要求比较高的需求,比如拍照、视频和游戏等
  • SurfaceView有独立的绘图表面,可以在子线程中进行绘制,
    缺点是不能够执行平移、缩放、旋转、透明渐变操作,TextureView的出现就是为了解决这些问题
  • SurfaceView的使用方法,大概是获取SurfaceHolder 对象,监听surface创建,更新,销毁,创新一个新的线程,并在其中绘制并提交
  • TextureView并没有独立的绘图表面,在使用过程中, 需要添加监听surfaceTexture是否可用,再做相应的处理

十一、谈一谈RecyclerView.Adapter的几种刷新方式有何不同?

刷新全部可见的item,notifyDataSetChanged() 刷新指定item,notifyItemChanged(int)
从指定位置开始刷新指定个item,notifyItemRangeChanged(int,int)插入、移动一个并自动刷新,notifyItemInserted(int)、notifyItemMoved(int)、notifyItemRemoved(int) 局部刷新,notifyItemChanged(int, Object)

十二、有了解过WindowInsets吗?它有哪些应用?

ViewRootImpl在performTraversals时会调dispatchApplyInsets,内调DecorViewdispatchApplyWindowInsets,进行WindowInsets的分发。

十三、谈一谈屏幕刷新机制?

屏幕刷新频率和绘制频率cpu负责 measurelayoutdraw =>displayListgpu 负责 display => 位图每个16ms会发送一次垂直同步信号, vsync每次信号发送的时候都会从gpu的buffer中取出渲染好的位图显示在屏幕上,同时如果有需要 还会进行下一次的 cpu计算,计算好后放入buffer中,如果计算时间超过了两次vsync之间的时间 ,即16ms ,则vsync信号会把 上一次gpu buffer中的信息展示出来,这时候就是卡顿,另外如果页面没有变化 屏幕还是一样会去buffer中取出上一次的刷新,只不过cpu不再去计算而已

十四、Java 中使用多线程的方式有哪些?

大概这么4种:
extends Thread;implRunnable;implCallable通过 FutureTask包装器来创建线程;使用ExecutorService、Callable、Future实现有返回结果的多线程。;extends Thread 和 implRunnable 的线程没有返回值, 而FutureTask ExecutorService(ExecutorService.submit(xxx) return Future<?> )有返回值

十五、说一下线程的几种状态?

第一是创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。

第二是就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。

第三是运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。

第四是阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞。

第五是死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪。

十六、如何实现多线程中的同步?

多线程同步和异步不是一回事。几种情况,
1.就是大家说的synchronized 他可以保证原子性,保证多个线程在操作同一方法时只有一个线程可以持有锁,并且操作该方法,
2.就是手动调用读写锁
3.手动操作线程的wait和notify
4.volatile我记得是没有原子性的,他可以保证内存可见性,在多线程的情况下保证每个线程的数据都是最新的

十七、死锁的定义

多线程以及多进程改善了系统资源的利用率并提高了系统的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

十八、死锁产生的原因

  1. 系统资源的竞争2. 进程推进顺序非3.信号量使用不当也会造成死锁。

十九、死锁产生的必要条件

互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某 资源仅为一个进程所占有。
此时若有其他进程请求该资源,则请求进程只能等待。

不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。

请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。

二十、硬件的效率问题

计算机处理器处理绝大多数运行任务都不可能只靠处理器“计算”就能完成,处理器至少需要与内存交互,如读取运算数据、存储运算结果,这个I/O操作很难消除(无法仅靠寄存器完成所有运算任务)。
由于计算机的存储设备与处理器的运算速度有几个数量级的差距,为了避免处理器等待缓慢的内存读写操作完成,现
代计算机系统通过加入一层读写速度尽可能接近处理器运算速度的高速缓存。缓存作为内存和处理器之间的缓冲:将
运算需要使用到的数据复制到缓存中,让运算能快速运行,当运算结束后再从缓存同步回内存之中。

二十一、缓存一致性问题

基于高速缓存的存储系统交互很好地解决了处理器与内存速度的矛盾,但是也为计算机系统带来更高的复杂度,因为
引入了一个新问题:缓存一致性。
在多处理器的系统中(或者单处理器多核的系统),每个处理器(每个核)都有自己的高速缓存,而它们有共享同一主内存(Main Memory)。当多个处理器的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致。 为此,需要各个处理器访问缓存时都遵循一些协议,在读写时要根据协议进行操作,来维护缓存的一致性。

二十二、代码乱序执行优化问题

为了使得处理器内部的运算单元尽量被充分利用,提高运算效率,处理器可能会对输入的代码进行乱序执行,处理器
会在计算之后将乱序执行的结果重组,乱序优化可以保证在单线程下该执行结果与顺序执行的结果是一致的,但不保
证程序中各个语句计算的先后顺序与输入代码中的顺序一致。

二十三、Java内存模型的组成

主内存 :Java内存模型规定了所有变量都存储在主内存(Main Memory)中(此处的主内存与介绍物理硬件的主内存名字一样,两者可以互相类比,但此处仅是虚拟机内存的一部分)。

工作内存: 每条线程都有自己的工作内存(Working Memory,又称本地内存,可与前面介绍的处理器高速缓存类比),线程的工作内存中保存了该线程使用到的变量的主内存中的共享变量的副本拷贝。工作内存是 JMM 的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

二十四、JVM内存操作的并发问题

1、 工作内存数据一致性 各个线程操作数据时会保存使用到的主内存中的共享变量副本,当多个线程的运算任务都涉及同一个共享变量时,将导致各自的的共享变量副本不一致,如果真的发生这种情况,数据同步回主内存以谁的副本数据为准? Java内存模型主要通过一系列的数据同步协议、规则来保证数据的一致性,后面再详细介绍。

2 、指令重排序优化 Java中重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段。重排序分为两类:编译期重排序和运行期重排序,分别对应编译时和运行时环境。 同样的,指令重排序不是随意重排序,它需要满足以下两个条件:
1)在单线程环境下不能改变程序运行的结果 即时编译器(和处理器)需要保证程序能够遵守 as-if-serial 属性。通俗地说,就是在单线程情况下,要给程序一个顺序执行的假象。即经过重排序的执行结果要与顺序执行的结果保持一致。
2)存在数据依赖关系的不允许重排序,多线程环境下,如果线程处理逻辑之间存在依赖关系,有可能因为指令重排序导致运行结果与预期不同,后面再展开Java内存模型如何解决这种情况。

二十五、说下Activity生命周期 ?

在正常情况下,Activity的常用生命周期就只有如下7个

  • onCreate():表示Activity正在被创建,常用来初始化工作,比如调用setContentView加载界面布局资源,初始化Activity所需数据等;
  • onRestart():表示Activity正在重新启动,一般情况下,当前Acitivty从不可见重新变为可见时,OnRestart就会被调用;
  • onStart():表示Activity正在被启动,此时Activity可见但不在前台,还处于后台,无法与用户交互;
  • onResume():表示Activity获得焦点,此时Activity可见且在前台并开始活动,这是与onStart的区别所在;
  • onPause():表示Activity正在停止,此时可做一些存储数据、停止动画等工作,但是不能太耗时,因为这会影响到新Activity的显示,onPause必须先执行完,新Activity的onResume才会执行;
  • onStop():表示Activity即将停止,可以做一些稍微重量级的回收工作,比如注销广播接收器、关闭网络连接等,同样不能太耗时;
  • onDestroy():表示Activity即将被销毁,这是Activity生命周期中的最后一个回调,常做回收工作、资源释放;

二十六、Activity A 启动另一个Activity B 会调用哪些方法?如果B是透明主题的又或则是个DialogActivity呢 ?

Activity A 启动另一个Activity B,回调如下
Activity A 的onPause() → Activity B的onCreate() → onStart() → onResume() → Activity A的onStop();
如果B是透明主题又或则是个DialogActivity,则不会回调A的onStop;

二十七、说下onSaveInstanceState()方法的作用 ? 何时会被调用?

发生条件:异常情况下(系统配置发生改变时导致Activity被杀死并重新创建、资源内存不足导致低优先级的Activity被杀死)

  • 系统会调用onSaveInstanceState来保存当前Activity的状态,此方法调用在onStop之前,与onPause没有既定的时序关系;
  • 当Activity被重建后,系统会调用onRestoreInstanceState,并且把onSave(简称)方法所保存的Bundle对象同时传参给onRestore(简称)和onCreate(),因此可以通过这两个方法判断Activity
    是否被重建,调用在onStart之后;

二十八、说下 Activity的四种启动模式、应用场景 ?

standard标准模式:每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存
在,此模式的Activity默认会进入启动它的Activity所属的任务栈中;
singleTop栈顶复用模式:如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时会回调onNewIntent方法,如果新Activity实例已经存在但不在栈顶,那么Activity依然会被重新创建;

singleTask栈内复用模式:只要Activity在一个任务栈中存在,那么多次启动此Activity都不会重新创
建实例,并回调onNewIntent方法,此模式启动Activity A,系统首先会寻找是否存在A想要的任务
栈,如果不存在,就会重新创建一个任务栈,然后把创建好A的实例放到栈中;
singleInstance单实例模式:这是一种加强的singleTask模式,具有此种模式的Activity只能单独地位
于一个任务栈中,且此任务栈中只有唯一一个实例;

二十九、了解哪些Activity常用的标记位Flags?

  • FLAG_ACTIVITY_NEW_TASK : 对应singleTask启动模式,其效果和在XML中指定该启动模式相同;
  • FLAG_ACTIVITY_SINGLE_TOP : 对应singleTop启动模式,其效果和在XML中指定该启动模式相同;
  • FLAG_ACTIVITY_CLEAR_TOP : 具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。这个标记位一般会和singleTask模式一起出现,在这种情况下,被启动
    Activity的实例如果已经存在,那么系统就会回调onNewIntent。如果被启动的Activity采用standard
    模式启动,那么它以及连同它之上的Activity都要出栈,系统会创建新的Activity实例并放入栈中;
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS : 具有这个标记的 Activity 不会出现在历史 Activity 列
    表中;

三十、如何启动其他应用的Activity?

在保证有权限访问的情况下,通过隐式Intent进行目标Activity的IntentFilter匹配,原则是:
一个intent只有同时匹配某个Activity的intent-filter中的action、category、data才算完全匹配,才能启动该Activity;
一个Activity可以有多个 intent-filter,一个 intent只要成功匹配任意一组 intent-filter,就可以启动该Activity;

三十一、谈一谈Fragment的生命周期?

Fragment从创建到销毁整个生命周期中涉及到的方法依次为:onAttach()→onCreate()→onCreateView()→onActivityCreated()→onStart()→onResume()→onPause()→onStop()→onDestroyView()→onDestroy()→onDetach(),其中和Activity有不少名称相同作用相似的方法,而不同的方法
有:

  • onAttach():当Fragment和Activity建立关联时调用;
  • onCreateView():当fragment创建视图调用,在onCreate之后;
  • onActivityCreated():当与Fragment相关联的Activity完成onCreate()之后调用;
  • onDestroyView():在Fragment中的布局被移除时调用;
  • onDetach():当Fragment和Activity解除关联时调用;

三十二、谈谈Activity和Fragment的区别?

相似点:都可包含布局、可有自己的生命周期

不同点:

  • Fragment相比较于Activity多出4个回调周期,在控制操作上更灵活;
  • Fragment可以在XML文件中直接进行写入,也可以在Activity中动态添加;
  • Fragment可以使用show()/hide()或者replace()随时对Fragment进行切换,并且切换的时候不会出现明显的效果,用户体验会好;Activity虽然也可以进行切换,但是Activity之间切换会有明显的翻页或者其他的效果,在小部分内容的切换上给用户的感觉不是很好;

三十三、Fragment中add与replace的区别(Fragment重叠)

  • add不会重新初始化fragment,replace每次都会。所以如果在fragment生命周期内获取获取数据,使用replace会重复获取;
  • 添加相同的fragment时,replace不会有任何变化,add会报IllegalStateException异常;
  • replace先remove掉相同id的所有fragment,然后在add当前的这个fragment,而add是覆盖前一个fragment。所以如果使用add一般会伴随hide()和show(),避免布局重叠;
  • 使用add,如果应用放在后台,或以其他方式被系统销毁,再打开时,hide()中引用的fragment会销
    毁,所以依然会出现布局重叠bug,可以使用replace或使用add时,添加一个tag参数;

三十四、getFragmentManager、getSupportFragmentManager 、getChildFragmentManager之间的区别?

  • getFragmentManager()所得到的是所在fragment 的父容器的管理器, getChildFragmentManager()所得到的是在fragment 里面子容器的管理器, 如果是fragment嵌套fragment,那么就需要利用getChildFragmentManager();
  • 因为Fragment是3.0 Android系统API版本才出现的组件,所以3.0以上系统可以直接调用getFragmentManager()来获取FragmentManager()对象,而3.0以下则需要调用getSupportFragmentManager() 来间接获取;

三十五、FragmentPagerAdapter与FragmentStatePagerAdapter的区别与使用场景

相同点 :二者都继承PagerAdapter

不同点 :FragmentPagerAdapter的每个Fragment会持久的保存在FragmentManager中,只要用户可以返回到页面中,它都不会被销毁。因此适用于那些数据相对静态的页,Fragment数量也比较少的那种; FragmentStatePagerAdapter只保留当前页面,当页面不可见时,该Fragment就会被消除,释放其资源。因此适用于那些数据动态性较大、占用内存较多,多Fragment的情况;

后记

整理这篇面试题花了我不少精力,把题刷一遍以后再将面试题抄一遍的做法看上去显得有一些笨拙。而为了看上去不是这么笨拙,我对作者提到的问题专门去思考了自己平时看见的问题以及查阅相关的资料,并将这些资料放在了相对应的面试题下面。整理本文的初衷是为了应付面试官,但是越看越觉得自己欠缺的越多,因此不管是作者分享的面试经验还是作者分享的技术细节对自己都是如此弥足珍贵!这个过程就好像当初我们上学一样,一开始为了应付老师和家长的要求,但是随着自己逐渐长大才发现这个过程自己的收获远远大于应付老师和家长的要求。

最后,篇幅原因就展现到此了,努力将面试题中提到的短板补足,也希望各位看到这里的看官都能找到一份满意的工作或者在文章中找到了自己想要的东西!还需要了解更多Android面试题答案解析的可以文末卡片领取

整理不易,您的点赞,在看,转载,星标是对我最大的鼓励!祝愿大家都可以拿到满意的offer!

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值