2021Android核心基础面试题解析
Posted 码农 小生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021Android核心基础面试题解析相关的知识,希望对你有一定的参考价值。
1、四大组件是什么?
1)Activity:用户可操作的可视化界面,为用户提供一个完成操作指令的窗口。一个Activity 通常是一个单独的屏幕,Activity 通过 Intent 来进行通信。android 中会维持一个Activity Stack,当一个新 Activity 创建时,它就会放到栈顶,这个 Activity 就处于运行状态。
2)Service:服务,运行在手机后台,适合执行不需和用户交互且还需长期运行的任务。
3)ContentProvider:内容提供者,使一个应用程序的指定数据集提供给其他应用程序,其他应用可通过 ContentResolver 类从该内容提供者中获取或存入数据。它提供了一种跨进程数据共享的方式,当数据被修改后,ContentResolver 接口的 notifyChange 函数通知那些注册监控特定 URI 的 ContentObserver 对象。
如果 ContentProvider 和调用者在同一进程中,ContentProvider 的方法(query/insert/update/delete 等)和调用者在同一线程中;
如果ContentProvider 和调用者不在
同一进程,ContentProvider 方法会运行在它自身进程的一个 Binder 线程中。
4)Broadcast Receiver: 广播接收者,运用在应用程序间传输信息,可以使用广播接收器来让应用对一个外部事件做出响应。
2、四大组件的生命周期和简单用法
1)Activity:
onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory()
-
onCreate():为 Activity 设置布局,此时界面还不可见;
-
onStart(): Activity 可见但还不能与用户交互,不能获得焦点
-
onRestart():重新启动 Activity 时被回调
-
onResume(): Activity 可见且可与用户进行交互
onPause(): 当前 Activity 暂停,不可与用户交互,但还可见。在新
Activity 启动前被系统调
用保存现有的 Activity 中的持久数据、停止动画等。
-
onStop(): 当 Activity 被新的 Activity 覆盖不可见时被系统调用
-
onDestory(): 当 Activity 被系统销毁杀掉或是由于内存不足时调用
2)Service
a) onBind 方式绑定的:
onCreate->onBind->onUnBind->onDestory(不管调用 bindService 几次,onCreate 只会调用一次,onStart 不会被调用。
建立连接后,service 会一直运行,直到调用 unBindService 或是之前调用的 bindService 的 Context 不存在了,系统会自动停止Service,对应的 onDestory 会被调用)
b) startService 启动的:
onCreate->onStartCommand->onDestory(start 多次,onCreate 只会被调用一次,onStart 会调用多次,该 service 会在后台运行,直至被调用 stopService 或是stopSelf)
c) 又被启动又被绑定的服务
不管如何调用 onCreate()只被调用一次,startService 调用多少次,onStart 就会被调用多少次,而 unbindService 不会停止服务,必须调用 stopService或是 stopSelf 来停止服务。
必须 unbindService 和 stopService(stopSelf)同时都调用了才会停
止服务。
3)BroadcastReceiver
a) 动态注册:存活周期是在 Context.registerReceiver 和 Context.unregisterReceiver 之间,BroadcastReceiver 每次收到广播都是使用注册传入的对象处理的。
**b) 静态注册:进程在的情况下,receiver 会正常收到广播,调用 onReceive 方法;生命周期只存活在 onReceive 函数中,此方法结束,BroadcastReceiver 就销毁了。onReceive()只有十几秒存活时间,在 onReceive()内操作超过 10S,就会报 ANR。
进程不存在的情况,广播相应的进程会被拉,Application.onCreate 会被调用,再调用onReceive。
4)ContentProvider
和应用的生命周期一样,它属于系统应用,应用启动时,它会跟
着初始化,应用关闭或被杀,它会跟着结束。
3、Activity 之间的通信方式
1)通过 Intent 方式传递参数跳转
2)通过广播方式
3)通过接口回调方式
4)借助类的静态变量或全局变量
5)借助 SharedPreference 或是外部存储,如数据库或本地文件
4、Activity 各种情况下的生命周期
- 两个 Activity(A->B)切换(B 正常的 Activity)的生命周期:
onPause(A)->onCreate(B)->onStart(B)->onResume(B)->oStop(A)
这时如果按回退键回退到 A onPause(B)->onRestart(A)>onStart(A)->onResume(A)->oStop(B)
如果在切换到 B 后调用了 A.finish(),则会走到 onDestory(A),这时点回退键会退出应用
- 两个 Activity(A->B)切换(B 透明主题的 Activity 或是 Dialog 风格的 Acivity)的生命周期:onPause(A)->onCreate(B)->onStart(B)->onResume(B)
这时如果回退到 A onPause(B)->onResume(A)->oStop(B)->onDestory(B)
- Activity(A)启动后点击 Home 键再回到应用的生命周期:onPause(A)->oStop(A)->onRestart(A)->onStart(A)->onResume(A)
5、如何退出Activity?
退出activity我们只需要调用finish就可以了。退出activity 会执行 onDestroy()方法。
如何一次性的退出多个Activity?
a、抛异常强制退出:
该方法通过抛异常,使程序Force Close。
验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。
b、记录打开的Activity:
每打开一个Activity,就记录下来。放到application里面去存起来。在需要退出时,关闭每一个Activity即可。
6、Activity 与 Fragment 之间生命周期比较
Fragment 生命周期:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onPause->onStop->onDestoryView->onDestory->onDetach
切换到该 Fragment:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume
按下 Power 键:onPause->onSaveInstanceState->onStop
点亮屏幕解锁:onStart->onRestoreInstanceState->onResume
切换到其他 Fragment: onPause->onStop->onDestoryView
切回到该 Fragment: onCreateView->onActivityCreated->onStart->onResume
退出应用:onPause->onStop->onDestoryView->onDestory->onDetach
7、Activity 上有 Dialog 的时候按 Home 键时的生命周期
AlertDialog 并不会影响 Activity 的生命周期,按 Home 键后才会使 Activity 走 onPause->onStop,AlertDialog 只是一个组件,并不会使 Activity 进入后台。
8、两个 Activity 之间跳转时必然会执行的是哪几个方法?
前一个 Activity 的 onPause,后一个 Activity 的 onResume
9、Activity 的四种启动模式对比
1)standard:标准启动模式(默认),每启动一次 Activity,都会创建一个实例,即使从ActivityA startActivity ActivityA,也会再次创建 A 的实例放于栈顶,当回退时,回到上一个ActivityA 的实例。
2) singleTop:栈顶复用模式,每次启动 Activity,如果待启动的 Activity 位于栈顶,则不会重新创建 Activity 的实例,即不会走 onCreate->onStart,会直接进入 Activity 的 onPause->onNewIntent->onResume 方法
3) singleInstance: 单一实例模式,整个手机操作系统里只有一个该 Activity 实例存在,没有其他 Actvity,后续请求均不会创建新的 Activity。若 task 中存在实例,执行实例的onNewIntent()。应用场景:闹钟、浏览器、电话
4) singleTask:栈内复用,启动的 Activity 如果在指定的taskAffinity 的 task 栈中存在相应的实例,则会把它上面的 Activity 都出栈,直到当前 Activity 实例位于栈顶,执行相应的onNewIntent()方法。
如果指定的 task 不存在,创建指定taskAffinity 的 task,taskAffinity 的作用,进入指写 taskAffinity 的 task,如果指定的 task 存在,将 task 移到前台,如果指定的task 不存在,创建指定的 taskAffinity 的 task.应用场景:应用的主页面
10、fragment 各种情况下的生命周期
正常情况下的生命周期:onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onPause->onStop->onDestoryView->onDestory->onDetach
Fragment 在 Activity 中 replace onPause(旧)->onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume->onStop(旧)->onDestoryView(旧)
如果添加到 backStack 中,调用 remove()方法 fragment 的方法会走到 onDestoryView,但不会执行 onDetach(),即 fragment 本身的实例是存在的,成员变量也存在,但是 view 被销毁了。如果新替换的 Fragment 已在 BackStack 中,则不会执行 onAttach->onCreate
11、Fragment 状态保存 onSaveInstanceState 是哪个类的方法,在什么情况下使用?
在对应的 FragmentActivity.onSaveInstanceState 方法会调FragmentController.saveAllState,其中会对 mActive 中各个 Fragment 的实例状态和 View 状态分别进行保存.当 Activity 在做状态保存和恢复的时候, 在它其中的 fragment 自然也需要做状态保存和恢复。
12、Fragment.startActivityForResult 是和 FragmentActivity 的 startActivityForResult?
如果希望在 Fragment 的 onActivityResult 接收数据,就要调用
Fragment.startActivityForResult,而不是Fragment.getActivity().startActivityForResult。
Fragment.startActivityForResult-Fragment.
startActivityForResult>FragmentActivitymHost.HostCallbacks.onStartActivityFromFragment>FragmentActivity.startActivityFromFragment。
13、如何实现 Fragment 的滑动?
ViewPager+FragmentPagerAdapter+List
##14、fragment 之间传递数据的方式?
1)在相应的 fragment 中编写方法
在需要回调的 fragment 里获取对应的 Fragment 实例,调用相应的方法;
2)采用接口回调的方式进行数据传递;
- a) 在 Fragment1 中创建一个接口及接口对应的 set 方法;
- b) 在 Fragment1 中调用接口的方法;
- c)在 Fragment2 中实现该接口;
3)利用第三方开源框架 EventBus
15、service 和 activity 怎么进行数据交互?
1)通过 bindService 启动服务
可以在 ServiceConnection 的 onServiceConnected 中获取到Service 的实例,这样就可以调用 service 的方法,如果 service 想调用 activity 的方法,可以在 service 中定义接口类及相应的 set 方法,在 activity 中实现相应的接口,这样 service 就可以回调接口言法;
2)通过广播方式
16、简单描述一下广播接收者
广播接收者有两类,一种是系统本身就有的,一种是我们自己写的广播接收者。
广播接收者注册的方式也有两种:
一种是动态注册。
一种是静态注册。
只能用代码注册的广播,这种广播产生的频率是比较高的。比如电量变化的广播,屏幕解锁的广播。电量变化的广播。
广播接收者在注册的时候可以指定优先级,用于提高接收到广播的顺序。
广播一般是用于跨进程通讯的时候。
17、广播的分类
1)普通广播:完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高,并且无法中断广播的传播。
2)有序广播:发送有序广播后,广播接收者将按预先声明的优先级依次接收 Broadcast。
优先级高的优先接收到广播,而在其 onReceiver()执行过程中,广播不会传播到下一个接收者,此时当前的广播接收者可以 abortBroadcast()来终止广播继续向下传播,也可以将intent 中的数据进行修改设置,然后将其传播到下一个广播接收者。sendOrderedBroadcast(intent, null);//发送有序广播
3)粘性广播:sendStickyBroadcast()来发送该类型的广播信息,这种的广播的最大特点是,当粘性广播发送后,最后的一个粘性广播会滞留在操作系统中。如果在粘性广播发送后的一段时间里,如果有新的符合广播的动态注册的广播接收者注册,将会收到这个广播消息,虽然这个广播是在广播接收者注册之前发送的,另外一点,对于静态注册的广播接收者来说,这个等同于普通广播。
18、请描述一下广播 BroadcastReceiver 的理解
BroadcastReceiver 是一种全局监听器,用来实现系统中不同组件之间的通信。有时候也会用来作为传输少量而且发送频率低的数据,但是如果数据的发送频率比较高或者数量比较大就不建议用广播接收者来接收了,因为这样的效率很不好,因为BroadcastReceiver 接收数据的开销还是比较大的。
19、View的绘制流程,自定义View注意的几点?
View 的绘制过程遵循如下几步:
- a.绘制背景 background.draw(canvas)
- b.绘制自己(onDraw)
- c.绘制 children(dispatchDraw)
- d.绘制装饰(onDrawScrollBars)
View 绘制过程的传递是通过 dispatchDraw 来实现的,它会遍历所有的子元素的draw 方法,如此 draw 事件就一层一层的传递下去了
ps:view 有一个特殊的方法 setWillNotDraw,如果一个 view 不需要绘制内容,即不需要重写 onDraw 方法绘制,可以开启这个标记,系统会进行相应的优化。
默认情况下,View 没有开启这个标记,默认认为需要实现 onDraw 方法绘制,当我们继承 ViewGroup 实现自定义控件,并且明确知道不需要具备绘制功能时,可以开启这个标记,如果我们重写了 onDraw,那么要显示的关闭这个标记子 view 宽高可以超过父 view?
可以
1.android:clipChildren = “false” 这个属性要设置在父 view 上。代表其中的子View 可以超出屏幕。
2.子 view 要有具体的大小,一定要比父 view 大 才能超出。比如 父 view 高 度 100px 子 view 设置高度 150px。子 view 比父 view 大,这样超出的属性才有意义。(高度可以在代码中动态赋值,但不能用 wrap_content /match_partent)。
3.对父布局还有要求,要求使用 linearLayout(反正我用 RelativeLayout 是不行)。你如果必须用其他布局可以在需要超出的 view 上面套一个 linearLayout 外面再套其他的布局。
4.最外面的布局如果设置的 padding 不能超出
自定义View注意的几点?
1.让 view 支持 wrap_content 属性,在 onMeasure 方法中针对 AT_MOST 模式做专门处理,否则 wrap_content 会和 match_parent 效果一样(继承 ViewGroup 也同样要在 onMeasure 中做这个判断处理)
MeasureSpec.AT_MOST){
setMeasuredDimension(200,200); // wrap_content
情况下要设置一个默认
值,200 只是举个例子,最终的值需要计算得到刚好包裹内容的宽高值
}else if(widthMeasureSpec == MeasureSpec.AT_MOST){
setMeasuredDimension(200,heightMeasureSpec );
}else if(heightMeasureSpec == MeasureSpec.AT_MOST){
setMeasuredDimension(heightMeasureSpec ,200);
}
2.让 view 支持 padding(onDraw 的时候,宽高减去 padding 值,margin 由父布局控制,不需要 view 考虑),自定义 ViewGroup 需要考虑自身的 padding 和子view 的 margin 造成的影响
3.在 view 中尽量不要使用 handler,使用 view 本身的 post 方法
4.在 onDetachedFromWindow 中及时停止线程或动画
5.view 带有滑动嵌套情形时,处理好滑动冲突
ACTION_DOWN 没有拦截,ACTION_MOVE ACTION_UP 还会拦截吗
20、Android线程间的通讯
Handler是用来进行线程间的通信。
Looper是用来管理所属线程的消息队列MessageQueue的
每一个线程都需要有一个looper,每一个looper管理一个MessageQueue
Handler.sendMessage的意思是将某一个message放到MessageQueue中去,looper是个死循环,不断的读MessageQueue中的新消息。
要让looper的死循环运行起来,得调用Looper.loop()方法。
我们通常都会在子线程中,发一个消息到主线程中的messagequeue中去。
21、Android 线程间通信有哪几种方式?
1)共享变量(内存)
2)管道
3)handle 机制
runOnUiThread(Runnable)
view.post(Runnable)
22、Android 内存泄露原因
Android 内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到 gc roots 导致无法被 GC 回收。无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏了。
场景
1.类的静态变量持有大数据对象
静态变量长期维持到大数据对象的引用,阻止垃圾回收。
2.非静态内部类的静态实例
非静态内部类会维持一个到外部类实例的引用,如果非静态内部类的实例是静态的,就会间接长期维持着外部类的引用,阻止被回收掉。
3.资源对象未关闭
资源性对象如 Cursor、File、Socket,应该在使用后及时关闭。未在 finally 中关闭,会导致异常情况下资源对象未被释放的隐患。
4.注册对象未反注册
未反注册会导致观察者列表里维持着对象的引用,阻止垃圾回收。
5.Handler 临时性内存泄露
Handler 通过发送 Message 与主线程交互,Message 发出之后是存储在MessageQueue 中的,有些 Message 也不是马上就被处理的。在 Message 中存在一个 target,是 Handler 的一个引用,如果 Message 在 Queue 中存在的时间越长,就会导致 Handler 无法被回收。
如果 Handler 是非静态的,则会导致Activity 或者 Service 不会被回收。
由于 AsyncTask 内部也是 Handler 机制,同样存在内存泄漏的风险。
此种内存泄露,一般是临时性的。
22、内存泄露检测有什么好方法?
1、DDMS Heap 发现内存泄露
dataObject totalSize 的大小,是否稳定在一个范围内,如果操作程序,不断增加,说明内存泄露
2、使用 Heap Tool 进行内存快照前后对比
BlankActivity 手动触发 GC 进行前后对比,对象是否被及时回收定位:
1)、MAT 插件打开.hprof 具体定位内存泄露:
查看 histogram 项,选中某一个对象,查看它的 GC 引用链,因为存在 GC 引用链的,说明无法回收
2)、AndroidStudio 的 Allocation Tracker:
观测到期间的内存分配,哪些对象被创建,什么时候创建,从而准确定位
23、WebView 的性能优化
一个加载网页的过程中,native、网络、后端处理、CPU 都会
参与,各自都有必要的工作和依赖关系;让他们相互并行处理
而不是相互阻塞才可以让网页加载更快:
1.WebView 初始化慢,可以在初始化同时先请求数据,让后端和网络不要闲着。
2.常用 JS 本地化及延迟加载,使用第三方浏览内核后端处理慢,可以让服务器分 trunk 输出,在后端计算的同时前端也加载网络静态资源。
3.脚本执行慢,就让脚本在最后运行,不阻塞页面解析。同时,合理的预加载、预缓存可以让加载速度的瓶颈更小。
4.WebView 初始化慢,就随时初始化好一个 WebView待用。
5.DNS 和链接慢,想办法复用客户端使用的域名和链接。
24、Android 中图片的三级缓存策略
三级缓存:
•内存缓存,优先加载,速度最快;
•本地缓存,次优先加载,速度快;
•网络缓存,最后加载,速度慢,浪费流量 ;
三级缓存策略,最实在的意义就是 减少不必要的流量消耗,增加加载速度。
三级缓存的原理:
•首次加载的时候通过网络加载,获取图片,然后保存到内存和 SD 卡中。
•之后运行 APP 时,优先访问内存中的图片缓存。
•如果内存没有,则加载本地 SD 卡中的图片。
具体的缓存策略可以是这样的:内存作为一级缓存,本地作为二级缓存,网络加载为最后。
其中,内存使用 LruCache ,其内部通过 LinkedhCache。加载图片的时候,首先使用 LRU 方式进行寻找,找不到指定内容,按照三级缓存的方式,进行本地搜索,还没有就网络加载。ashMap 来持有外界缓存对象的强引用;对于本地缓存,使用 DiskLru
这个就是我整理的一些题目Android最基础的面试了,如果有写的有不好的地方希望大家多多指点 谢谢大家!!
最后
小编还整理了一些 其他Android 开发相关的学习文档、Android 核心笔记等等文档,希望能帮助到大家学习提升,如有需要参考的可以点击链接领取点击这里免费领取
以上是关于2021Android核心基础面试题解析的主要内容,如果未能解决你的问题,请参考以下文章