简单面试整理

又到了金三银四的面试季,自己也不得不参与到这场战役中来,其实是从去年底就开始看,android的好机会确实不太多,但也还好,3年+的android开发经历还是有一些面试机会的,不过确实不像几年前门槛那么低了,总的体会就是小的创业公司比较注重你的项目经历是否和自己的贴合,直接能过来独当一面。大厂除了看中项目经历外,还比较注重你知识面的广度,是广度、深度和解决方案等多方面的考察,平时够工作要好好积累临时刷题只聊点皮毛估计是过不了关的。下面就总结一些遇到的题目,各种风格的都有。这里先列举问题。

1.dp是什么,sp呢,有什么区别

  • 长度宽度的数值要使用dp作为单位放入dimens.xml文件中
  • 字体大小的数值要使用sp作为单位,也放入dimens.xml文件中
  • 使用sp作为字体大小单位,会随着系统的字体大小改变,而dp作为单位则不会.

2.自定义View,ViewGroup注意那些回调?

  • 构造方法
    1.public View(Context context)
    2.public View(Context context, @Nullable AttributeSet attrs)
    3.public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr)
    4.public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)
  • void onFinishInflate();
    当系统解析XML中声明的View后回调此方法,调用顺序:内层View->外层View,如果是viewgroup,适合在这里获取子View。
  • void onAttachedToWindow(); 当view
    被添加到window中回调,调用顺序:外层View->内层View。在XML中声明或在代码中构造,并调用addview(this
    view)方法都会回调该方法。
  • void onDetachedFromWindow(); 看名字就知道是与void
    onAttachedToWindow();对应的方法,在VIew从Window中移除时回调,如执行removeView()方法。 注意点:
    如果一个View从window中被移除了,那么其内层View(如果有)也会被一起移除,都会回调该方法,且会先回调内层View的onDetachedFromWindow()方法。
  • void onWindowFocusChanged(boolean hasWindowFocus);
    当View所在的Window获得或失去焦点时被回调此方法。
    hasWindowFocus:View所在Window是否获取到焦点,当该Window获得焦点时,hasWindowFocus等于true,否则等于false。
  • onTouchEvent(MotionEvent event);
    当手指触摸View的时候回调该方法,前提是触摸事件没有被拦截或者被子View消费掉。该方法是事件分发流程中最后的消费者。
    event:触摸事件对象,包含了该事件的所有信息,如触摸的类型(down、move、up),触摸位置等。
    返回值:true:事件被消费了,false:没有被消费,事件传递到外层View,super方法:同false。

3.界面卡顿的原因以及解决方法
UI卡顿的根本原因:
Android每个16ms就会绘制一次Activity,通过上述的结论我们知道,如果由于一些原因导致了我们的逻辑、CPU耗时、GPU耗时大于16ms,UI就无法完成一次绘制,那么就会造成卡顿。简单的一句话就是:卡主线程了。

下面总结一些常见的UI卡顿原因:

  • 外部因素引起的(以View为区分)
    内存抖动的问题
    方法太耗时了(CPU占用)
  • View本身的卡顿
    CPU计算时间,CPU的测量、布局时间
    CPU将计算好的Polygons和Texture传递到GPU的时候也需要时间。OpenGL ES
    API允许数据上传到GPU后可以对数据进行保存,缓存到display list。因此,我们平移等操作一个view是几乎不怎么耗时的。

解决办法:

  • 修改方法(算法),使得方法不耗时。
  • 放到子线程中,例如网络访问、大文件操作等,防止ANR。
  • GPU优化建议就是一句话:尽量避免过度绘制(overdraw)

4.android中的存储类型
① 使用SharedPreferences存储数据 
② 文件存储数据
③  SQLite数据库存储数据
④ 使用ContentProvider存储数据
⑤ 网络存储数据

5.service用过么,基本调用方法


6.Handler机制
1.Handler中开启线程和分发消息的一些方法:

  • post(Runnable)直接开启Runnable线程
  • postAtTime(Runnable,long)在指定的时间long,开始启动线程
  • postDelayed(Runnable long)在延迟long时间后,启动Runnable线程
  • sendEmptyMessage(int) 发送指定的消息,通过参数int来区分不同的消息
  • sendMessage(Message)发送消息到UI线程中
  • sendMessageAtTime(Message,long) 这个long代表的是系统时间,不推荐用
  • sendMessageDelayed(Message,long) 此方法long代表调用后几秒后执行。

2.流程图:

3.Handler,Message,looper 和 MessageQueue 构成了安卓的消息机制,handler创建后可以通过 sendMessage 将消息加入消息队列,然后 looper不断的将消息从 MessageQueue 中取出来,回调到 Hander 的 handleMessage方法,从而实现线程的通信。

从两种情况来说,第一在UI线程创建Handler,此时我们不需要手动开启looper,因为在应用启动时,在ActivityThread的main方法中就创建了一个当前主线程的looper,并开启了消息队列,消息队列是一个无限循环,为什么无限循环不会ANR?因为可以说,应用的整个生命周期就是运行在这个消息循环中的,安卓是由事件驱动的,Looper.loop不断的接收处理事件,每一个点击触摸或者Activity每一个生命周期都是在Looper.loop的控制之下的,looper.loop一旦结束,应用程序的生命周期也就结束了。我们可以想想什么情况下会发生ANR,第一,事件没有得到处理,第二,事件正在处理,但是没有及时完成,而对事件进行处理的就是looper,所以只能说事件的处理如果阻塞会导致ANR,而不能说looper的无限循环会ANR

另一种情况就是在子线程创建Handler,此时由于这个线程中没有默认开启的消息队列,所以我们需要手动调用looper.prepare(),并通过looper.loop开启消息

主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。

4.Handler为什么会泄漏?
handler发送的消息在当前handler的消息队列中,如果此时activity finish掉了,那么消息队列的消息依旧会由handler进行处理,若此时handler声明为内部类(非静态内部类),我们知道内部类天然持有外部类的实例引用,那么就会导致activity无法回收,进而导致activity泄露。
5.为何handler要定义为static?
因为静态内部类不持有外部类的引用,所以使用静态的handler不会导致activity的泄露
为何handler要定义为static的同时,还要用WeakReference 包裹外部类的对象?
这是因为我们需要使用外部类的成员,可以通过"activity. "获取变量方法等,如果直接使用强引用,显然会导致activity泄露。

6.避免handle内存泄露的办法

  • 1.使用static 修饰的handler,但是一般会弱引用activity对象,因为要使用activity对象中的成员
  • 2.单独定义handler,同样可以弱引用activity
  • 3.使用内部类的handler,在onDestroy方法中removeCallbacksAndMessages

7.LinearLayout、FrameLayout、RelativeLayout性能对比,为什么

  • 1.RelativeLayout会让子View调用2次onMeasure,LinearLayout 在有weight时,也会调用子View2次onMeasure
  • 2.RelativeLayout的子View如果高度和RelativeLayout不同,则会引发效率问题,当子View很复杂时,这个问题会更加严重。如果可以,尽量使用padding代替margin。
  • 3.在不影响层级深度的情况下,使用LinearLayout和FrameLayout而不是RelativeLayout。 最后再思考一下文章开头那个矛盾的问题,为什么Google给开发者默认新建了个RelativeLayout,而自己却在DecorView中用了个LinearLayout。因为DecorView的层级深度是已知而且固定的,上面一个标题栏,下面一个内容栏。采用RelativeLayout并不会降低层级深度,所以此时在根节点上用LinearLayout是效率最高的。而之所以给开发者默认新建了个RelativeLayout是希望开发者能采用尽量少的View层级来表达布局以实现性能最优,因为复杂的View嵌套对性能的影响会更大一些。
  • 4.能用两层LinearLayout,尽量用一个RelativeLayout,在时间上此时RelativeLayout耗时更小。另外LinearLayout慎用layout_weight,也将会增加一倍耗时操作。由于使用LinearLayout的layout_weight,大多数时间是不一样的,这会降低测量的速度。这只是一个如何合理使用Layout的案例,必要的时候,你要小心考虑是否用layout
    weight。总之减少层级结构,才是王道,让onMeasure做延迟加载,用viewStub,include等一些技巧。

8.Activity的生命周期,finish调用后其他生命周期还会走么?

  • 在onCreate中:onCreate->onDestroy
  • 在onStart中:onCreate->onStart->onStop->onDestroy
  • 在onResume中:onCreate->onStart->onResume->onPause->onStop->onDestroy

9.apk包大小有限制么?怎么减少包大小?
用 Android Studio 提供的 APK Analyser 工具来分析下我们的 apk 文件:
可以看到占空间最多的主要是三个部分:classes.dex, res 和 resources.arsc。

  • classes.dex:包含有 Java 代码的字节码文件。
  • res:包含了资源文件,比如图片、布局文件等。
  • resources.arsc:包含所有的值资源文件,如 strings, dimensions, styles, integers 等。

解决办法:
一. gradle 中设置
1.1 开启minifyEnabled : 开启混淆,删除没用的java文件
1.2 开启shrinkResources : 去除无用资源
1.3 resConfig “zh”删除无用的语言资源
二.对图片的处理
2.1 使用tinpping 有损压缩,可以对png图片压缩
2.2 对于非透明的大图,png->jpp
2.3 使用webp 格式(根据 Google 的测试,无损压缩后的 WebP 比 PNG 文件少了 45% 的文件大小,即使这些 PNG 文件经过其他压缩工具压缩之后,WebP 还是可以减少 28% 的文件大小)
四. 使用shape文件替换图片
五.使用一套图,能用代码绘制出的图,就不要用图片
六谨慎添加libs,用不到的就删除

10.savedInstanceState onRestoreInstanceState区别

一、onSaveInstanceState(Bundle outState)在什么时机会被调用呢?
答案是当activity有可能被系统回收的情况下,而且是在onStop()之前。注意是有可能,如果是已经确定会被销毁,比如用户按下了返回键,或者调用了finish()方法销毁activity,则onSaveInstanceState不会被调用。
或者也可以说,此方法只有在activity被异常终止的情况下会被调用。

总结下,onSaveInstanceState(Bundle outState)会在以下情况被调用:
1、当用户按下HOME键时。
2、从最近应用中选择运行其他的程序时。
3、按下电源按键(关闭屏幕显示)时。
4、从当前activity启动一个新的activity时。
5、屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)。
在前4种情况下,当前activity的生命周期为:
onPause -> onSaveInstanceState -> onStop。 onPause和onSaveInstanceState的顺序是不一定的
二、onRestoreInstanceState什么时机被调用?
onRestoreInstanceState(Bundle savedInstanceState)只有在activity确实是被系统回收,重新创建activity的情况下才会被调用。
三、onCreate()里也有Bundle参数,可以用来恢复数据,它和onRestoreInstanceState有什么区别?

因为onSaveInstanceState 不一定会被调用,所以onCreate()里的Bundle参数可能为空,如果使用onCreate()来恢复数据,一定要做非空判断。而onRestoreInstanceState的Bundle参数一定不会是空值,因为它只有在上次activity被回收了才会调用。而且onRestoreInstanceState是在onStart()之后被调用的。有时候我们需要onCreate()中做的一些初始化完成之后再恢复数据,用onRestoreInstanceState会比较方便

11.view的绘制原理
View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()
各步骤的主要工作:

  1. OnMeasure():测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。
  2. OnLayout():确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。
  3. OnDraw():绘制视图。ViewRoot创建一个Canvas对象,然后调用OnDraw()。六个步骤:①、绘制视图的背景;②、保存画布的图层(Layer);③、绘制View的内容;④、绘制View子视图,如果没有就不用;⑤、还原图层(Layer);⑥、绘制滚动条。


measure过程:
1.ViewGroup.LayoutParams :用来指定视图高度和宽度的参数,不包括Padding。
2.MeasureSpec :表示测量规格。32位的Int值,高二位表示模式,后三十位表示测量规格的大小。
(1)UNSPECIFIED:不限定子视图尺寸大小。
(2)EXACTLY:父容器会为子视图确定一个尺寸大小,无论子视图要求多大,都要在父容器的限制内。
(2)AT_MOST:父容器会为子视图指定一个最大的尺寸,子视图所有的大小都必须在这个尺寸范围内。对应wrap_content。此时父容器无法获取子视图的大小,只能子视图自己根据需求设定。
ViewGroup.LayoutParams最终会被封装成MeasureSpec。

measure——重要方法
measure会从父容器遍历子View的measure方法,会根据ViewGroup.MeasureSpec和子View的LayoutParams来决定子视图的测量规格,通过这个测量规格进一步获取到子View的宽高,一层一层向下传递,不断保存整个父容器的测量宽高。
1.measure:该方法会调用onMeasure方法。
2.onMeasure:该方法会调用setMeasuredDimension()方法。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}

3.setMeasuredDimension():该方法完成整个的测量过程。

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {        boolean optical = isLayoutModeOptical(this);        if (optical != isLayoutModeOptical(mParent)) {            Insets insets = getOpticalInsets();            int opticalWidth  = insets.left + insets.right;            int opticalHeight = insets.top  + insets.bottom;            measuredWidth  += optical ? opticalWidth  : -opticalWidth;            measuredHeight += optical ? opticalHeight : -opticalHeight;        }        setMeasuredDimensionRaw(measuredWidth, measuredHeight);}

layout
onLayout方法是用来设置与父容器的摆放关系的,自定义View需重写该方法。
View中的onLayout是个空方法。

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}

LinearLaout中的onLayout如下:

@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {    if (mOrientation == VERTICAL) {        layoutVertical(l, t, r, b);    } else {        layoutHorizontal(l, t, r, b);    }}

draw
两个容易混淆的方法:
1.invalidate():请求系统重绘,未发生变化时不会调用。
2.requestLayout():当布局(方向、尺寸等)发生变化时,需手动调用,该方法会触发measure和layout的过程,而不会调用draw方法。

详细见:添加链接描述

12.IntentService生命周期是怎样的,使用场合

要编写耗时操作又不得不交于service管理的话,就需要引入IntentService,IntentService继承service,拥有service的全部生命周期,包含了service的全部特性,与service不同的是IntentService在onCreat被执行时内部会开启一个线程,执行耗时操作

简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
IntentService是Service类的子类,用来处理异步请求。客户端可以通过startService(Intent)方法传递请求给IntentService。IntentService在onCreate()函数中通过HandlerThread单独开启一个线程来处理所有Intent请求对象(通过startService的方式发送过来的)所对应的任务,这样以免事务处理阻塞主线程。执行完所一个Intent请求对象所对应的工作之后,如果没有新的Intent请求达到,则自动停止Service;否则执行下一个Intent请求所对应的任务。

IntentService在处理事务时,还是采用的Handler方式,创建一个名叫ServiceHandler的内部Handler,并把它直接绑定到HandlerThread所对应的子线程。 ServiceHandler把处理一个intent所对应的事务都封装到叫做onHandleIntent的虚函数;因此我们直接实现虚函数onHandleIntent,再在里面根据Intent的不同进行不同的事务处理就可以了。

更多相关文章

  1. Android(安卓)Binder 应用层调用过程分析
  2. BitmapFactory类的decodeStream方法在网络超时或较慢的时候无法
  3. Android(安卓)过度渲染及优化方法--3D效果(JakeWharton大神的scal
  4. android 4.0以上设置wifi静态IP以及DNS的方法
  5. android锁屏原理(一)
  6. Android免Root权限Hook系统函数修改程序运行时内存指令逻辑
  7. 消息推送Android(安卓)sdk集成指南
  8. Android(安卓)一文学会无障碍服务(AccessibilityService)
  9. Android源代码下载过程中无法下载repo的解决方法

随机推荐

  1. Android String Placeholders
  2. Android退出程序(三)——Android事件总线
  3. usb 配置
  4. Android 系統存在設計漏洞,釣魚網站隨時出
  5. ListView 取消点击效果
  6. 【Android的从零单排开发日记】之入门篇(
  7. android 基础知识整理 1
  8. 详解Android 触摸事件处理和传递过程的来
  9. android 访问SD卡的方法
  10. Android(安卓)API demos 阅读笔记 4