欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android面试知识点汇总

程序员文章站 2024-03-23 12:53:52
...

四大组件:

Activity:

  • 生命周期:
  • 启动模式:
    • standard、singleTop、singleTask、singleInstance
    • 任务栈:前台任务栈、后台任务栈
    • TaskAffinity + singleTask
    • 使用adb查看任务栈信息
  • 启动方式:
    • 显式:intent.setClass()
    • 隐式:设置过滤信息:action、category、data类别,且同时匹配上述三类
  • 四种状态:
    • Active/Runing: 它处于可见并可和用户交互的**状态。
    • Paused: 仍然可见,但它已经失去了焦点故不可与用户交互。比如当 Activity 被另一个透明或者 Dialog 样式的 Activity 覆盖时的状态。
    • Stoped: 当 Activity 被另外一个 Activity 覆盖、失去焦点并不可见时处于 Stoped 状态。
    • Killed: Activity被系统杀死回收或者没有被启动时处于 Killed 状态。

Service:

  • 生命周期:

  • 如何提高service的生存率

    1. 在onStartCommand()中使用startforegound,将service变成前台进程(详见android的集中进程等级)
    2. 在onStartCommand中return sart_sticky
    3. 注册静态BroadcastReceiver,监听系统广播,然后判断Service状态
    4. 守护进程

BroadcastReceiver:

  • 普通广播、
  • 系统广播:系统内置的一些广播,比如监听网络变化、打开相机等等
  • 有序广播:
    • 接收者按照预先声明的优先级依次接收Broadcast;
    • 可以把数据存入结果对象中,传递给下一个接收者;
    • 可以被一种某一个接收者终止。
  • 本地广播(APP内)

ContentProvider:


Android进程(等级)

  1. foreground process 前端进程

    前端进程就是目前显示在屏幕上和用户交互的进程

    比如说:

    1. 顶层可交互的activity(已执行onResume);
    2. 有个Service,并绑定到跟用户正在交互的activity;
    3. 在Service里调用了startForground函数;
    4. 正在执行onReceive函数的BroadCastReceiver
  2. visible process 可见进程

    没有任何前台组件,但是仍然能影响用户在屏幕上看到东西。
    比如:

    • 如果一个activity在一个对话框运行之后仍然是可视的;
    • 输入法的弹出时。
  3. Service process 服务进程
    服务进程不会直接为用户所见
    比如在后台播放mp3或者从网上下载东西

  4. background process 后台进程
    比如:Activity执行了onStop

  5. empty process 空进程


数据持久化

SQLight:

- SQLite是一个轻量级的数据库,支持基本的SQL语法
- SQLiteDatabase的类,封装了一些操作数据库的api
    1. context.openOrCreateDatabase()方法创建SQLiteDatabase实例
    2. SQLiteDatabase实例调用insert()方法插入数据
    3. 调用query()方法查询数据
    4. 调用execSQL()方法执行SQL语句

SharedPreference:

- 是一种轻量级的数据存储方式,采用简直对的方式来存储数据。
- 其本质就是一个xml文件,一般位于/data/data/包名/shared_prefs/目录下。
- 由于内存中存在sharedPreference文件的缓存,所以在多进程的环境下,系统对它的读写不可靠。因此不建议用在IPC中

ContentProvider:

- Android系统中能实现不同应用间共享的一种数据存储方式。例如音频,视频,图片和通讯录,一般都可以采用此种方式进行存储
- 每个Content Provider都会对外提供一个公共的URI,应用程序通过这个URI来对数据进行操作。
- Content Provider天生支持跨进程访问,因此可以用于IPC

Android应用程序之间是通过哪些方式共享数据的?

File,Sqlite,Content Provider,BroadCast Receiver,Intent,同个Application内部的话还可以通过静态变量共享数据。


webView

加载

  1. 提高渲染的优先级

    webSettings.setRenderPriority(RenderPriority.HIGH);

  2. 把图片加载放在最后来加载渲染

    webSettings.setBlockNetworkImage(true);

  3. 使用硬件加速,该功能在Android 3.0 (API level 11)才加入。
    硬件加速可以在一下四个级别开启或关闭:Application、Activity、Window、View
    比如,在AndroidManifest.xml中添加android:hardwareAccelerated属性;关闭view的硬件加速myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

  4. 开启缓存
    设置websetting

js和java对象交互

  1. 获取webview控件的websetting
  2. 设置websetting.setJavascriptEnabled( true )
  3. 将一个对象暴露给JavaScript:webview.addJavascriptInterface。这个对象包含了JS调用的方法,这些方法用@JavascriptInterface修饰

  4. JS通过这些方法与Android交互

防止OOM

  1. 在代码中动态地将webview设置到布局中,而不是直接写到xml文件中;
  2. 在Activity的onDestory中销毁webview

线程相关

Linux线程基础

  • 线程与进程的区别
  • 线程同步
  • Linux线程通讯方式

ANR

- what
    - Activity 5s内无响应,BroadcastReceiver 10s内无响应
    - /data/anr/traces.txt 文件记录了ANR的信息
- why
- how

耗时任务或者线程间通讯

  • AsyncTask
    • 本质上是对 ThreadPool 和 Handler 的一个封装
    • 默认是串行的执行任务,可以调用executeOnExecutors()方法并行执行任务
  • Handler
  • IntentService

Handler

  • Handler + MessageQueue + Looper

MessageQueue本质上是一个单链表,不是Queue。采用FIFO方式管理,enqueueMessage()方法是将消息插入一条队列,next()方法是一个无限循环的方法。如果有消息,则取出,如果没有,就阻塞

  • HandlerThread

本质上是一个继承了Thread的线程类
通过创建HandlerThread**获取looper对象,传递给Handler对象,执行异步任务。在HandlerThread中通过**Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。创建HandlerThread后必须先调用start()方法,才能调用getLooper()获取Looper对象。

HandlerThread封装了Looper对象,使我们不用关心Looper的开启和释放的细节问题。如果不用HandlerThread的话,需要手动去调用Looper.prepare()和Looper.loop()这些方法。

IntentService

  • 原理:IntentService是一个抽象类,封装了HandlerThread和Handler,负责处理耗时的任务。任务执行完毕后会自行停止。在onCreate()方法中开启了一个HandlerThread线程,之后通过HandlerThread的Looper初始化了一个Handler,负责处理耗时操作。通过startService()方法启动,在handler中调用抽象方法onHandleIntent(),该方法执行完成后自动调用stopself()方法停止

  • override onHandleIntent() 方法

  • 优点:一方面不需要自己去创建线程,另一方面不需要考虑在什么时候关闭该Service

OOM

  • what
    OOM 和 内存泄漏 的区别

  • how

    • 静态变量持有Activity或Context对象
    • 非静态内部类的实例(默认持有外部类的引用)
    • 资源未关闭:file、stream、bitmap等
  • Handler造成OOM

    • 原因:使用(匿名)内部类实例化handler,默认持有context引用
    • 避免:静态内部类、Activity在onDestroy的时候,清空handler未处理的消息
  • WebView造成OOM


View相关

三部曲

三个核心步骤:Measure、Layout、Draw

Touch分发机制

重要

滑动冲突

简述Activity、Window、WindowManager、View、ViewRootImpl的作用和相互之间的关系

  • Activity不负责视图的控制,而是交给Window。这个Window本质上是一个PhoneWindow,被windowmanager管理。

  • Window中有decorview,decorview是当前视图的底层View,是setContentView所设置View的父View
    View是所有控件的基类。

  • ViewRoot对应ViewRootImpl,它是连接WindowManager和DecorView的纽带。绘制的三大流程都是在ViewRootImpl中完成的:从ViewRootImpl中的performTraversals开始,有三个方法performMeasure, performLayout, prformDraw分别对应measure,layout,draw三个流程,完成对*View的绘制。

  • 在父View的Measure过程中,会调用子View的Measure过程,如此反复,完成对整个View树的遍历。同理,在Layout和Draw中也是如此。

RecyclerView

  • 优点:

    • 封装了ViewHolder
    • 与ListView相比,耦合性更低、更加灵活:根据viewType设置不同的布局
    • 设置LayoutManager,实现ListView的功能和GridView的功能(支持 LinearLayoutManager 和 GridLayoutManager)
    • 支持局部刷新:notifyItemChanged()方法 (Listview用的BaseAdapter只有notifyDataSetChanged()方法)
  • 缺点:

    • 使用更加复杂
    • 没有onItemClickListener()、setOnItemLongClickListener()方法,只有OnItemTouchListener()方法
  • RecyclerView.Adapter

    • onCreateViewHolder()方法:产生一个ViewHolder对象,该对象中封装了view
    • onBindViewHolder()方法:根据传入的ViewHolder对象,显示数据
    • getItemViewType()方法:根据情况,返回不同的viewType,方便后续显示不同的布局和业务处理

IPC

Linux中IPC的方式:命名管道、信号量、共享内存

基础

  • 开启多进程的方式:给四大组件在Menifest文件中,添加process属性,指定进程名称
  • Android为每个进程分配一个独立的虚拟机,有不同的Application和地址空间
  • 不同进程访问同一个类的对象会有不同的副本。因此静态成员和单例模式失效、线程同步失效、sharedPreference可靠性降低。

序列化

  • Serializable接口:Java的序列化接口,使用简单,但开销大,序列化和反序列化需要大量IO操作

  • Parcelable接口:是Android的序列化方式,使用复杂,但效率高。

  • 对象是不能直接跨进程传输的。对象的跨进程传输,其本质是序列化和反序列化的过程

机制:Bundle、文件共享、ContentProvider、Socket、AIDL、Messager

  • 四大组件间,把数据封装到Bundle。在一个进程中开启另一个进程的Activity或者Service,就可以通过Intent把Bundle传递过去。其中,封装在Bundle中的数据需要能够被序列化

  • 使用文件共享方式,多进程读写一个相同的文件,获取文件内容进行交互。

  • 使用ContentProvider,常用于多进程共享数据,比如系统的相册,音乐等,我们也可以通过ContentProvider访问到

  • 使用Socket传输数据。服务端(比如一个进程中运行了一个Service)创建一个ServerSocket对象,监听本地的端口;客户端(比如另一个进程中运行的Activity)通过Socket连接本地的那个接口。经过TCP的三次握手后,建立连接。接着可以发送数据。使用socket不仅可以实现进程间通信,也可以实现设备间通信。

Binder

  • 基本原理:
    Android特有的IPC、客户端-服务器C/S的模式、

四个角色:Client、Server、ServiceManager、BinderDriver

调用过程:
1. Server向ServiceManager注册
2. Client通过ServiceManager获取Server的代理对象
3. Client向代理对象发起请求,该请求通过BinderDriver发送给Server处理
4. Server通过BinderDriver返回处理结果

  • 注意:客户端调用服务端的方法,被调用的方法运行在服务端的Binder线程池中,此时客户端被挂起。因此此时需要避免ANR。(AIDL和Messager同理)

  • Binder连接池

在一个应用有多个使用AIDL的场景,无需为每一个AIDL创建自己的Service。而是使用一个Service,创建并返回一个Binder连接池的Binder对象。Activity在使用AIDL的时候,可以通过该Binder连接池对象,获取不同的Binder对象(类似于工厂模式)

AIDL

  • 使用流程:以Activity(进程1)和Service(进程2)通信为例
    • 创建AIDL接口,Build一下,产生相关代码
    • 创建IBinder实例,即实例化xxx.Stub()抽象内部类,override抽象方法
    • 创建Service,在onBind()中,把上述IBinder实例返回
    • 在Activity中调用bindService启动Service,然后在ServiceConnection中的onServiceConnected方法回调中获得该IBinder实例。
    • Activity调用该实例的方法,实现通信

Messager

  • 一种轻量级的跨进程通讯方案,底层使用AIDL实现。

  • 是一种串行的通信,即服务端需要一个一个处理消息。因此,在大量并发请求的情况下,用Messager就不太合适。

  • 使用流程:以Activity(进程1)和Service(进程2)通信为例

    1. 在Service中new一个Messenger(这个Messenger需要指定Handler)
    2. 然后在onBind函数中,返回messenger的Binder对象(messenger.getBinder())
    3. 在Activity中,通过bindService启动service,通过ServiceConnection获取到Binder对象。
    4. 通过这个Binder对象实例化一个Messenger,然后messenger.send(message)进行通信

启动流程

Android开机流程

init进程-zygote进程-SystemServer进程-各种ManagerService(AMS,PMS,WMS)- launcher程序

App启动流程

launcher-AMS-(pause)-zygote-新进程ActivityThread-(main函数)-向AMS注册-通知ActivityThread创建Activity并执行生命周期

App内Activity启动流程

Activity1-AMS-(pause)-在同一个ActivityThread-加载Activity2类,执行生命周期

ActivityManagerService 和 Instrument 的区别


性能及优化

apk包大小

1、减少不必要的jar包依赖
2、优先使用代码来设置UI效果
3、去除没用到的资源文件,压缩其他资源文件的大小,不用适配所有尺寸的设备
4、尽量重用代码,避免代码的冗余
5、限制app支持的cpu架构的数目:在当前的Android 生态系统中,让你的app支持 armabi 和 x86 架构就够了;

方法数越界 multiDex方案

  • what:dex是Android平台上(Dalvik虚拟机)的可执行文件, 相当于Windows平台中的exe文件, 每个Apk安装包中都有dex文件。

  • 单个dex文件所包含的最大方法数是65536,包含Android Framwork、依赖的jar包,以及应用本身的所有方法。

  • 解决方法数越界:

    1. 删除无用的代码和第三方库
    2. 采用插件化机制,动态加载dex。这是一个重量级的方案
    3. multiDex方案——可以从apk中加载多个dex文件
  • 基本使用:

    1. 配置Gradle,添加 multiDexEnabled true
    2. 添加multiDex依赖
    3. 在Application中添加 MultiDex.install(this) 代码

其他

目标:

- 快:流畅
- 稳:稳定
- 省:省电、省流量
- 小:安装包小

优化方案:

  • 布局优化:

    • 减少View树的层数
    • 合理使用优先使用FrameLayout和LinerLayout,减少使用RelativeLayout
    • 布局复用,使用标签
  • OOM优化

  • ANR优化

  • ListView(GridView)优化

    • 使用viewholder,进行view复用
    • 不要在getview()中进行耗时操作
  • Bitmap优化

    • 图片压缩
    • 缓存(核心):内存缓存和磁盘缓存、LRU算法

架构:本质上都是一种代码架构思想

MVC

其中M层处理数据,业务逻辑等;V层处理界面的显示结果;C层起到桥梁的作用,来控制V层和M层通信
视图层(View):一般采用XML文件进行界面的描述,这些XML可以理解为AndroidApp的View。
控制层(Controller):Android中由Activit、Fragment承担,负责逻辑处理
模型层(Model):提供数据,从进行数据库或者网络的操作。

缺点:在Android开发中,Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界面,接受并处理来自用户的操作请求,进而作出响应,既是view层,又是controller层。随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。

MVP

  • MVP框架由3部分组成:

    • Model:提供数据,从进行数据库或者网络的操作
    • View:对应于Activity/Fragment等View,主要负责UI显示
    • Presenter:是Model和View之间的桥梁,进行逻辑处理。View并不能直接对Model进行操作
  • 优点:将在Activty中的大量逻辑操作放到Presenter控制层中,避免Activity的臃肿。

  • 缺点:MVP模式需要多写许多新的接口;过于复杂的逻辑会使得Presenter臃肿

  • 实现方法:

    • 定义IView接口,Activity实现IView接口,然后在方法中更新UI;
    • 在Presenter中维持IView的一个引用;
    • 在Activity中实例化Presenter,然后将IView的实例(即this)赋值给Presenter。
    • 在Model中做具体的操作,Presenter获取具体的结果,通过调用所因为的View的方法,更新UI。

MVVM

  • Model,View和ViewModel
    • Model:提供数据,从进行数据库或者网络的操作
    • View:应于Activity/Fragment等View,主要负责UI显示;
    • ViewModel是负责逻辑处理;Model提供数据。ViewModel和View之间通过绑定,使得耦合度进一步降低

AAC(Android Architecture Components,架构组件)

  • LiveData:

    • 使用观察者模式,可以与控件绑定,监听数据的改变刷新UI。
    • 可以感知控件的生命周期,在控件销毁时自动取消注册,因此也不会产生内存泄漏
  • ViewModel:将视图的数据和逻辑从具有生命周期特性的实体(如 Activity 和 Fragment)中剥离开来。比如 AndroidViewModel(ViewModle的子类)

  • Room:官方数据库框架,对原生的SQLite API进行了一层封装。

    • 与SQLite相比:对于复杂的数据库结构,SQL使用复杂,代码冗长、管理困难;Room,使用简单、易于管理

MVVM和AAC

个人理解:MVVM是一种思想,AAC提供多种工具。利用AAC中的工具实现MVVM的思想

View:

ViewModel:

Model:

  • 橘黄色框的Repository及其下都是Model层。一个Repository数据仓库负责通过不同方式获取同类型的数据。
  • 数据来源有:
    • 本地存储数据,如数据库,文件,SharedPreferences(本质也是文件)
    • 内存的缓存或临时数据
    • 通过各种网络协议获取的远程数据
  • ViewModel在从Repository获取数据时,不需关注数据具体是怎么来的。

Android面试知识点汇总


响应式编程

RxJava/RxAndroid

基于观察者模式,可以方便地以流的方式处理异步事件

  1. 创建:
    Observable.create/just/from

  2. Schedulers线程调度

    • 在不指定线程的情况下, RxJava 遵循的是线程不变的原则

    • subscribeOn():

      • 指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被**时所处的线程。或者叫做事件产生的线程。

      • subscribeOn() 的位置放在哪里都可以,但它是只能调用一次的。

    • observeOn():

      • Subscriber 所运行在的线程,或者叫做事件消费的线程。
      • 指定的是它之后的操作所在的线程,以此实现线程的多次调用
    • 举例:
      AndroidSchedulers.mainThread();
      Schedulers.single();
      Schedulers.newThread();
      Schedulers.computation();
      Schedulers.io();
  3. 变换 操作符

    1. map():将发射的每一项数据都用一个函数进行变换
    2. flapMap():
  4. 背压

在异步场景下,被观察者的发送速度远远大于观察者的处理速度。

  • Observable不支持背压

  • Flowable支持背压:
    - 背压策略:MISSING、ERROR、BUFFER、DROP、LATEST

Retrofit

是一个网络请求框架,底层依赖OkHttp

  • 首先,Retrofit将Http请求封装为Java接口;
  • 然后,网络请求交给OkHttp处理;
  • 最后,OkHttp将返回的结果交给Retrofit解析

支持两种模式:callback、RxJava/RxAndroid


其他

JIN

由于Java的跨平台的特性,导致Java同本地交互的能力不够强大,一些和操作系统相关的操作无法完成。通过JNI可以调用C和C++的代码,提高自己的本地交互能力

JNI开发流程:

  1. 在Java中编写native方法
  2. 将Java编译成class文件,然后导出JNI的.h头文件。
  3. 用C或者C++实现java中声明的JNI方法
  4. 编译.so库文件,然后在java中调用

设计一个图片加载类

  • 图片同步、异步加载方式
  • 图片压缩(使用BitmapFactory) —— 降低OOM的风险
  • 缓存 —— 内存缓存LruCache、磁盘缓存DiskLruCache。两级缓存降低了网络访问次数,减少了流量消耗
  • 网络拉取 —— 从网络加载图片。
  • View复用
  • 调用过程:从内存缓存中读取 —— 从磁盘缓存中读取 —— 从网络中拉取

画出一个项目中网络请求的流程图

第三方库源码解析:

OkHttp

说说XML、JSON、GSON有什么样的联系

XML全称叫做可扩展标记语言,它的结构相对简单,数据共享比较方便。但是对于一些比较复杂的数据,XML文件格式复杂,解析的代价大。
JSON的数据格式比较简单,易于读写。但是目前还没有XML应用广泛。
GSON的Google的一个开源库。这个开源库可以很方便地将JSON数组转换为对象,这在开发中简化了将JSON的字段转换为属性的步骤。

Android访问权限

apk下载到cache目录,只有 rw 权限,没有 x 权限,所以无法安装

Runtime.getRuntime().exec()
执行 chmod 命令,修改为rwx


APM

Android性能信息

  • CPU信息:/proc/cpuinfo
  • Memory 信息:/proc/meminfo

异常捕获:

UncaughtExceptionHandler:
android全局异常捕获器——在Application中调用**Thread.setDefaultUncaughtExceptionHandler
(uncaughtExceptionHandler)**,定制自己的错误日志系统。比如在发生Exception时,记录exception的堆栈信息。

监控Activity生命周期

在application中,调用registerActivityLifecycleCallbacks(),通过实现了ActivityLifecycleCallbacks接口的实例,完成对Activity各个生命周期信息的采集。比如哪个Activity在什么时间处于onCreate、onStop等等,进而统计出Activity的使用时间和使用次数。

对于Fragment的信息采集

SDK提供不同接口,分别对应Fragment的各个生命周期,进而采集信息。使用时,需要用户在Fragment的生命周期中的各个环节中,调用对应的接口。
会不会使用麻烦?会的,解决方法就是:在SDK中封装一个Fragment的子类,在这个子类中按照上述方法采集信息。用户在使用SDK过程中,可以直接继承使用这个子类,而不是继承使用Fragment。

对HTTP接口的监测

采用插装的办法:SDK提供两个接口,用户在发起HTTP请求时,调用第一个接口,可以记录下url和时间。在结束HTTP时,调用第二个接口,记录下url、时间和返回码。这两组记录就完成了对HTTP接口的数据采集?
会不会麻烦?会的,但是这种方法很通用,其他商用的SDK中也是这种方法。或者使用Gradle插件,在编译的时候,将上述代码自动插入项目中。

错误信息上报机制:

方案一:发现错误后立即上报。优点:实时性好;缺点:不能保证每一条信息都能上报成功。
方案二:发生错误后,记录在本地,当第二次启动App的时候,上报上一次的信息。

接口加密、token原理

相关标签: android 面试

上一篇: 华为面试汇总

下一篇: