2020Android初中级面试知识点记录——Android篇

Android

持续更新…

Q:Fragment的生命周期

onAttach -> onCreate -> onCreateVIew -> onActivityCreated -> onStart -> onResume -> onPause -> onStop -> onDestroyView -> onDestroy -> onDetach

  • onAttach() 在Fragment 和 Activity 建立关联是调用
  • onCreateView() 当Fragment 创建视图时
  • onActivityCreated() 在相关联的 Activity 的 已创建,在这可以getActivity
  • onDestroyView() 当Fragment中的视图被销毁
  • onDetach() 当Fragment 和 Activity 解除关联时

通过FragmentManager管理Fragment的生命周期:

只有再创建的时候加入返回栈,在退栈时才会一起销毁创建的fragment,否则退栈时不销毁fragment

Q:Activity的生命周期

onCreate -> onStart -> onResume -> onPause -> onStop -> onDestoy

Q:onSaveInstanceState() 与 onRestoreIntanceState()

二者不是生命周期方法

  • onSaveInstanceState 用于意外死亡保存应用内容(如果是用户点击退出这种正常退出则不会走这个方法)
  • onRestoreIntanceState 用于恢复数据内容,方法传入的Bundle对象是onSaveInstanceState 所保存的

onSaveInstanceState和onPause的区别:

  • onSaveInstanceState用于临时性的数据保存
  • onPause用于持久性的数据保存

Q:启动Service的两种方式

  • context.startService() 会直接启动一个服务,会走Service 的 onCreate -> onStartCommand 方法,直到外部调用stopService() 或 Service里面调用stopSelf()方法,服务会停止并走onDestroy的生命周期
  • context.bindService() 会绑定一个服务,回调方法:onCreate(如果未创建)-> onBind 在onBind方法会返回一个IBinder实例,用于与Service交互,解绑调用unBind方法,如果没有client与服务端连接,会走onDestroy()方法停止服务

多次调用:

  • 第一种:会走onStartCommand 方法
  • 第二种:不会重复创建,会返回第一次创建的IBinder对象(同一个)
    同时调用:

需要同时调用unBind和stopService才能停止服务

注意:

在Service也是IBinder于UI线程,所以避免进行耗时操作, 所以可以使用IntentService;

IntentService

运行在子线程,并且运行完会自动销毁,但是需要通过startService来启动一个IntentService,不能使用bindService,因为IntentService其实里面封装了一个子线程的Handler,在IntentService的onStart方法里面会通过这个Handler发送一个message,然后在这个Handler里面的handlerMessage才会走到我们执行任务的onHandleIntent,但是如果直接bindService,就不会走到onStart方法,更不会执行onHandleIntent里面的方法了

Q:IntentService使用以及原理:

使用:

IntentService 是一个抽象类,继承并实现 onHandleIntent(Intent intent),在onHandleIntent处理异步任务

原理

工作流程

  • 在Service的 onCreate 创建了一个 HandlerThread和该线程的Handler
  • 在onStartCommand 调用onStart,在onStart又使用第一步创建的Handler发送一个包装(带有启动信息的)Intent的Message
  • 在handleMessage,会调用onHandleIntent,之后会调用stopSelf

注意

  • 可以多次开启IntentService,但是一次只能处理一次请求,剩下的请求会被挂起,依次执行后,如果没有任务了就会调用stopSelf停止
  • HandlerThread 是封装了 Handler的线程,带有Looper

推荐阅读

Q:安卓中数据持久化有哪几种方案?

  • SP存储
  • 文件存储
  • 数据库
  • 网络存储

Q:SharedPreferences是线程安全的吗?commit和apply的区别?

  • SP是线程安全的,但是进程不安全
  • commit是同步操作,会返回结果,尽量不在主线程操作;apply是异步操作
  • 每次commit/apply都会把全部数据一次性写入到磁盘,即没有增量写入的概念 。 所以单个文件千万不要太大 否则会严重影响性能

Q:广播相关
广播接收器的两种注册方式:

  • AndroidManifest文件注册
  • java文件动态注册(记得解除注册)

广播的种类

  • 普通广播
  • 有序广播(按照优先级一个个收到,可以终止传递)
  • 本地广播(应用内广播)
  • 粘性广播

推荐参考:厘米姑娘

Q:MVP和MVC模式的区别

如果不采用MVP/MVC模式,将所有的功能全部写在Activity/Fragment,会发现稍微复杂点的逻辑Activity/Fragment会变得非常臃肿
传统MVC(Model-View-Controler)中,xml无力承担所有View的功能,会发现Activity/Fragment同时承担了View和Controler的角色,所以对Activity解耦不够完全
MVP (Model-View-Presenter)中,Activity/Fragment只负责显示,将业务逻辑放在P层,P层再通过实际需求向M层请求数据,再返回给P层,P层再控制V层显示

Q:组件化相关

  • 组件化和模块化的区别

模块化以module的形式将业务模块分开,实现隔离/封装、高内聚,更加灵活可以随意拔插
组件化在模块化的基础上演变的,目的是解耦和复用,各个组件可以单独编译,在开发时可以专注于该模块,减少编译时间

具体参考这篇文章

  • 组件化的优点:
    1、模块间解耦,功能模块可以复用,业务模块可以提供演示版本
    2、开发时可以专注于当前模块,减少编译时间
    3、利于项目管理

需要注意的点:

  • 封装性
  • 注意生命周期管理

Q:讲一下Handler

Handler 是安卓里面用于跨线程通信的一个机制
主要使用为在一个线程创建一个Handler对象,重写其handleMessage方法,对接收的消息进行处理,而在其他线程则通过sendMessage或者sendMessageDelayed发送一个Message消息
优化:
发送的消息通过Message.obtain方法获取,这样可以减少资源浪费,不用每一次都new一个新对象
一般我们使用Handler对象都会创建一个匿名内部类,这样回默认持有外部类的引用,导致内存泄漏,解决方法是:通过创建静态内部类,并且传入外部Activity或者Fragment的弱引用,又或者是在生命周期销毁时移除Handler所有的消息,可以避免内存泄漏
实现原理:
在主线程里有一个looper对象,在主线程被创建的时候会自动的创建这个对象,并且这个looper会不停的从MessageQueue读取消息,而当我们发送Message时,会将这个消息加入到MessageQueue消息队列中,looper从MessageQueue取到消息后会回调到handleMessage方法对消息进行处理;
需要注意的是,子线程中默认没有looper,所以如果要在子线程中创建Handler,需要先调用 Looper.prepare() 方法将子线程变成一个looper线程,再调用 Looper.loop() 开始对消息的读取,这时创建的Handler会自动绑定子线程的looper,开始不停的从MessageQueue消息。
内存泄漏:

  • 使用非静态内部类会导致内存泄漏,将其改为静态内部类 + 弱引用的形式传入Activity
  • 在生命周期结束前移除所有的消息

MessageQueue是什么数据结构?

  • 单链表

Q:什么是ANR?如何避免

ANR 是指应用程序在一定时间内没有相应(主线程阻塞),系统对跳出一个对话框显示XXX未响应;
不同的组件有不同的时间:1. Activity(5s)2. BroadCastReceiver(10s)3. Service(20s)
几种常见的情况:

  • 主线程被IO操作(从4.0之后网络IO不允许在主线程中)阻塞。
  • 主线程中存在耗时的计算
  • 应用在5秒内未响应用户的输入事件(如按键或者触摸)
  • BroadcastReceiver未在10秒内完成相关的处理
  • Service在特定的时间内无法处理完成 20秒

处理:

  • /data/anr/traces.txt 存放有ANR信息,通过ADB命令获取
  • 异步执行耗时操作(AsyncTask、Handler)
  • Activity onCreate、onResume避免进行耗时操作

Q:Android里面的Context

Context有三种类型:Application、Service、Activity。一个应用中的Context数量为:Context数量 = Activity数量 + Service数量 + 1
实现类:ContextImpl

大多数情况下,这三种Context可以通用,除了:

  • 启动Activity (加入new task标志)
  • 弹出Dialog (只能在一个Activity类型的Context弹出)

生命周期的区别:
ApplicationContext 是伴随着应用的生命周期,一些情况下需要传入ApplicationContext,防止内存泄漏

Q:Android的几种动画
Android 动画分为三种:属性动画、视图动画、帧动画

  • 视图动画(View Animation):作用于View上,XML/代码两种方式使用,没有改变View的属性(位置等,影响点击等事件)
  • 帧动画(Frame Animation):通过一系列图片播放达到动画效果,没有改变View的属性(位置等,影响点击等事件)
  • 属性动画(Property Animation):Android 3.0 之后引入,进行动画的同时也会改变相关控件的属性
  • 插值器 对动画的运行过程进行操作(匀速、先快后慢等等)(0~1变化的规律)
  • 估值器 对动画变化的结果进行操作(0~1变化时,每一个点应该是什么值)

示例:

视图动画:

// set代表一个动画集合

    
    
     
     // 缩放动画
    
    // 平移动画
    <translate 
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="100%"
        android:toYDelta="100%"
        android:duration="2000"
        />
set>

属性动画:

  • ValueAnimator 先改变值,再手动赋值给对应的View
  • ObjectAnimator 先改变值,再自动赋值给对应的对象

配合ObjectAnimator使用:估值器,推荐参考
属性动画使用:推荐参考

作用 资源ID 对应的Java类
动画加速进行 @android:anim/accelerate_interpolator AccelerateInterpolator
快速完成动画,超出再回到结束样式 @android:anim/overshoot_interpolator OvershootInterpolator
先加速再减速 @android:anim/accelerate_decelerate_interpolator AccelerateDecelerateInterpolator
先退后再加速前进 @android:anim/anticipate_interpolator AnticipateInterpolator
先退后再加速前进,超出终点后再回终点 @android:anim/anticipate_overshoot_interpolator AnticipateOvershootInterpolator
最后阶段弹球效果 @android:anim/bounce_interpolator BounceInterpolator
周期运动 @android:anim/cycle_interpolator CycleInterpolator
减速 @android:anim/decelerate_interpolator DecelerateInterpolator
匀速 @android:anim/linear_interpolator LinearInterpolator

Q:LRU缓存原理
LRU(Least Recently Used)

  • 核心思想:当缓存满时, 会优先淘汰那些近期最少使用的缓存对象。主要是两种方式:
  • LruCache(内存缓存):LruCache类是一个线程安全的泛型类:内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,并提供get和put方法来完成缓存的获取和添加操作,当缓存满时会移除较早使用的缓存对象,再添加新的缓存对象。
  • DiskLruCache(磁盘缓存): 通过将缓存对象写入文件系统从而实现缓存效果

Q:Android里几种异步机制

  • IntentService
  • Handler
  • AsyncTask
  • RxJava
  • runOnUiThread 切换到主线程

Q:Handler 和 Rxjava的异同

相同点:都是为了线程之间的通信
不同点:区别:
1.RxJava线程切换更方便,Handler需要在子线程去发送消息,在主线程去接受消息然后才能改变UI。
2.RxJava是观察者模式,Handler的消息队列用的是单链表

Q:事件分发机制
参考 Android事件分发机制详解:史上最全面、最易懂
从activity的dispatchTouchEvent->phoneWindow->frameLayout->viewGroup->View

三个关键方法:

  • public boolean dispatchTouchEvent(MotionEvent event)
  • public boolean onInterceptTouchEvent(MotionEvent ev)
  • public boolean onTouchEvent(MotionEvent event
dispatchTouchEvent onInterceptTouchEvent onTouchEvent
用于进行事件的分发,如果事件能传递到当前View,此方法一定会调用 存在于ViewGroup中,在dispatchTouchEvent方法中调用,判断是否拦截某个事件 用来处理点击事件
返回值:受当前View的onTouchEvent和下一个View的dispatchTouchEvent影响 true代表拦截 返回值:代表是否消耗此事件

关系伪代码

public boolean dispatchTouchEvent(MotionEvent event){
        boolean consume = false;
        if (onInterceptTouchEvent(event)){
            consume = onTouchEvent(event);
        }else {
            consume = child.dispatchTouchEvent(event);
        }
        
        return consume;
}

在事件分发机制里主要有dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent三个方法,分别的作用是分发事件、拦截事件、对事件的处理;
对于事件的分发:

  • Activity -> ViewGroup:
    事件分发从Activity开始,传递到ViewGroup再到View,当产生一个事件时,调用Activity的dispatchTouchEvent,里面调用了getWindow().superDispatchTouchEvent(ev),这里其实调用了mDecor.superDispatchTouchEvent(event),而mDecor是一个DecorView对象,DecorView的父类是继承FrameLayout,所以实际上是调用类ViewGroup的dispatchTouchEvent,这里将事件从Activity传递到ViewGroup
  • ViewGroup对事件的处理:对于一个根ViewGroup,当产生一个点击事件时,此时会调用dispatchTouchEvent方法,然后再dispatchTouchEvent方法里面会调用onInterceptTouchEvent方法,如果onInterceptTouchEvent返回true,那么这个ViewGroup的onTouchEvent方法会被调用,事件分发停止;
    如果onInterceptTouchEvent返回false(默认),则会将事件传递给子元素,子元素会调用它的dispatchTouchEvent,直到事件最终被处理;
  • ViewGroup -> View :
    如果子元素是View,则会调用子元素的onTouchEvent方法,onTouchEvent的返回结果就是dispatchTouchEvent的返回结果

对于事件的处理:
对于最后分发到的View,如果他的onTouchEvent方法返回false,那么会回调他的父容器的onTouchEvent。直到Activity
如果设置了OnTouchListener,那么会先进入OnTouchListener的onTouch方法,如果onTouch方法返回false,则会继续回调到onTouchEvent,如果返回true,则不会回调到onTouchEvent方法。并且OnClickListener也是处于onTouchEvent方法中的,由此可见他的优先级其实是最低的

Q:View的绘制流程

开发艺术之旅 | View的工作原理
x
Q:View重新绘制几个方法的区别

null requestLayout invalidate postInvalidate
工作线程 UI线程 UI线程 子线程
作用效果 重新确定位置,调用onMeasure和onLayout方法,也有可能会调用onDarw方法 重新绘制View,调用onDraw方法 和invalidate一样

Q:自定义View 需要重写哪几个方法?

  • onMeasure 调用该方法来检测View组件及它所包含的所有子组件的大小,在这可以实现wrap_content
  • onSizeChanged 当该组件的大小被改变时,在这可以获取控件的大小
  • onDraw 绘制时调用,需要实现padding可以在这实现
  • onTouchEvent 处理点击事件等
  • onAttachedToWindow 被添加到Window
  • onDetachedFromWindow 从Window移除,可以做一些销毁操作
  • 构造方法:自定义属性、渲染布局等

Q:为什么跨进程需要使用AIDL等机制?

在 Stub的asInterface 方法,如果是本地进程,会返回一个Stub对象本身;
如果不是同一进程,就会返回一个Stub.Proxy 对象,进而导致我们在ServiceConnection的onServiceConnected接收到的IBinder对象其实是一个BinderProxy,而不是Binder,所以不能转换成我们自己定义的Binder,会报错,需要用到AIDL或者Messenger等跨进程机制

Q:Stub.Proxy 如何实现跨进程调用?

  • Stub.Proxy 实现了我们定义的接口,但是他的实现方法是,通过调用Stub的transact方法,传入需要的参数、一个用于接收返回值的Parcel以及方法标识,最后Stub回调到onTransact方法。
  • 在onTransact方法会取出参数,调用Stub相应的方法,再把结果写入传入的Parcel;
  • 在onTransact执行完方法后,在Stub.Proxy相应的方法会取出结果并返回

Q:了解Binder机制吗
Binder机制流程图
2020Android初中级面试知识点记录——Android篇_第1张图片
Q:Android各版本适配

Android 6.0 —— SDK 23

  • 动态权限申请

Android 7.0 —— SDK 24(7.1 是 25)

  • 新版V2签名 打包只勾选V2版本会在7.0以下显示未安装
  • 跨应用共享文件(拍照相册等会涉及),需要用到FileProvider

Android 8.0 —— SDK 26 官方文档

  • 通知渠道适配:可以创建通知渠道组(可选)、渠道ID,发通知时指定渠道ID

  • 服务创建的适配:处于后台的应用(无可见的Activity、无可见的前台服务、无被其他应用关联)不可以启动一个后台服务;在后台的应用创建的后台服务需要用**startForegroundService()启动一个服务,并且在5s内在服务调用startForeground()**将应用推到前台,否则会ANR错误
    对于周期性的任务,官方推荐以“作业”的方式(JobScheduler);替代IntentService:JobIntentService(JobScheduler使用:推荐参考)

  • 广播的适配:禁止在其清单中为隐式广播注册广播接收器

Android 9.0 —— SDK 28

  • 默认禁用http,不允许明文流量( application标签下:android:usesCleartextTraffic=“true” 或者定义一个xml标签: android:networkSecurityConfig="@xml/network_security_config" )
  • 需要禁用 Apache HTTP 客户端
  • 启动前台服务需要加上权限
<network-security-config>
    
        
        
    
    <base-config cleartextTrafficPermitted="true"/>
network-security-config>

<manifest ... >
    <application
    
      ...
      android:networkSecurityConfig="@xml/network_security_config"
      android:usesCleartextTraffic="true">
      ...
      
		<uses-library android:name="org.apache.http.legacy" android:required="false"/>
            ...
    application>
manifest>

Q:布局性能优化

  • 减少View层级嵌套:使用merge 和 include搭配使用,减少嵌套层级
  • 布局复用:include 引入相同布局,布局模块化
  • 减少View的绘制次数:尽可能少用布局属性 wrap_content、使用ViewStub(只有被设置为可见时才会被加载)

Q:主线程Looper.loop()进入无限循环,为什么不会造成ANR

  • Android应用是由事件驱动,looper.loop()不断接收事件,Handler不断处理消息,如果没有消息,那么主线程就关闭了,应用也退出了。所以不是Looper.loop()阻塞了主线程,而是在处理事件的时候超过一定时间造成的ANR;
  • MessageQueue.next 会一直读取Message,直到有消息返回Message;而Looper.loop方法会调用MessageQueue.next获取Message,如果没有消息了就返回为NULL,退出循环

主线程中的Looper.loop()一直无限循环为什么不会造成ANR?

线程的阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。

Q:res文件夹下的资源会不会被编译成二进制编码?和asset的区别?

  • 共同点:
  • 二者文件夹下的文件都会被原封不动的打包到apk包中,不会被编译成二进制编码
  • 不同点:

获取方式

  • res 文件夹下的文件会被映射到R.java文件中,访问资源可以用过R.id 可以引用到资源文件
  • asset文件夹下的文件不会被映射到R.java,访问资源通过AssetManager
    res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹

目录结构

res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹

Q:XML的几种解析方式和对比
2020Android初中级面试知识点记录——Android篇_第2张图片
参考区别:链接
参考使用:链接

Q:图片加载框架对比

Glide

  • 多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)
  • 生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求)
  • 高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力)
  • 高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半)

Fresco

  • 最大的优势在于5.0以下(最低2.3)的bitmap加载。在5.0以下系统,Fresco将图片放到一个特别的内存区域(Ashmem区)
  • 大大减少OOM(在更底层的Native层对OOM进行处理,图片将不再占用App的内存)
  • 适用于需要高性能加载大量图片的场景

进阶

  • 应用的启动过程
  • rxjava 观察者(响应式编程…)
  • eventBus 委托模式
  • Glide生命周期管理

你可能感兴趣的:(Android,知识点总结)