Android面试题——View篇

Android面试题View篇
Activity生命周期?

onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDetroy()

Activity的启动过程(不要回答生命周期)

app启动的过程有两种情况,第一种是从桌面launcher上点击相应的应用图标,第二种是在activity中通过调用startActivity来启动一个新的activity。 我们创建一个新的项目,默认的根activity都是MainActivity,而所有的activity都是保存在堆栈中的,我们启动一个新的activity就会放在上一个activity上面,而我们从桌面点击应用图标的时候,由于launcher本身也是一个应用,当我们点击图标的时候,系统就会调用startActivitySately(),一般情况下,我们所启动的activity的相关信息都会保存在intent中,比如action,category等等。我们在安装这个应用的时候,系统也会启动一个PackaManagerService的管理服务,这个管理服务会对AndroidManifest.xml文件进行解析,从而得到应用程序中的相关信息,比如service,activity,Broadcast等等,然后获得相关组件的信息。当我们点击应用图标的时候,就会调用startActivitySately()方法,而这个方法内部则是调用startActivty(),而startActivity()方法最终还是会调用startActivityForResult()这个方法。而在startActivityForResult()这个方法。因为startActivityForResult()方法是有返回结果的,所以系统就直接给一个-1,就表示不需要结果返回了。而startActivityForResult()这个方法实际是通过Instrumentation类中的execStartActivity()方法来启动activity,Instrumentation这个类主要作用就是监控程序和系统之间的交互。而在这个execStartActivity()方法中会获取ActivityManagerService的代理对象,通过这个代理对象进行启动activity。启动会就会调用一个checkStartActivityResult()方法,如果说没有在配置清单中配置有这个组件,就会在这个方法中抛出异常了。当然最后是调用的是Application.scheduleLaunchActivity()进行启动activity,而这个方法中通过获取得到一个ActivityClientRecord对象,而这个ActivityClientRecord通过handler来进行消息的发送,系统内部会将每一个activity组件使用ActivityClientRecord对象来进行描述,而ActivityClientRecord对象中保存有一个LoaderApk对象,通过这个对象调用handleLaunchActivity来启动activity组件,而页面的生命周期方法也就是在这个方法中进行调用。

Activity的启动模式
  1. standard:默认标准模式,每启动一个都会创建一个实例,

  2. singleTop:栈顶复用,如果在栈顶就调用onNewIntent复用,从onResume()开始

  3. singleTask:栈内复用,本栈内只要用该类型Activity就会将其顶部的activity出栈

  4. singleInstance:单例模式,除了3中特性,系统会单独给该Activity创建一个栈,

Activity缓存方法
  • 1.配置改变导致Activity被杀死,横屏变竖屏:在onStop之前会调用onSaveInstanceState()保存数据在重建Activity之后,会在onStart()之后调用onRestoreInstanceState(),并把保存下来的Bundle传给onCreate()和它会默认重建Activity当前的视图,我们可以在onCreate()中,回复自己的数据。

  • 2.内存不足杀掉Activity,优先级分别是:前台可见,可见非前台,后台。

Service的生命周期,两种启动方法,有什么区别
  1. context.startService() ->onCreate()- >onStart()->Service running-->(如果调用context.stopService() )->onDestroy() ->Service shut down
  • 1.如果Service还没有运行,则调用onCreate()然后调用onStart();

  • 2.如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次。

  • 3.调用stopService的时候直接onDestroy,

  • 4.如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。该Service的调用者再启动起来后可以通过stopService关闭Service。

  1. context.bindService()->onCreate()->onBind()->Service running-->onUnbind() -> onDestroy() ->Service stop
  • 1.onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。

  • 2.这个时候会把调用者和Service绑定在一起,Context退出了,Service就会调用onUnbind->onDestroy相应退出。

  • 3.所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。

静态的Broadcast 和动态的有什么区别
  • 1.动态的比静态的安全

  • 2.静态在app启动的时候就初始化了 动态使用代码初始化

  • 3.静态需要配置 动态不需要

  • 4.生存期,静态广播的生存期可以比动态广播的长很多

  • 5.优先级动态广播的优先级比静态广播高

  • 6.静态不受页面生命周期的影响,即使退出了页面,也可以收到广播这种广播一般用于想开机自启动啊等等,由于这种注册的方式的广播是常驻型广播,所以会占用CPU的资源。

  • 7.动态叫非常驻型广播,收到生命周期的影响,退出页面后,就不会收到广播,我们通常运用在更新UI方面。这种注册方式优先级较高。最后需要解绑,否会会内存泄露。

  • 8.广播是分为有序广播和无序广播。

Android的布局方式有哪些?

LinearLayout,RelativeLayout,TableLayout,FrameLayout,AbsoluteLayout,GridLayout

在创建fragment时如何传递初始化参数?

Fragment初始化一定要提供默认构造函数。不能用构造函数传递参数!不要写带参数的构造函数。在Fragment里添加获取Fragment的newInstance函数,以后获取Fragment就使用这个函数,不要使用构造函数新建Fragment!使用setArgument和getArgument传递参数

设备横竖屏切换的时候,接下来会发生什么?
  • 1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

  • 2、设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

  • 3、设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

Android启动Service的两种方式是什么? 它们的适用情况是什么?
  • 如果后台服务开始后基本可以独立运行的话,可以用startService。音乐播放器就可以这样用。它们会一直运行直到你调用 stopSelf或者stopService。你可以通过发送Intent或者接收Intent来与正在运行的后台服务通信,但大部分时间,你只是启动服务并让它独立运行。如果你需要与后台服务通过一个持续的连接来比较频繁地通信,建议使用bind()。比如你需要定位服务不停地把更新后的地理位置传给UI。Binder比Intent开发起来复杂一些,但如果真的需要,你也只能使用它。

  • startService:生命周期与调用者不同。启动后若调用者未调用stopService而直接退出,Service仍会运行

  • bindService:生命周期与调用者绑定,调用者一旦退出,Service就会调用unBind->onDestroy

谈谈你对Android中Context的理解?

Context是一个抽象基类。在翻译为上下文,也可以理解为环境,是提供一些程序的运行环境基础信息。Context下有两个子类,ContextWrapper是上下文功能的封装类,而ContextImpl则是上下文功能的实现类。而ContextWrapper又有三个直接的子类, ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一个带主题的封装类,而它有一个直接子类就是Activity,所以Activity和Service以及Application的Context是不一样的,只有Activity需要主题,Service不需要主题。Context一共有三种类型,分别是Application、Activity和Service。这三个类虽然分别各种承担着不同的作用,但它们都属于Context的一种,而它们具体Context的功能则是由ContextImpl类去实现的,因此在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。

getApplicationContext()和getApplication()方法得到的对象都是同一个application对象,只是对象的类型不一样。 Context数量 = Activity数量 + Service数量 + 1 (1为Application)

理解Activity,View,Window三者关系

这个问题真的很不好回答。所以这里先来个算是比较恰当的比喻来形容下它们的关系吧。Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图)LayoutInflater像剪刀,Xml配置像窗花图纸。 1:Activity构造的时候会初始化一个Window,准确的说是PhoneWindow。 2:这个PhoneWindow有一个“ViewRoot”,这个“ViewRoot”是一个View或者说ViewGroup,是最初始的根视图。 3:“ViewRoot”通过addView方法来一个个的添加View。比如TextView,Button等 4:这些View的事件监听,是由WindowManagerService来接受消息,并且回调Activity函数。比如onClickListener,onKeyDown等。

Service的onCreate回调在UI线程中吗?

Service生命周期的各个回调和其他的应用组件一样,是跑在主线程中,会影响到你的UI操作或者阻塞主线程中的其他事情。

View的绘制流程

自定义控件: 1、组合控件。这种自定义控件不需要我们自己绘制,而是使用原生控件组合成的新控件。如标题栏。 2、继承原有的控件。这种自定义控件在原生控件提供的方法外,可以自己添加一些方法。如制作圆角,圆形图片。 3、完全自定义控件:这个View上所展现的内容全部都是我们自己绘制出来的。比如说制作水波纹进度条。

View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()

第一步:OnMeasure():测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。

第二步:OnLayout():确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。

第三步:OnDraw():绘制视图。ViewRoot创建一个Canvas对象,然后调用OnDraw()。六个步骤:①、绘制视图的背景;②、保存画布的图层(Layer);③、绘制View的内容;④、绘制View子视图,如果没有就不用; ⑤、还原图层(Layer);⑥、绘制滚动条。

View,ViewGroup事件分发
  1. Touch事件分发中只有两个主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三个相关事件。View包含dispatchTouchEvent、onTouchEvent两个相关事件。其中ViewGroup又继承于View。

  2. ViewGroup和View组成了一个树状结构,根节点为Activity内部包含的一个ViwGroup。

  3. 触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。

  4. 当Acitivty接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup的遍历可以看成是递归的。分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。

  5. 当某个子View返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下去的Move和Up事件将由该子View直接进行处理。由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上级ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象:如ViewGroup0-ViewGroup1-TextView的结构中,TextView返回了true,它将被保存在ViewGroup1中,而ViewGroup1也会返回true,被保存在ViewGroup0中。当Move和UP事件来时,会先从ViewGroup0传递至ViewGroup1,再由ViewGroup1传递至TextView。

  6. 当ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch事件。触发的方式是调用super.dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。

  7. onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。

Android中touch事件的传递机制是怎样的?
  • 1.Touch事件传递的相关API有dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent

  • 2.Touch事件相关的类有View、ViewGroup、Activity

  • 3.Touch事件会被封装成MotionEvent对象,该对象封装了手势按下、移动、松开等动作

  • 4.Touch事件通常从Activity#dispatchTouchEvent发出,只要没有被消费,会一直往下传递,到最底层的View

  • 5.如果Touch事件传递到的每个View都不消费事件,那么Touch事件会反向向上传递,最终交由Activity#onTouchEvent处理

  • 6.onInterceptTouchEvent为ViewGroup特有,可以拦截事件

  • 7.Down事件到来时,如果一个View没有消费该事件,那么后续的MOVE/UP事件都不会再给它

Fragment与Fragment、Activity通信的方式
  1. 直接在一个Fragment中调用另外一个Fragment中的方法

  2. 使用接口回调

  3. 使用广播

  4. Fragment直接调用Activity中的public方法

在创建fragment时如何传递初始化参数?

Fragment初始化一定要提供默认构造函数。不能用构造函数传递参数!不要写带参数的构造函数。在Fragment里添加获取Fragment的newInstance函数,以后获取Fragment就使用这个函数,不要使用构造函数新建Fragment!使用setArgument和getArgument传递参数

如何规避oom?
  • 使用更加轻量的数据结构

  • 避免在Android里面使用Enum

  • 减小Bitmap对象的内存占用

  • 使用更小的图片

  • 复用系统自带的资源

  • 注意在ListView/GridView等出现大量重复子组件的图里面对ConvertView的复用

  • Bitmap对象的复用

  • 避免在onDraw方法里面执行对象的创建

  • 避免对象的内存泄露(重点)

  • 考虑使用Application Context而不是Activity Context

  • 注意WebView的泄漏(重点)

  • 资源文件需要选择合适的文件夹进行存放

  • 谨慎使用static对象(重点)

  • 特别留意单例对象中不合理的持有

  • 珍惜Services资源

  • 谨慎使用“抽象”编程

  • 谨慎使用依赖注入框架

  • .谨慎使用多进程

  • Handler的使用(重点)

  • 强软弱虚引用的应用(重点)

  • 主线程操作UI,子线程操作数据(必填)

Android 中如何捕获未捕获的异常

自 定 义 一 个 Application , 比 如 叫 MyApplication 继 承 Application 实 现 UncaughtExceptionHandler。 覆写 UncaughtExceptionHandler 的 onCreate 和 uncaughtException 方法。

保存Activity状态

onSaveInstanceState(Bundle)会在activity转入后台状态之前被调用,也就是onStop()方法之前,onPause方法之后被调用;

数据存储有哪些方式?

1.sharedpreferences 2.file 3.Sqlite 4.ContentProvide 5.网络存储

如何将一个Activity设置成窗口的样式?

第一种方法,在styles.xml文件中,可以新建如下的类似Dialog的style。

第二种方法,在AndroidManifest.xml中在需要显示为窗口的Activity中添加如下属性: android:theme=“@style/Theme.FloatActivity”。 也可以直接添加对应需要展示为Dialog style的Activity的android:theme属性为android:theme=“@android:style/Theme.Dialog”。

ScrollView是否可以和ListView混合使用?如何可以,说明混合使用的方式,如果不行,说明原因。

可以,计算整个ListView的高度,填充数据后重新设置ListView高度,重写onMeasure和onInterceptTouchEvent方法

解决ScrollView嵌套ListView和GridView冲突的方法

重写ListView的onMeasure方法,来自定义高度: 解决ScrollView嵌套ListView和GridView冲突的方法 重写ListView的onMeasure方法,来自定义高度:

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);        super.onMeasure(widthMeasureSpec, expandSpec);
}

通过Intent传递一些二进制数据的方法有哪些? 使用Serializable接口实现序列化,这是Java常用的方法。 实现Parcelable接口,这里Android的部分类比如Bitmap类就已经实现了,同时Parcelable在Android AIDL中交换数据也很常见的。

Serializable 和 Parcelable 的区别

在使用内存的时候,Parcelable 类比 Serializable 性能高,所以推荐使用 Parcelable 类。

  1. Serializable 在序列化的时候会产生大量的临时变量,从而引起频繁的 GC。

  2. Parcelable 不能使用在要将数据存储在磁盘上的情况。尽管 Serializable 效率低点,但在这 种情况下,还是建议你用 Serializable 。

Bitmap的处理
  • 1.当使用ImageView的时候,可能图片的像素大于ImageView,此时就可以通过BitmapFactory.Option来对图片进行压缩,inSampleSize表示缩小2^(inSampleSize-1)倍。

  • 2.BitMap的缓存:

  • 1.同步加载只创建一个线程然后按照顺序进行图片加载

  • 2.异步加载使用线程池,让存在的加载任务都处于不同线程

  • 3.为了不开启过多的异步任务,只在列表静止的时候开启图片加载

  • 1.使用LruCache进行内存缓存。

  • 2.使用DiskLruCache进行硬盘缓存。

  • 3.实现一个ImageLoader的流程:同步异步加载、图片压缩、内存硬盘缓存、网络拉取

过度绘制、卡顿优化:
  • 1.过度绘制:

  • 1.移除Window默认的Background:getWidow.setBackgroundDrawable(null);

  • 2.移除XML布局文件中非必需的Background

  • 3.减少布局嵌套(扁平化的一个体现,减少View数的深度,也就减少了View树的遍历时间,渲染的时候,前后期的工作,总是按View树结点来)

  • 4.在引入布局文件里面,最外层可以用merge替代LinearLayout,RelativeLayout,这样把子UI元素直接衔接在include位置

  • 5.工具:HierarchyViewer 查看视图层级

  • 2.卡顿优化:16ms数据更新

view绘制机制和加载过程,请详细说整个流程
  1. 1.ViewRootImpl会调用performTraversals(),其内部会调用performMeasure()、performLayout、performDraw()。

  2. 2.performMeasure()会调用最外层的ViewGroup的measure()-->onMeasure(),ViewGroup的onMeasure()是抽象方法,但其提供了measureChildren(),这之中会遍历子View然后循环调用measureChild()这之中会用getChildMeasureSpec()+父View的MeasureSpec+子View的LayoutParam一起获取本View的MeasureSpec,然后调用子View的measure()到View的onMeasure()-->setMeasureDimension(getDefaultSize(),getDefaultSize()),getDefaultSize()默认返回measureSpec的测量数值,所以继承View进行自定义的wrap_content需要重写。

  3. 3.performLayout()会调用最外层的ViewGroup的layout(l,t,r,b),本View在其中使用setFrame()设置本View的四个顶点位置。在onLayout(抽象方法)中确定子View的位置,如LinearLayout会遍历子View,循环调用setChildFrame()-->子View.layout()。

  4. 4.performDraw()会调用最外层ViewGroup的draw():其中会先后调用background.draw()(绘制背景)、onDraw()(绘制自己)、dispatchDraw()(绘制子View)、onDrawScrollBars()(绘制装饰)。

  5. 5.MeasureSpec由2位SpecMode(UNSPECIFIED、EXACTLY(对应精确值和match_parent)、AT_MOST(对应warp_content))和30位SpecSize组成一个int,DecorView的MeasureSpec由窗口大小和其LayoutParams决定,其他View由父View的MeasureSpec和本View的LayoutParams决定。ViewGroup中有getChildMeasureSpec()来获取子View的MeasureSpec。

  6. 6.三种方式获取measure()后的宽高:

  7. 1.Activity#onWindowFocusChange()中调用获取

  8. 2.view.post(Runnable)将获取的代码投递到消息队列的尾部。

  9. 3.ViewTreeObservable.

Bitmap图像模式有哪几种,给出一张1080 * 1920的,ARGB 8888格式的占用内存是多大
  • Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位

  • Bitmap.Config ARGB_8888:每个像素占四位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位

  • Bitmap.Config RGB_565:每个像素占四位,即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位

  • Bitmap.Config ALPHA_8:每个像素占四位,只有透明度,没有颜色。

ARGB:指的是一种色彩模式,里面A代表Alpha,R表示red,G表示green,B表示blue。 ARGB 8888一个像素占用4个字节,一个字节8位,1080 * 1920 * 4 * 8

图片优化
  1. 对图片本身进行操作。尽量不要使用setImageBitmap、setImageResource、BitmapFactory.decodeResource来设置一张大图,因为这些方法在完成decode后, 最终都是通过java层的createBitmap来完成的,需要消耗更多内存.

  2. 图片进行缩放的比例,SDK中建议其值是2的指数值,值越大会导致图片不清晰。

  3. 不用的图片记得调用图片的recycle()方法

Android UI适配

字体使用sp,使用dp,多使用match_parent,wrap_content,weight 图片资源,不同图片的的分辨率,放在相应的文件夹下可使用百分比代替。

Android中的几种动画
  • 帧动画:Drawable Animation,指通过指定每一帧的图片和播放时间,有序的进行播放而形成动画效果,比如想听的律动条。

  • 补间动画:View Animation(Tween Animation),指通过指定View的初始状态、变化时间、方式,通过一系列的算法去进行图形变换,从而形成动画效果,主要有Alpha、Scale、Translate、Rotate四种效果。注意:只是在视图层实现了动画效果,并没有真正改变View的属性,比如滑动列表,改变标题栏的透明度。

  • 属性动画:Property Animation,在Android3.0的时候才支持,通过不断的改变View的属性,不断的重绘而形成动画效果。相比于视图动画,View的属性是真正改变了。比如view的旋转,放大,缩小。

HybridApp WebView和JS交互

Android与JS通过WebView互相调用方法,实际上是: Android去调用JS的代码

  1. 通过WebView的loadUrl(),使用该方法比较简洁,方便。但是效率比较低,获取返回值比较困难。

  2. 通过WebView的evaluateJavascript(),该方法效率高,但是4.4以上的版本才支持,4.4以下版本不支持。所以建议两者混合使用。 JS去调用Android的代码

  3. 通过WebView的addJavascriptInterface()进行对象映射 ,该方法使用简单,仅将Android对象和JS对象映射即可,但是存在比较大的漏洞。

漏洞产生原因是:当JS拿到Android这个对象后,就可以调用这个Android对象中所有的方法,包括系统类(java.lang.Runtime 类),从而进行任意代码执行。 解决方式: (1)Google 在Android 4.2 版本中规定对被调用的函数以 @JavascriptInterface进行注解从而避免漏洞攻击。 (2)在Android 4.2版本之前采用拦截prompt()进行漏洞修复。

你可能感兴趣的:(Android面试题——View篇)