您的位置:首页 > 职场人生

[置顶] Android面试(一)

2017-09-02 18:34 162 查看

说明

最近由于自己换了一份工作,这时就有一些小伙伴问我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 时的传入的
4000
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等来通信; 主程访问; 回归统一进程调试在改回来。

自定义控件的步骤 (onLayout,onMeasure,onTouchEvent)

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)。

* 一共有六步

* 1. 绘制背景

* 2. If necessary, save the canvas’ layers to prepare for fading

* 3. 绘制View的内容, 在onDraw(canvas)方法中完成

* 4. 绘制子View的内容 ,dispatchDraw。

* 5. If necessary, draw the fading edges and restore layers

* 6. 绘制装饰品(如滚动条)

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

1. 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

总结

这次就先准备了这么多,以后会持续更新的,这里面有还几个问题还没有写出来,
902c
我会在面试题二中给出答案,敬请期待。

如有错误,敬请批评指正,或者有更好的知识点,也可反馈给我收纳,谢谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: