Android开发Android开发Android开发经验谈

Android面试题——View篇

2019-04-15  本文已影响32人  迷途小码农h
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缓存方法
Service的生命周期,两种启动方法,有什么区别
  1. context.startService() ->onCreate()- >onStart()->Service running-->(如果调用context.stopService() )->onDestroy() ->Service shut down
  1. context.bindService()->onCreate()->onBind()->Service running-->onUnbind() -> onDestroy() ->Service stop
静态的Broadcast 和动态的有什么区别
Android的布局方式有哪些?

LinearLayout,RelativeLayout,TableLayout,FrameLayout,AbsoluteLayout,GridLayout

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

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

设备横竖屏切换的时候,接下来会发生什么?
Android启动Service的两种方式是什么? 它们的适用情况是什么?
谈谈你对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事件的传递机制是怎样的?
Fragment与Fragment、Activity通信的方式
  1. 直接在一个Fragment中调用另外一个Fragment中的方法

  2. 使用接口回调

  3. 使用广播

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

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

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

如何规避oom?
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。

<style name="Theme.FloatActivity" parent="android:style/Theme.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的处理
过度绘制、卡顿优化:
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格式的占用内存是多大

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中的几种动画
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()进行漏洞修复。

上一篇 下一篇

猜你喜欢

热点阅读