说明
最近由于自己换了一份工作,这时就有一些小伙伴问我Android的面试会问什么,这次就先给大家展示一下一些简单的Android面试题吧,以后会持续更新,希望对有需要的朋友有帮助。
面试题##
sp操作apply和commit的区别:
1. apply没有返回值而commit返回boolean表明修改是否提交成功
2. apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘,
而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后在操作,从而降低了效率。
而apply只是原子的提交到内存,后面有调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。
3. apply方法不会提示任何失败的提示。
由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要用commit的。
Activity和Fragment生命周期:
Activity: onCreate(), onStart(), onResume(), onPause(), onStop(), onDestory(),
Fragment: onAttach(), onCreate(), onCreateView(), onActivityCreated(), onStart(), onResume(), onPause(), onStop(), onDestoryView(), onDestory(), onDetach()
两个Activity的跳转执行的方法:
一般情况比如说有两个activity,分别叫A,B ,当在A里面激活B组件的时候, A 会调用 onPause()方法,然后B 调用onCreate() ,onStart(), OnResume() ,
这个时候B覆盖了窗体, A会调用onStop()方法. 如果B呢 是个透明的,或者是对话框的样式, 就不会调用onStop()方法.
横竖屏切换:
设置 Activity 的 android:configChanges="orientation|keyboardHidden|screenSize"时,切屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法
Activity启动模式(singleTask, singleInstance, singleTop, standard)
Standard: 不管有没有已存在的实例,都生成新的实例。
SingleTop:如果发现有对应的 Activity 实例正位于栈顶,则重复利用,不再生成新的实例。不是位于栈顶,于是重新生成一个实例。
SingleTask:如果发现有对应的 Activity 实例,则使此 Activity 实例之上的其他 Activity 实例统统出栈,使此 Activity 实例成为栈顶对象,显示到幕前。
SingleInstance:它会启用一个新的栈结构,将 Activity 放置于这个新的栈结构中,并保证不再有其他 Activity 实例进入。
一个启动模式为 singleTop 的 activity,如果再试图启动会怎样
onNewIntent()
该方法被启动模式设置为“singleTop”的 Activity 回调,或者当通过设置 Intent.FLAG_ACTIVITY_SINGLE_TOP
的 Intent 启动 Activity 时被回调。在任何情况下,只要当栈顶的 Activity 被重新启动时没有重新创建一个新的 Activity
实例而是依然使用该 Activity 对象,那么 onNewIntent(Intent)方法就会被回调。
当一个 Activity 接收到新 Intent 的时候会处于暂停状态,因此你可以统计到 onResume()方法会被再次执行,当然这个执行是在 onNewIntent 之后的。
注意:如果我们在 Activity 中调用了 getIntent()方法,那么返回的 Intent 对象还是老的 Intent(也就是第一
次启动该 Activity 时的传入的 Intent 对象),但是如果想让 getIntent()返回最新的 Intent,那么我们可以通过setIntent(Intent)方法设置。
FragmentPagerAdapter和FragmentStatePagerAdapter:
FragmentPagerAdapter:对于不再需要的fragment,选择调用detach方法,仅销毁视图,并不会销毁fragment实例。
FragmentStatePagerAdapter:会销毁不再需要的fragment,当当前事务提交以后,会彻底的将fragmeng从当前Activity的FragmentManager中移除,state标明,
销毁时,会将其onSaveInstanceState(Bundle outState)中的bundle信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的fragment,也就是说,你可以在onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行恢复创建。
如上所说,使用FragmentStatePagerAdapter当然更省内存,但是销毁新建也是需要时间的。
一般情况下,如果你是制作主页面,就3、4个Tab,那么可以选择使用FragmentPagerAdapter,
如果你是用于ViewPager展示数量特别多的条目时,那么建议使用FragmentStatePagerAdapter。
广播的注册?方式(静态和动态)有什么区别:
(1)动态注册广播不是常驻型广播,也就是说广播跟随Activity的生命周期。注意在Activity结束前,移除广播接收器。
静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
(2)当广播为有序广播时:优先级高的先接收(不分静态和动态)。同优先级的广播接收器,动态优先于静态
(3)同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
(4)当广播为默认广播时:无视优先级,动态广播接收器优先于静态广播接收器。同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后册的。
多进程的缺点及改进方法:
缺点:1. Application多次创建 ; 2. 静态成员,全局变量,单例会失效 ;3. 共享文件的问题; 4.断点调试问题
改进方法: 在onCreate()的时候判断进程ID来创建;利用Intent、AIDL等来通信; 主程访问; 回归统一进程调试在改回来。
自定义控件的步骤 (onMeasure, onLayout,onDraw)
measure(widthMeasureSpec,heightMeasureSpec)--------->onMeasure(widthMeasureSpec,heightMeasureSpec)
- MeasureSpec:父控件对子View的测量宽高的期望--------->一个32位的数,前两位表示测量模式——SpecModel;后30位表示测量大小SpecSize。
- SpecModel测量模式有三种:1、Exactly,精确的,有300px,match_parent。2、AtMost,最大是多少,有wrap_content。3、Unspecfide,无限大。
- 测量结束后能获取测量的宽和测量的高,也就是widthSpecSize和heightSpecSize。通过getMeasureWidth和getMeasureHeight方法。
- 代表测量结束的方法setMeasureDimetion(widthSpecSize,heightSpecSize)。
- 自定义ViewGroup一定要重写onMeasure方法,如果不重写则子View获取不到宽和高。重写是在onMeasure方法中调用measureChildern()方法,遍历出所有子View并对其进行测量。
- 自定义View如果要使用wrap_content属性的话,则需重写onMeasure方法。
layout(l,t,r,b)------->onLayout(l,t,r,b)。 - 该方法其实是指定控件摆放的位置。
- 自定义View一般不重写onLayout方法。由父控件给其确定位置。
- 自定义ViewGroup需要重写onLayout方法,要不然子控件不知道摆放在哪。
- layout方法中调用了setFram(l,t,r,b)方法。该方法内部的实现{mLeft = l;mRight = r;mTop = t;mBottom = b};该方法代表布局完成。
- 布局完成之后,能够获取getWidth()和getHeight()的值。这两个方法的实现分别是return mRight - mLeft; return mBottom - mTop;
draw(Canvas canvas)。 - 一共有六步
-
- 绘制背景
-
- If necessary, save the canvas’ layers to prepare for fading
-
- 绘制View的内容, 在onDraw(canvas)方法中完成
-
- 绘制子View的内容 ,dispatchDraw。
-
- If necessary, draw the fading edges and restore layers
-
- 绘制装饰品(如滚动条)
-
Handle机制
这里面除了 Handler、Message 外还有隐藏的 Looper 和 MessageQueue 对象。
在主线程中 Android 默认已经调用了 Looper.preper()方法,调用该方法的目的是在 Looper 中创建
MessageQueue 成员变量并把 Looper 对象绑定到当前线程中。当调用 Handler 的 sendMessage(对象)方法的时候就将 Message 对象添加到了 Looper 创建的 MessageQueue 队列中,
同时给 Message 指定了 target 对象,其实这个 target 对象就是 Handler 对象。主线程默认执行了 Looper.looper()方法,该方法从 Looper 的成员变量
MessageQueue 中取出 Message,然后调用 Message 的 target 对象的 handleMessage()方法。这样就完成了整个消息机制。
为什么我们看不见messageQueue,Looper。
* 主线程给我们准备了Looper对象。
* 在Looper的构造函数里面生成了messageQueue对象。
* 主线程中的Looper对象存放在哪里?----->ThreadLocal的values里面。
怎么样来实现主线程向子线程发消息和子线程之间发消息?
* 在子线程new Handler然后在主线程发消息,在子线程处理消息。
* 会报错,因为子线程没有Looper对象,所以不能new Handler,调用Looper.prepare方法,生成Looper。
怎么样来确保发送消息和接收消息的Handler是同一个handler。
* message的target属性就是handler。
view层级优化(merge,ViewStub,include)
1:标签在布局优化中是使用最多的一个标签了,它就是为了解决重复定义布局的问题。如果想使用标签覆盖嵌入布局root布局属性,必须同时覆盖layout_height和layout_width属性,否则会直接报编译时语法错误
2:标签都是与标签组合使用的,它的作用就是可以有效减少View树的层次来优化布局。
对比截图就可以发现上面的四层结构,现在已经是三层结构了。当我们使用标签的时候,系统会自动忽略merge层级,而把TextView直接放置与平级。
标签在使用的时候需要特别注意布局的类型,例如我的标签中包含的是一个LinearLayout布局视图,布局中的元素是线性排列的,如果嵌套进主布局时,include标签父布局时FrameLayout,这种方式嵌套肯定会出问题的,merge中元素会按照FrameLayout布局方式显示。所以在使用的时候,标签虽然可以减少布局层级,但是它的限制也不可小觑。
只能作为XML布局的根标签使用。当Inflate以开头的布局文件时,必须指定一个父ViewGroup,并且必须设定attachToRoot为true。
View android.view.LayoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot)
root不可少,attachToRoot必须为true。
3:在开发过程中,经常会遇到这样一种情况,有些布局很复杂但是却很少使用。例如条目详情、进度条标识或者未读消息等,这些情况如果在一开始初始化,虽然设置可见性View.GONE,但是在Inflate的时候View仍然会被Inflate,仍然会创建对象,由于这些布局又想到复杂,所以会很消耗系统资源。
ViewStub就是为了解决上面问题的,ViewStub是一个轻量级的View,它一个看不见的,不占布局位置,占用资源非常小的控件。
SurfaceView
SurfaceView是View类的子类,可以直接从内存或者DMA等硬件接口取得图像数据,是个非常重要的绘图视图。它的特性是:可以在主线程之外的线程中向屏幕绘图上。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽量在画布canvas中画出。
继承SurfaceView并实现SurfaceHolder.Callback接口 ----> SurfaceView.getHolder()获得SurfaceHolder对象 ---->SurfaceHolder.addCallback(callback)添加回调函数---->SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布----> Canvas绘画 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示。
anroid提供的网络接?了解(HttpClient, HttpURLConnection)
在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。
而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。
它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。
对于新的应用程序应该更加偏向于使用HttpURLConnection,因为在以后的工作当中我们也会将更多的时间放在优化HttpURLConnection上面。
1.HttpClient是apache的开源实现,而HttpUrlConnection是安卓标准实现,安卓SDK虽然集成了HttpClient,但官方支持的却是HttpUrlConnection;
2.HttpUrlConnection直接支持GZIP压缩;HttpClient也支持,但要自己写代码处理;我们之前测试HttpUrlConnection的GZIP压缩在传大文件分包trunk时有问题,只适合小文件,不过这个BUG后来官方说已经修复了;
3.HttpUrlConnection直接支持系统级连接池,即打开的连接不会直接关闭,在一段时间内所有程序可共用;HttpClient当然也能做到,但毕竟不如官方直接系统底层支持好;
4.HttpUrlConnection直接在系统层面做了缓存策略处理,加快重复请求的速度。
对https支持:
AIDL:
tcp/udp了了解
连接: tcp三次握手连接,四次握手释放连接
资源: tcp多
结构: tcp复杂 20字节 udp8字节
模式: tcp流结构传输, udp报文传输
可靠性: tcp可靠(停止等待、连续ARQ、滑动窗口、累积确认)慢开始、拥塞避免、快重传、快恢复(3个连续重传请求)
设计模式:
单例(懒汉式、饿汉式)
工厂模式:普通工厂(根据字符类型判断) 多工厂(分别创建)、静态工厂(静态方法)、抽象工厂(分别实现接口)
建造者模式:
装饰着模式:顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
观察者模式:
适配器模式:
第三方框架框架: Rajava,Volley, Okhttp、EventBus、Retrofit、Glide、Picasso等等
RecycleView与ListView;GridView之对比
内存泄漏和内存溢出、OOM
1、一次性加载过多内容。
- 2、内存泄漏。
- 1、资源未关闭。---->游标、流、要调用recycle方法的。
- 2、生命周期长的对象持有了生命周期短的对象的引用。
- 静态成员变量持有类引用。
- 非静态内部类持有外部类的引用。
- 3、线程生命周期不可控。 无限循环动画。
MVC和MVP模式。
- 1、MVC模式。
- 目的------->将数据和视图分离。
- 结构。
- M------>Model层,包含网络请求数据、数据库读取、文件读写等等。
- V------>View层,在android工程当中所代表的就是layout文件。
- C------>Controler层,在android工程当中是Activity和Fragment。
- 缺点------>Activity不仅仅做了控制层的工作,很多时候还要做View层的工作,因为在android的MVC当中View层是写死的,不灵活Activity和Fragment。
- 2、MVP模式。
- 目的:解决MVC中的缺陷。
- 结构。
- M------>Model层,包含网络请求数据、数据库读取、文件读写等等。
- V------>View层,Activity和Fragment。
- P------->Presenter,自己写的Presenter类。在这个类的构造函数中传入Model和View的对象。将数据显示到视图上。 presenter对象在哪儿new------->activity中。
- 缺点。这是一个面向接口编程的模式,使用View层和P层的时候都会写接口和实现类,会增加很多的文件和类
线程池
- 为什么使用线程池?
- 1、开启线程和销毁线程都有资源消耗。
- 2、开启线程需要时间。虽然时间短,但蚊子腿上也有肉。
- 3、便于线程的管理。
- 怎么去创建线程池。------>参考谷歌市场。
- 线程池的运行机制?
- 首先几个重要的参数:corpalsize(核心线程数)、maxSize(最大线程数)、keepAliveTime(存活时间)。
- 当任务数小于核心线程数的时候?-------->创建线程执行任务。
- 当任务数大于核心线程数但是小于最大线程数的时候?------->放入队列中等待。
- 如果队列满了?--------->创建线程执行任务。
- 当任务数大于最大线程数?------->抛出异常。
AsyncTask(三个参数,onPreExecute(第一个参数),doInBackground, onProgressUpdate(第二个参数),onPostExecute(第三个参数))
核心线程数 cpu + 1, 最大线程数2n+1, 队列最大128 执行过程是mTask队列offer出一个任务加到请求队列去执行
String、StringBuilder和StringBuffer
-
String 类
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间。
String a = “a”; //假设a指向地址0x0001
a = “b”;//重新赋值后a指向地址0x0002,但0x0001地址中保存的"a"依旧存在,但已经不再是a所指向的,a 已经指向了其它地址。
因此String的操作都是改变赋值地址而不是改变值操作。 -
StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。 每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。
StringBuffer buf=new StringBuffer(); //分配长16字节的字符缓冲区
StringBuffer buf=new StringBuffer(512); //分配长512字节的字符缓冲区
StringBuffer buf=new StringBuffer(“this is a test”)//在缓冲区中存放了字符串,并在后面预留了16字节的空缓冲区。
3.StringBuffer
StringBuffer和StringBuilder类功能基本相似,主要区别在于StringBuffer类的方法是多线程、安全的,而StringBuilder不是线程安全的,相比而言,StringBuilder类会略微快一点。对于经常要改变值的字符串应该使用StringBuffer和StringBuilder类。
4.线程安全
StringBuffer 线程安全
StringBuilder 线程不安全
5.速度
一般情况下,速度从快到慢:StringBuilder>StringBuffer>String,这种比较是相对的,不是绝对的。
6.总结
(1).如果要操作少量的数据用 = String
(2).单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
(3).多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
总结
这次就先准备了这么多,以后会持续更新的,这里面有还几个问题还没有写出来,我会在面试题二中给出答案,敬请期待。
如有错误,敬请批评指正,或者有更好的知识点,也可反馈给我收纳,谢谢!