Android 面试题总结(一)

时间:2021-07-13 13:53:53

前言

  • 这里不断总结在面试中问到的一些问题,作为以后面试复习的资料.在此记录也是为了方便各位读者.

Java

1.设计模式和其UML类图:比较常用的
- 单例http://blog.csdn.net/qq_28690547/article/details/50479241
- Builderhttp://blog.csdn.net/qq_28690547/article/details/50493099
- 工厂方法http://blog.csdn.net/qq_28690547/article/details/50660376
- 原型http://blog.csdn.net/qq_28690547/article/details/50626237
- 策略http://blog.csdn.net/qq_28690547/article/details/50683310
- 状态http://blog.csdn.net/qq_28690547/article/details/50706448
- 观察者http://blog.csdn.net/qq_28690547/article/details/50661603
- 命令http://blog.csdn.net/qq_28690547/article/details/50708061
- 模板方法http://blog.csdn.net/qq_28690547/article/details/50708547
- 访问者http://blog.csdn.net/qq_28690547/article/details/50710061
- 代理http://blog.csdn.net/qq_28690547/article/details/50734045
- 中介者http://blog.csdn.net/qq_28690547/article/details/50710622

2.Volatile关键字:http://www.infoq.com/cn/articles/java-memory-model-4
对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
volatile变量的写-读可以实现线程之间的通信.
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存。
当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
下面对volatile写和volatile读的内存语义做个总结:
线程A写一个volatile变量,实质上是线程A向接下来将要读这个volatile变量的某个线程发出了(其对共享变量所在修改的)消息。
线程B读一个volatile变量,实质上是线程B接收了之前某个线程发出的(在写这个volatile变量之前对共享变量所做修改的)消息。
线程A写一个volatile变量,随后线程B读这个volatile变量,这个过程实质上是线程A通过主内存向线程B发送消息。

3.Thread的sleep(),wait(),yield()notify(),notifyAll()等方法:
sleep():使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步块,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
yield():该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会.
wait():该方法使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中.
notify():当调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志,如果锁标志等待池中没有线程,则notify()不起作用.
notifyAll():该方法则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中.
synchronized:该关键字用于保护共享数据,当然前提条件是要分清哪些数据是共享数据.每个对象都有一个锁标志,当一个线程访问到该对象,被Synchronized修饰的数据将被”上锁”,阻止其他线程访问.当前线程访问完这部分数据后释放锁标志,其他线程就可以访问了.

Android

3.AsyncTask:
AsyncTask首次引入时,异步任务是在一个独立的线程中顺序执行,一直只能执行一个任务,不能并行得执行.
1.6-2.3的版本,AsyncTask引入了线程池,支持同时执行5个异步任务,也就是说同时只能由5个线程运行,超过的线程等待.
3.0以后的版本,每次只启动一个线程执行一个任务,完成之后再执行第二个任务,也就是相当于只有一个后台线程在执行所提交的所有任务.

1.生命周期:
AsyncTask不会随着activity的生命周期终止而终止,activity生命结束,AsyncTask会一直执行到doInBackground()方法执行完毕.在activity销毁前,必须取消AsyncTask通过cancle()这个方法.但有一种情况是,在doInBackground()如果执行了BitmapFactory.decodeStream()这个方法,那么这个操作会继续下去.
2.内存泄露:
如果AsyncTask被声明为activity的非静态的内部类,那么AsyncTask会保留一个对activity的引用,如果activity已经被销毁,AsyncTask的后台还在执行,它将继续在内存保留这个引用,导致activity无法被回收,从而内存泄露
3.结果丢失:
屏幕旋转或者其他操作导致activity被重建,之前运行的AsyncTask会持有之前activity的引用,因此更新界面将不再生效.
4.并行和串行的问题:
1.6以前,AsyncTask是串行的
1.6-2.3,AsyncTask是并行的
2.3以后,AsyncTask中调用execute()是串行执行,executeOnExecytor(Executor)是并行执行.
5.缺点:AsyncTask不能灵活控制其内部线程池.

4.activity,fragment,service的生命周期:
常见例子:程序运行过程中被其他应用终止,调用onPause-onStop,恢复onStart-onPause;如果打断这个应用程序的Theme为Translucent或者dialog的activity那么只是onPause,恢复的时候onResume;
onCreate:创建界面的时候,做一些数据的初始化工作
onStart:到这一步变成用户可见不可交互的
onResume:变成和用户可交互的
onPause:到这一步是可见但不可交互,系统会停止动画等消耗CPU的事情,这个方法要做的事情很短,因为下一个activity不会等到这个方法完成在启动.
onStop:变得不可见,被下一个activity覆盖了
onDestory:
启动另一个activity然后finish当前的activity,先调用旧activity的onPause方法,然后调用新的activity的onCreate-onStop-onResume方法,然后调用旧activity的onStop-onDestory方法
如果旧activity没有被finish掉,那么在onStop之前 还会调用onSaveInstanceState的方法;

fragment的生命周期:
onAttach-onCreate-onCreateView-onActivityCreated-onStart-onResume
onPause-onStop-onDestoryView-onDestory-onDetach

5.四大启动模式:
Android 面试题总结(一)

5.1**onNewIntent():**
大家遇到一个应用的Activity供多种方式调用启动的情况,多个调用希望只有一个Activity的实例存在,这就需要Activity的onNewIntent(Intent intent)方法了。只要在Activity中加入自己的onNewIntent(intent)的实现加上Manifest中对Activity设置lanuchMode=“singleTask”or “singleTop”就可以。
onNewIntent()非常好用,Activity第一启动的时候执行onCreate()—->onStart()—->onResume()等后续生命周期函数,也就时说第一次启动Activity并不会执行到onNewIntent(). 而后面如果再有想启动Activity的时候,那就是执行onNewIntent()—->onResart()——>onStart()—–>onResume().

5.2 activity启动方式和如何关闭activity:
activity启动分为显示启动隐式启动.
显示启动:
1.构造方法传入Component,最常用的方式.例如:Intent intent = new Intent(this, SecondActivity.class);
2.setComponent方法,例如:ComponentName componentName = new ComponentName(this, SecondActivity.class);
3.setClass/setClassName方法.例如Intent intent = new Intent(); intent.setClass(this, SecondActivity.class);
隐式启动:
即不是像显式的那样直接指定需要调用的Activity,隐式不明确指定启动哪个Activity,而是设置Action、Data、Category,让系统来筛选出合适的Activity。筛选是根据所有的来筛选。
activity的关闭的几种方法:
1. finish();结束当前Activity,不会立即释放内存。遵循android内存管理机制。
2. killProcess;例如:android.os.Process.killProcess(android.os.Process.myPid()); 结束当前组件如Activity,并立即释放当前Activity所占资源。
3. exit;System.exit(),0表示正常退出,1表示非正常 ;结束当前组件如Activity,并立即释放当前Activity所占资源。
4. restartPackage()注:只能2.2以下的版本使用:ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.restartPackage(getPackageName());
结束整个App,包括service等其它Activity组件。
除了finish()方法可以调用Activity的生命周期方法如onStop()、onDestroy(),其余三种退出App均不会调用Activity的生命周期方法。

6.onSaveInstanceState()和onRestoryInstanceState():
当应用遇到意外情况(例如:内存不足,用户直接按home键,屏幕方向改变,设备语言设定,键盘弹出)由系统销毁一个activity时,onSaveInstanceState()这个方法就会被调用,用来保存一些临时性的状态.然后该状态可以在onCreate()或者onRestoryInstanceState()中恢复.

7.android 5.0特性:http://blog.csdn.net/qq_28690547/article/details/50975739

9.MVP架构:http://blog.csdn.net/qq_28690547/article/details/50711786
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示.作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。
view层不再和model层存在耦合。MVP主要解决就是把逻辑层抽出来成P层,要是遇到需求逻辑上的更改就可以只需要修改P层了或者遇到逻辑上的大概我们可以直接从写一个P也可以,我们会经常把所有的东西都写在了Activity/Fragment里面这样一来遇到频繁改需求或者逻辑越来越复杂的时候,Activity /Fragment里面就会出现过多的混杂逻辑导致出错,所以MVP模式对于APP来对控制逻辑和UI的解耦来说是一个不错的选择!

10.ART和Dalvik区别:
ART上应用启动快\运行快,但是消耗更多存储空间,安装时间长,总的来说ART的功效就是空间换时间.
Dalvik是Google公司自己设计用于Android平台的Java虚拟机.Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一.它可以支持已转换的.dex格式的java应用程序的运行,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统.Dalvik经过优化,允许在有限的内存中同事运行多个虚拟机的实例,并且每一个Dalvik应用作为一个独立的Linux进程执行.独立的进程可以防止在虚拟机崩溃的时候所有程序都关闭.
ART代表android runtime,其处理应用程序的执行方式完全不同于Dalvik,Dalvik是依靠一个Just-In-Time编译器去解释字节码.开发者编译后的应用需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行.ART则完全改变了这套做法,在应用安装时就预编译字节码到机器语言,这一机制教Ahead-Of-Time编译.在移除解释代码这一过程后,应用程序执行将更有效率,启动更快
ART优点:
1.性能提升显著.
2.应用启动更快\运行更快\体验更流畅\触感反馈更及时.
3.更长的电池续航能力
4.支持更低的硬件
ART缺点:
1.更大储存空间占用,可能会增加10%-20%
2.更长应用安装时间

11.Java运行时机制JVM和android运行时机制:
JVM运行机制:
1.将Java代码编译成字节码
2.类加载,这个工作由类加载器完成,这个实际上就是把.class文件从一盘读到内存里.
3.类执行.
android运行时机制:
见第10.

12.如何避免ANR:
1.UI线程尽量只做跟UI相关的工作.
2.耗时工作把它放入单独的线程处理.
3.尽量用Handler来处理UIthread和逼得thread之间的交互.

13.状态模式和策略模式的比较:
两者虽然UML类图相类似,但是两种完全不同的思想.对状态进行建模时,状态迁移是一个核心内容,然而,在选择策略时,迁移与此毫无关系,用户关系的不同策略不同的输出结果.
1.策略模式是一组方案,他们可以相互替换选择一个策略,获得策略的输出.策略模式用于随不同外部环境采取不同行为的场合.
2.状态模式处理的核心问题是状态的迁移,因为在对象存在很多状态的情况下,各个状态之间跳转和迁移的过程都是非常复杂的.
在状态模式中,状态的变迁是由对象的内部条件决定,外界只需关心其接口,不必关心其状态对象的创建和转化;而策略模式里,采取何种策略由外部条件(C)决定

14.Android消息处理机制(Handler、Looper、MessageQueue与Message):http://blog.csdn.net/bboyfeiyu/article/details/38555547
1.消息的表示:Message
2.消息队列:MessageQueue,MessageQueue是Looper内部使用的
3.消息循环,用于循环去除消息进行处理:Looper
4.消息处理,消息循环从消息队列中取出消息后要对消息进行处理:Handler
Android 面试题总结(一)
在应用启动时,会开启一个主线程(UI线程),并且启动消息循环,应用不停地从该消息队列中取出、处理消息达到程序运行的效果。Looper对象封装了消息队列,Looper对象是ThreadLocal的,不同线程之间的Looper对象不能共享与访问。而Handler通过与Looper对象绑定来实现与执行线程的绑定,handler会把Runnable(包装成Message)或者Message对象追加到与线程关联的消息队列中,然后在消息循环中挨个取出消息,并且处理消息。当Handler绑定的Looper是主线程的Looper,则该Handler可以在handleMessage中更新UI,否则更新UI则会抛出异常!
其实我们可以把Handler、Looper、Thread想象成一个生产线,工人(搬运工)相当于Handler,负责将货物搬到传输带上(Handler将消息传递给消息队列);传送带扮演消息队列的角色,负责传递货物,货物会被挨取出,并且输送到目的地 ( target来处理 );而货物到达某个车间后再被工人处理,车间就扮演了Thread这个角色,每个车间有自己独立的传送带,车间A的货物不能被车间B的拿到,即相当于ThreadLocal( 车间独有 )。

15.ContentProvider和ContentResolver:

一个应用实现ContentProvider来提供内容给别的应用来操作,
一个应用通过ContentResolver来操作别的应用数据,当然在自己的应用中也可以。

16.Android内存基础:
stack栈内存和Heap堆内存.stack是存放对象的引用,heap里存放实际对象数据.
内存泄露(Memory Leak):
Java内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。
内存溢出OOM:
当应用程序申请的java heap空间超过Dalvik VM HeapGrowthLimit时,溢出。
android系统默认内存回收机制:
1.**进程优先级:**Foreground进程\Visible进程\Service进程\Background进程\Empty进程.
2.ActivityManagerService:直接管理所有进程的内存资源分配。所有进程要申请或释放内存都需要通过ActivityManagerService对象。
3.垃圾回收不定期执行。当内存不够时就会遍历heap空间,把垃圾对象删除。
4.堆内存越大,则GC的时间更长.
对象引用类型:
1.强引用 strong :当内存不足时,java虚拟机宁愿跑出OOM内存溢出异常,也不会轻易回收强引用对象来解决内存不足问题
2.软引用 soft:只有当内存达到某个阀值时才回去回收,常用语缓存.
3.弱引用 weak:只要被GC线程扫描到了就进行回收.
4.虚引用
如果想要避免OOM发生,则使用软引用对象,即当内存快不足时进行回收;如果想尽快回收某些占用内存较大的对象,例如bitmap,可以使用弱引用,能被快速回收.不过如果要对bitmap做缓存就不要使用弱引用,因为很快就会被GC回收,导致缓存失败

17.Parcelable:
Parcelable,定义了将数据写入Parcel,和从Parcel中读出的接口.一个实体(用类来表示),如果需要封装到消息中去,就必须实现这一接口,实现了这一接口,该实体就成为”可打包的”了.
Parcelable传递对象:
1.实现Serializable接口,实现Serializable接口是JavaSE本身就支持的.
2.实现Parcelable接口,Parcelable是Android特有的功能,效率比实现Serializable接口高,像用于Intent数据传递也都支持,而且还可以用在进程间通信(IPC);
除了基本类型外,只有实现了Parcelable接口的类才能被放入Parcel中.

18.Activity之间数据传递的方式:
1.通过Intent传递数据:通过Parcelable和Serializable传递,Parcelable比Serializable效率更高
2.通过Application:全局Application保存数据,使用时一定要做好非空判断,如果数据为空,可以考虑逻辑上让应用直接返回到最初的activity.
3.使用单例:
4.静态成员变量.(可以考虑WeakReferences):跟单例类似,如果数据为空,可以考虑逻辑上让应用直接返回到最初的activity
如果数据很大很多,比如bitmap,处理不当是很容易导致内存泄露或者内存溢出的。
所以可以考虑使用WeakReferences 将数据包装起来。
5.持久化(sqlite,share preference ,file等)
组件之间通过bundle传递大数据时候的解决方案:
1.可以将需要传递的数据卸载临时文件或者数据库
2.可以将需要传递的数据信息封装在一个静态的类中也就是上第4中方案,并且为了防止内存泄露或者内存溢出,可以选择用WeakPeferences将数据包装起来.

19.WP,IOS,Android的区别:
WindowsPhone:因为它时基于MVVM的编程模型,它把业务逻辑ViewModel和页面View彻底分开.同时View中的每个控件的状态,都与ViewModel中的属性进行了绑定,这样的话,View中控件状态的变化,ViewModel中的属性也会相应变化.就可重现View被销毁之前的状态–我们称之为墓碑机制.

20.Protobuffer和json的对比:
Protobuffer的这种协议是二进制格式的,在表示大数据时,控件比Json小很多.地址http://cxshun.iteye.com/blog/1974498这里写链接内容
8.RecyclerView:

  • 与ListView的区别:

    (1)**Item复用方面:**RecyclerView内置了RecyclerViewPool,多级缓存,ViewHolder.而AdapterView需要手动去添加ViewHolder且复用功能也没RecyclerView更加完善.
    (2)**样式丰富方面:**RecyclerView通过支持水平,垂直和表格列表及其他更复杂形式,而AdapterView只支持具体某一种.
    (3)**效果增强方面:**RecyclerView内置了ItemDecoration和ItemAnimator,可以自定义item的分割线和item项数据变化的动画效果,而用AdapterView实现时采取的做法是将这些特殊UI作为itemView的一部分,设置可见不可见决定是否展现,而数据变化时的动画效果也没有提供,实现起来比较麻烦.
    (4)**代码内聚方面:**RecyclerView将功能密切相关的类写成内部类,如ViewHolder,Adapter.而AdapterView没有.

  • 原理解析:
    (1)RecycledViewPool:用于多个RecyclerView之间共享View,只需要创建一个RecyclerViewPool实例,然后调用RecyclerView的setRecycledViewPool(RecycledViewPool)方法即可.RecyclerView默认会创建一个RecyclerViewPool实例.内部有两个SparseArray(它也是一个list,android系统建议我们用SparseArray来代替HashMap