美文网首页面试
Android面试题集(二)

Android面试题集(二)

作者: 搬码人 | 来源:发表于2022-02-27 11:50 被阅读0次

    什么是support library?为什么要引入support library?

    • support library引入主要是因为Android版本有很多,新版本的更新也快,每个版本都会有一个开发版本号
    • 在进行Android开发的时候,我们通常需要考虑,应该选择什么样的API级别进行开发
    • Google为此也给出了解决方案,我们在开发过程中可以给出三个设置项:minsSdkVersion<=targetSdkVersion<=compileSdkVersion
    • 当我们targetSdkVersion为24,需要使用API28的新增功能时,这时需要向后兼容,使用低版本的API兼容高版本的API,而支持库就是这个作用,它会跟着每个新发布的API级别同步发布,所以这里支持库我们选择与compileSDKVersion一样的版本即可。support library只能保证我们针对24开发的APP在28上能正常运行,但运行效果不一定相同。

    什么是ANR?如何避免ANR?

    • ANR(Application Not responding),是指应用程序未响应,Android系统对于一
      些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或
      者响应时间过长,都会造成ANR
    • 场景
      Service Timeout
      BroadcastQueue Timeout
      ContentProvider Timeout
      KeyDispatching Timeout
    • 超时时间的计数一般是从按键分发给app开始。超时的原因一般有两种:
      (1)当前的事件没有机会得到处理(即UI线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)
      (2)当前的事件正在处理,但没有及时完成
    • 如何分析ANR
      ANR产生的原因是要有输入事件,若用户没有输入任何事件,即使主线程阻塞了,也不会ANR,因为InputDispatcher没有分发事件给应用程序,当然也不会检测处理超时和报告ANR了
      1)ANR发生时都会在log中输出错误信息,从log中可以获得ANR的类型,CPU的使用情况:
      CPU使用率过高有可能是CPU饥饿导致了ANR;
      CPU使用率过低说明主线程被block了;
      如果IOwait高是因为主线程进行I/O操作造成的。

    2)除了看LOG,还可以从trace.txt文件查看调用stack。可以用如下命令获取trace.txt

    $chmod 777 /data/anr
    $rm /data/anr/traces.txt
    $ps
    $kill -3 PID
    adbpull data/anr/traces.txt ./mytraces.txt
    
    • 如何避免ANR
      1、主线程尽量只做UI相关的操作,避免耗时操作,比如过度复杂的UI绘
      制,网络操作,文件IO操作;
      2.、避免主线程跟工作线程发生锁的竞争,减少系统耗时binder的调用,
      谨慎使用sharePreference,注意主线程执行provider query操作

    总之,尽可能减少主线程的负载,让其空闲待命,可随时响应用户的操作。

    Bitmap如何优化,三级缓存的思想和逻辑

    BitmapFactory.Options

    • inPreferredConfig
      设置图片解码后的像素格式,如ARGB_8888/RGB_565
    • inSampleSize
      设置图片的采样率进行图片的缩放显示。
      比如值为2,则加载图片的宽高是原来的 1/2,整个图片所占内存的大小就是1/4
    • 三级缓存
      三级缓存的原理就是当 App 需要引用缓存时,首先到内存缓存中读取,读取不到再到本地缓存中读取,还获取不到就到网络异步读取,读取成功之后再缓存到内存和本地中。

    内存溢出(Out Of Memory)是怎么发生的?

    • 产生的原因
      1、大量的内存泄漏
      2、频繁申请内存得不到及时的回收
    • 减少OOM的概率
      1、尽可能减少内存泄漏的发生
      2、尽可能不在循环中申请内存
      3、尽可能不在调用次数多的函数中申请内存

    如何理解上下文Context

    • 理解Context
      1、可以理解为“上下文”:它贯穿整个应用;
      2、也可以理解为“运行环境”:它提供了一个应用运行所需要的信息,资源,系统服务等
      3、可以理解为“场景”:用户操作和系统交互这一过程就是一个场景,比如Activity之间的切换,服务的启动等都少不了Context。
    • 不同Context(Activity、Application、Service)的区别
      Activity和Application中的Context大多数情况下两者可以通用。在涉及界面跳转的情况下尽量使用activity的context,其提供了默认的任务栈。而applicationContext没有提供任务栈,需要自己创建新的task
      除了Activity可以创建Dialog,其他都不可以创建Dialog。
    • Context内存泄漏问题
      1、静态资源导致的内存泄漏
      2、单例模式导致内存泄漏

    onTrimMemory()方法是什么?

    能让Activity得到内存情况的通知

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
        switch (level) {
        case TRIM_MEMORY_UI_HIDDEN:
          // 进行资源释放操作  
            break;  
        }  
    }  
    
    • 运行时的回调
      1、TRIM_MEMORY_RUNNING_MODERATE:表示应用进程正常运行,并且不会被杀掉。但是目前手机的内存已经有点低了。
      2、TRIM_MEMORY_RUNNING_LOW:表示应用进程正常运行,并且不会被杀掉。但是目前手机内存已经非常低了,我们应该释放掉一些不必要的资源以提升系统的性能。
      3、TRIM_MEMORY_RUNNING_CRITICAL:表示应用进程仍然正常运行,但是系统已经根据LRU缓存规则杀掉了大部分缓存的进程。
    • 缓存的回调
      1、TRIM_MEMORY_UI_HIDDEN:UI组件全部不可见的时候才会触发,一旦触发就说明用户已经离开了我们的程序。
      2、TRIM_MEMORY_BACKGROUND:表示手机目前内存已经很低了,系统准备开始根据LRU缓存来清理进程。这个时候我们的程序在LRU缓存列表的最近位置,是不太可能被清理掉的。
      3、TRIM_MEMORY_MODERATE:表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的中间位置。
      4、TRIM_MEMORY_COMPLETE:表示手机目前内存已经很低了,并且我们的程序处于LRU缓存队列的最边缘位置。

    动态代理机制你懂吗?

    • 为什么使用动态代理
      1、一个静态代理只服务一种类型的目标对象,若要服务多类型的目标对象,则需要为每一种类对象提供静态代理对象
      2、在目标对象较多的情况下,若采用静态代理,则会出现静态代理对象量多、代码量大,从而导致代码复杂的问题
    • 实现原理
      设计动态代理类时,不需要显示实现与目标对象类相同的接口,而是将这种实现推迟到程序运行时由JVM来实现,即:在使用时再创建动态代理类和实例。通过Java的反射机制的method.invoke(),通过调用动态代理类对象方法,从而自动调用目标对象的方法。
      而静态代理则是在代理类实现时就指定与目标对象类相同的接口。
    • 优点
      更加灵活、不需要事先实例化
    • 缺点
      效率低,因为用到了Java反射机制
      只能动态代理实现了接口的类(只能针对接口创建代理类,不能针对类创建代理类)
    • 应用
      Retrofit就是一个经典的动态代理

    Android动画种类和区别

    • 补间动画:包括移动、渐变、伸缩、旋转,有XML和Android代码两种定义方式,建议使用XML,重用性更好。补间动画只是视觉效果,控件的属性并没有发生变化。
    • 帧动画:帧动画FrameAnimation,顾名思义就是一帧一帧的变化。
    • 属性动画:视觉效果和补间动画差不多,不过属性动画是真实的属性发生了变化。
      介绍一下View的属性成员:
      1、translationX,translationY:控制View的位置,值是相对于View容器左上角坐标的偏移。
      2、rotationX,rotationY:控制相对于轴心旋转。
      3、x,y:控制View在容器中的位置,即左上角坐标加上translationX和translationY的值。
      4、alpha:控制View对象的alpha透明度值。

    Android中如何处理耗时操作操作

    • 常见方法
      Android中处理耗时操作的基本思路为将耗时的操作放到非UI线程执行,常用的是
      1、AsyncTask
      2、Handler
      3、Thread
      4、Executors
    • 非UI线程中的处理思路
      一种是每次耗时操作启动一个线程,代价比较大。
      一种是将所有的一部分放入队列,由一个或几个线程去执行,例如HandlerThread和AsyncTask。
    • AsyncTask
      1、cancel方法实现不是很好,如果你调用了AsyncTask的cancel(false)方法,doInBackground()仍然会执行到方法结束,只是不会去调用onPostExecute()方法,但是实际上也是让程序执行了没有意义的操作。如果调用cancel(true),mayInterruptIfRunning设置为true,会使任务尽早结束,但是如果doInBackground()有不可被打断的方法,就会失效,比如BitmapFactory.decodeStream()操作。
      2、内存泄漏,在Activity中使用非静态匿名内部类AsyncTask类,由于Java内部类的特点,内部类持有外部类引用,而由于AsyncTask生()的生命周期可能比Activity的长,当Activity销毁时,AsyncTask还在执行,由于AsyncTask持有Activity的引用,导致Activity对象无法回收,进而产生内存泄漏。
      3、结果丢失,当屏幕旋转等造成Activity重新构建时AsyncTask数据丢失的问题,当Activity销毁并创建新的时,还在运行的AsyncTask会持有一个 Activity的非法引用,即之前的Activity的实例,导致onPostExecute方法失效。
      4、串行、并行多版本不一致,1.6之前为串行,1.6-2.3为并行,3.0之后又改为串行,但是可以通过executeOnExecutor()实现并行处理。

    1、线程池有限,太多内容同时异步会超时,甚至会出现ANR。
    2、AsyncTask可能存在新开大量线程消耗系统资源和导致应用FC(强制关机)的风险。

    Activity、View和Window之间的关系

    • Activity:是Android四大组件之一,是存放View对象的容器,也是我们界面的载体,可以用来展示一个界面。它有一个SetContentView()方法,可以将我们定义的布局设置到界面上。
    • View:就是一个个视图的对象,实现了KeyEvent.Callback和Drawable.Callback。
    • Window:是一个抽象类,是一个顶层的窗口,它的唯一实例是PhoneWindow,它提供标准的用户界面策略,如背景、标题、区域,默认按钮处理等。
      Activity与Window相关概念
    • Activity只负责生命周期和事件处理
    • Window只控制视图
    • 一个Activity包含一个Window,如果Activity没有Window,那就相当于Service
    • AMS统一调度所有应用程序的Activity
    • WMS控制所有Window的显示与隐藏以及要显示的位置

    Activity是View的框架容器,而PhoneWindow是对View的操作者。


    三者关系图

    Glide框架

    EngineJob管理DecodeJob,DecodeJob负责解码与加载,可以将DecideJob理解为一个异步线程,而EngineJob就是一个线程池。
    缓存:分为内存缓存和磁盘缓存两大类。磁盘缓存:Resource(资源类型)保存曾被解过码、转换并写入磁盘缓存,比如手机上显示的100×100的图片;而Data(数据来源)则保存的原图。
    如果缓存不存在那么将执行加载。

    原理解析1 原理解析2

    如何排除应用崩溃的原因?

    如果在做开发,那么只需要logcat看报错信息即可
    如果作为用户

    • 确定重点
      1、Java崩溃。Java崩溃类型比较明显。比如NullPointException是指空指针,OutOfMemoryError是资源不足,这时候需要去进一步查看日志中的“内存信息”和“资源信息”。
      2、Native崩溃。需要观察signal、code、fault addr等内容,以及崩溃时Java的堆栈。关于各signal含义的介绍,你可以查看崩溃信号介绍。比较常见的有SIGSEGY和SIGABRT。前者一般是由于空指针、非法指针造成,后者主要是因为ANR和调用abort()退出所导致。
      ANR。先看看主线程的堆栈,是否是因为锁导致的。接着看看ANR日志中iowait、CPU、GC、system server等信息,进一步确定是I/O问题还是CPU竞争问题,亦或是由于大量GC导致卡死。
    • 查找共性
      如果使用上面的方法还是不能有效定位问题,我们可以尝试查找这类崩溃有没有什么共性。找到了共性,也就可以进一步找到差异,离解决问题也就更进一步。机型、系统、ROM、厂商、ABI,这些采集到的系统信息都可以作为维度聚合,共性问题例如是不是只出现在小米的手机,是不是只有小米8这个机型,是不是只在Android10的系统上。应用信息也可以作为维度来聚合,比如正在打开的链接、正在播放的视频、国家、地区等。
      找到了共性,可以对你下一步复现问题有更明确的指引。
    • 尝试复现
      如果我们大概知道了崩溃的原因,为了进一步确认更多信息,就需要尝试复现崩溃。如果我们对崩溃完全没有头绪,也希望通过用户操作路径来尝试复现,然后再去分析崩溃的原因。
      “只要能本地复现,我就能解”,相信这是很多开发和测试者说的话。有这样的底气主要是因为在稳定的复现路径上面,我们可以采用增加日志或使用Debugger、GDB等各种各样的手段或工具做进一步分析。
      我们可能会遇到各种各样的奇葩问题。比如某个厂商改了底层实现、新的Android系统实现有所更改,都需要去Google、翻源码,有时候还需要去抠厂商的ROM或手动刷ROM。很多疑难问题需要我们耐得住寂寞,反复猜测、反复验证。
    • 总结
      崩溃攻防是一个长期的过程,我们尽可能地提前预防崩溃的发生,将它消灭在萌芽中。作为技术人员,我们不应该盲目追求崩溃率这一个数字,应该以用户体验为先,如果强行去掩盖一些问题往往更加适得其反。我们不应该随意使用try catch去隐藏真正的问题,要从源头入手,了解崩溃的本质原因,保证后面的运行流程。

    Android分层框架

    Java应用程序无需过多解释,基本可以理解为各个App,由Java语言实现。
    Java框架层就是常说的Framework,这层里东西很多也很复杂,比如说主要的一些系统服务如ActivityManagerService、PackageManagerService等,编写Android代码之所以能够正常识别和动作都要依赖这一层的支持,这一层也是由Java语言实现。
    Native这一层常见一些本地服务和一些链接库等。这一层的特点就是通过C和C++来实现。比如我们现在要执行一个复杂运算,如果通过Java去实现,那么效率会非常低,此时可以选择C或C++去实现,然后和上层的Java代码通信(这部分在Android中称为JNI机制)。又比如我们的设备需要运行,那么必然要和底层的硬件驱动交互,也要通过Native层
    Linux内核空间,顾名思义就是Kernel(内核)部分。提供了进程管理、文件网络管理、系统安全权限管理、以及系统与硬件设备通讯基础

    Dalvik和ART的区别

    • 什么是Dalvik
      Dalvik是Google公司设计用于Android平台的Java虚拟机。支持已转换为.dex(Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统。
    • 什么是ART
      Android Runtime,Android4.4中引入的一个开发者选项,也是Android5.0及更高版本的默认模式。在应用安装的时候Ahead-Of-Time(AOT)预编译字节码到机器语言,这一机制叫Ahead-Of-Time(AOT)预编译。应用程序安装会变慢,但是执行将更有效率,启动更快。
    • 区别
      1、Dalvik环境中,应用每次运行时,字节码都需要通过即时编译器(Just In Time,JIT)转换为机器码。ART环境中,应用会在安装的时候就将字节码预编译(Ahead Of Time,AOT)成机器码,使其成为真正的本地应用。
      2、ART占用的空间比Dalvik大,就是用空间换时间。
      3、ART不用每次运行时都重复编译,减少了CPU的使用频率,降低了能耗。
      注:支持ART的软件较少)

    Dalvik和JVM的区别

    1、JVM直接运行.class文件,而Dalvik直接运行.dex文件。.dex可以看作一些.class文件压缩的文件
    2、JVM的指令集是基于的,而Dalvik的指令集是基于寄存器

    什么是JNI?JNI的作用?

    • 定义:Java Native Interface,即Java本地接口
    • 作用:使得Java与本地其他类型语言(如C、C++)交互
    • JNI是Java调用Native的一种语言特性
    • JNI是属于Java的,与Android无直接关系
    • 现实中的驱动都是C/C++开发的,通过JNI,Java可以调用C/C++实现驱动,从而扩展Java虚拟机的能力。另外,在高效率的数学运算、游戏的实时渲染、音视频的编码和解码等方面,一般都是用C开发的

    什么是NDK?它有什么用?

    NDK(C/C++)SDK(Java)性质是一样的

    • 定义:Native Development Kit,是Android的一个工具开发包
    • 作用:快速开发C、C++的动态库,并自动将so(C++函数库)和应用一起打包成APK
    • 在Android的场景下使用JNI
    • 提供了把.so和.apk打包的工具
    • NDK提供的库有限,仅拥有算法效率和敏感的问题
    • 提供了交叉编译器,用于生成特定的CPU平台动态库
      特点:
    • 运行效率高
    • 代码安全性高
    • 功能扩展性好
    • 易于代码复用和移植

    NDK和JNI的关系

    • JNI是实现Java调用C/C++的途径,NDK是Android中实现JNI的手段
    • 即在Android的开发环境中,通过NDK从而实现JNI的功能
    • Java的优点是跨平台,和操作系统之间的调用由JVM完成,但是一些和操作系统相关的操作就无法完成,JNI的出现刚好弥补了这个缺陷,也完善了Java语言,将Java扩展得更加强大。

    AAPT是什么?

    AAPT(Android资源打包工具,Android Asset Packaging Tool)是一种编译工具,供Android Studio和Android Gradle Plugin用于编译和打包应用资源。而AAPT2是在AAPT上做了优化。Android Gradle Plugin3.0.0或者更高版本默认开启AAPT2。AAPT2会解析资源、为资源编索引并将资源编译为针对Android平台进行过优化的二进制格式。

    • AAPT2支持通过启用增量编译实现更快的资源编译。将资源处理拆分为两个步骤来实现:
      1、编译:将资源文件编译为二进制格式(.flat)。
      2、链接:合并所有已编译的文件并将它们打包到一个软件包中。
      这种拆分方式有助于提高增量编译的性能。例如,如果一个文件发生了改变,则只需要重新编译这个文件。
    • 编译
      AAPT2支持编译所有Android资源类型,例如图片文件和XML文件。调用AAPT2进行编译时,应为每次调用传递一个资源文件作为输入。然后,AAPT2会解析文件并生成一个扩展名为.flat的中间二进制文件。
      AAPT2输出的文件不是可执行文件,必须在链接阶段添加这些二进制文件作为输入来生成APK。但是,所生成的APK文件不是可以安装在Android设备上的,因为它不包含DEX文件且未签名。


      image.png

    APK打包流程

    .aidl:用于进程间通信
    aapt只是将xml的id进行二进制转换,以方便调用,并没有将xml内容本身转换。
    dx.bat将.class文件压缩成.dex文件
    只有完成签名应用才能运行,自己测试时是以debug开头,商业化的话是release开头


    image.png

    什么是SurfaceView

    View中显示一些需要长时间绘制的画面 ->出现卡顿 ->怎么办?

    • SurfaceView
      View的子类
      是一个专门用于做频繁绘制的View子类,它的绘制操作是在子线程中执行,所以频繁绘制不会阻塞线程,使用它去做一些需要频繁绘制和长时间绘制效果会好很多
    • SurfaceView的两个特点
      1、 子线程绘制
      surfaceView是在一个新起的单独线程中,可以重新绘制画面,而View必须在UI的主线程中更新画面。那么在UI的主线程中更新画面可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞
      但这也带来了另外一个问题,就是时间同步。比如你触屏了一下,你需要surfaceView中thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍微复杂一点,因为涉及到线程同步
      2、双缓冲模式
      通俗来讲就是两个缓冲区,一个后台缓冲区和一个前台缓冲区,每次后台缓冲区接收数据,当填充完整后交换给前台缓冲区,这样就保证了前台缓冲里的数据都是完整的。
      在后台线程执行繁重的绘图任务,把所有绘制的东西缓存起来;绘制完毕后,再回到UI线程,一次性把所有绘制的东西渲染到屏幕上(本质:就是后台线程绘制,UI主线程渲染)

    Android中你可以使用什么来进行后台操作?

    现有的后台操作机制

    • Service(前台服务、后台服务)
    • 线程池
      线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。
    • WorkManager(JobScheduler,Firebase JobDispatcher,自定义AlarmManager)
      选择时应考虑的三个因素
    • (线程池)此项工作是否可以延迟或者在预定时间内完成,如果需要从网络获取一些数据以响应用户单击按钮,则必须立即完成该工作;但是如果想将日志上传到服务器,那么可以推迟这项工作,因为这不会影响应用程序的性能或用户期望。
    • (Service)此项工作是否需要采取保活机制长存于系统中,比如对于解码和显示图片来说解码和显示位图只需要在应用程序处于前台并且进程处于活动状态时进行;但是对于地图定位、音乐播放器来说即使应用程序处于后台并且并未被主动使用也需要持续运行。
    • (WorkManager)此项工作是否为了响应系统某种机制(网络状态,电池状态,存储级别、开关机等等)而触发运行的,例如退出飞行模式后马上与服务器通信,在这种情况下,如果应用程序进程已死,你需要重新启动并完成通信。

    Okhttp、Retrofit的区别于联系

    Retrofit是对OkHttp的进一步封装框架

    LayoutInfater.inflate的函数意义与参数说明

    • 作用:解析xml文件的布局


      image.png
      image.png

    Android中的进程类型

    1、前台进程:即当前正在前台运行的进程,说明用户当前正在与通过该进程与系统进行交互,所以该进程为最重要的进程,除非系统的内容已经到不堪重负的情况,否则系统是不会将改进程终止的。
    2、可见进程:一般还是显示在屏幕中,但是用户并没有直接与之进行交互,该进程对用户来说同样是非常重要的进程,除非为了保证前台进程的正常运行,否则Android系统一般是不会将该进程终止的。
    3、服务进程:便是拥有Service进程,该进程一般是在后台为用户服务的。一般情况下,Android系统是不会将其中断的,除非系统的内容以及达到崩溃的边缘,必须通过释放该进程才能保证前台进程的正常运行时,才可能将其终止。
    4、后台进程:一般对用户的作用不大,缺少该进程并不会影响用户对系统的体验。所以如果系统需要终止某个进程才能保证系统正常运行,那么会有非常大的几率将该进程终止。
    5、空进程:对用户没有任何作用的进程,该进程一般是为缓存机制服务的,当系统需要终止某个进程保证系统的正常服务时,会首先将该进程终止。

    相关文章

      网友评论

        本文标题:Android面试题集(二)

        本文链接:https://www.haomeiwen.com/subject/kjrvlrtx.html