安卓面试题整理2(新增内容)

内存泄露场景:

handler使用不当内存泄露。

静态变量持有Context 强引用 。

匿名内部类引起的内存泄露,最典型的例子就是Handler泄漏。

Stream io流,bitmap等使用后未关闭或者注销。

eventbus、广播等未关闭注册。

Android 中的 WebView 存在很大的兼容性问题,有些 WebView 甚至存在内存泄露的问题。所以通常根治这个问题的办法是为 WebView 开启另外一个进程,通过 AIDL 与主进程进行通信, WebView 所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放。

讲讲LeakCanary 的原理?

作为一个内存检测泄露的工具框架。

LeakCanary实现内存泄漏的主要判断逻辑是这样的。当我们观察的Activity或者Fragment销毁时,我们会使用一个弱引用去包装当前销毁的Activity或者Fragment,并且将它与本地的一个ReferenceQueue队列关联。

在KeyedWeakReference内部,使用了key和name标识了一个被检测的WeakReference对象。在注释1处,将弱引用和引用队列 ReferenceQueue 关联起来,如果弱引用referent持有的对象被GC回收,JVM就会把这个弱引用加入到与之关联的引用队列referenceQueue中。即 KeyedWeakReference 持有的 Activity 对象如果被GC回收,该对象就会加入到引用队列 referenceQueue 中。

所以我们知道如果GC触发了,系统会将当前的引用对象存入队列中。

如果没有被回收,队列中则没有当前的引用对象。所以LeakCanary会去判断,ReferenceQueue是否有当前观察的Activity或者Fragment的引用对象,第一次判断如果不存在,就去手动触发一次GC,然后做第二次判断,如果还是不存在,则表明出现了内存泄漏。


事件分发流程:

核心的方法有三个: dispatchTouchEventonInterceptTouchEventonTouchEvent

流程图如下:


在 Android 中,当用户在设备上触发一个操作时,比如点击屏幕或者按下物理键,这些操作将会产生一个事件,并且会被封装成一个 MotionEvent 或者 KeyEvent 对象。事件传递到 View 的过程如下:

  1. 事件产生:用户在设备上触发操作,事件被产生。
  2. 事件传递:事件由activity传到Phonewindow到Decoreview在传递给顶层的 View 或 ViewGroup,也就是当前界面的根视图。
  3. 事件分发:根视图调用 dispatchTouchEvent() 或者 dispatchKeyEvent() 方法将事件分发给对应的子 View 进行处理。
  4. 事件处理:子 View 处理事件,如果事件被消费了,则返回 true,否则返回 false。
  5. 事件传递:如果子 View 返回 false,则事件被传递给父 View 进行处理,重复步骤 3-4 直到事件被处理或者到达根视图。

如果事件最终被消费了,则该事件将不会继续传递到其它 View 进行处理,相反,如果事件未被消费,则它将传递给其它 View 进行处理。

当事件处理完成后,系统会将事件的处理结果返回给 Activity,具体流程如下:

  1. Activity 的 onTouchEvent() 或者 onKeyDown() 方法会被调用。
  2. 如果事件被消费了,则返回 true,否则返回 false。
  3. 如果返回 true,表示该事件已经被处理了,不需要进一步处理。
  4. 如果返回 false,事件将继续传递到 Activity 的父类进行处理,直到事件被消费或者到达了顶级父类。 Android 事件传递到 View 并返回给 Activity 的流程是由底层向上层递归处理的,直到找到一个能够处理该事件的 View,并且该 View 成功地消费了该事件,否则该事件将被传递给 Activity 进行处理。

问:Android中加载大图方案?

Android中加载大图有多种方案,以下是一些常用的方案:

更好的方案是在SubScaleSampleImageView中,将大图切片,再判断是否可见,如果可见则加入内存中,否则回收,减少了内存占用与抖动 同时根据不同的缩放比例选择合适的采样率,进一步减少内存占用 同时在子线程进行decodeRegion操作,解码成功后回调至主线程,减少UI卡顿.

  1. 使用BitmapRegionDecoder: BitmapRegionDecoder是Android SDK提供的一种用于解码局部区域的大图像的工具类。可以通过设置解码的区域来加载大图,从而避免一次性加载整张图片造成内存溢出的问题。
  2. 使用缩略图: 可以先加载一个缩略图,当用户需要查看大图时,再通过异步加载的方式加载原图。这种方式可以避免一次性加载大图造成的卡顿问题。关键字“inSampleSize”
 BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4; // 缩放比例,值越大,缩放比例越大,图片越小
Bitmap thumbnail = BitmapFactory.decodeFile(filePath, options);
imageView.setImageBitmap(thumbnail);
  1. 使用Picasso、Glide等第三方库: 这些库能够有效地处理图片加载的问题,包括缓存、异步加载等功能,同时还支持网络图片加载等操作。

内存管理:Glide 对图片进行了内存管理,当加载超大图片时,它会将图片分成小块加载,而不是一次性将整个图片加载到内存中。这样可以避免内存溢出和应用程序崩溃的风险。

图片压缩:Glide 能够自动对图片进行压缩,减小图片的大小和分辨率,以降低对内存和性能的影响。这对于加载超大图片来说尤为重要,因为它们通常具有较高的像素密度和文件大小。


自定义View —— onMeasure、 onLayout

布局过程的作用

布局的流程

从整体看

从个体看

对于每一个 View:

  1. 运行前,开发者会根据自己的需求在 xml 文件中写下对于 View 大小的期望值
  2. 在运行的时候,父 View 会在 onMeaure()中,根据开发者在 xml 中写的对子 View 的要求, 和自身的实际可用空间,得出对于子 View 的具体尺寸要求
  3. 子 View 在自己的 onMeasure中,根据 xml 中指定的期望值和自身特点(指 View 的定义者在onMeasrue中的声明)算出自己的*期望

如果是 ViewGroup 还会在 onMeasure 中,调用每个子 View 的 measure () 进行测量.

  1. 父 View 在子 View 计算出期望尺寸后,得出 View 的实际尺 和位置
  2. View 在自己的 layout() 法中将父 View 传进来的实际尺寸和位置保存
    如果是 ViewGroup,还会在 onLayout() 调用每个字 View 的 layout() 把它们的尺寸 置传给它们

启动优化:


主题设置透明并设置启动图片防止白屏。

onCreate异步线程。少做初始化工作。idleHandler做不重要工作。gc抑制和核心线程绑定大核。

mainActivity和splashActivity合并。

核心功能核心页面内容优先显示。其他使用viewstub。

odex延后加载。


抖音odex优化

Glide源码相关和图片优化相关:

kotlin相关

kotlin优势:

语法糖,空安全,代码简洁,可对string类拓展自定义方法,协程解决了代码回调地狱问题。

协程相关问题:

本质上还是对线程的封装的一套api,Java是开辟线程,协程是通过调度线程去实现方法的挂起和恢复。所以可以开启非常多的协程而不会崩溃。

suspend是一个提醒作用。提醒你需要把方法异步,放在协程体里面。

协程工作的核心就是它内部的状态机,invokeSuspend() 函数。

所以函数即便被 suspend 修饰了,但是也未必会挂起。需要里面的代码编译后有返回值为 COROUTINE_SUSPENDED 这样的标记位才可以,所以程序执行到 case 0 的时候就 return 了。那就意味着方法被暂停了,那么协程也被暂停了。所以说协成的挂起实际上是方法的挂起,方法的挂起本质是 return。

列表卡顿优化

图片在停止滑动后加载,

避免用大图片做背景。用shape做背景绘制。

使用TraceView 抓取日子后对总耗时排序,通过app的包名搜索关键字,定位到 耗时方法

google描述这2个函数对耗时是敏感的。

1 优化onBindViewHolder. 这个函数原来会根据从网络获取不同的状态,判断是否显示和隐藏背景,通过ImageView.setBackgroundResource()。

因为背景不是动态获取的,所以优化为默认在ImageView中显示,通过setVisibility显示和隐藏ImageView.

优化后再次抓取TraceView,滑动列表是会不断触发onBindViewHolder, 但耗时只有1ms左右。 显示应该避免setBackground这种耗时操作。

那如果你确实需要加载不同图片呢?可以使用Glide等图片加载框架,在快速滑动时显示默认图片,待停止滑动后,再加载显示图片图片。


3 固定高度,避免重复计算高度 RecyclerView.setHasFixedSize(true);

4,局部刷新列表item,或者使用工具类对比一下哪里需要刷新就刷新哪里。

海外上架相关经验:遵循上架政策,并兼容到30或者更高。

1.马甲包:混淆代码,加入垃圾代码(使用AndroidJunkCode插件生成)。

  1. 更换域名,icon,UI,包名,创建新应用。
  2. 马甲包架构:利用路由(阿里的Arounter)进行组件化拆分,通过在buidl.gradle中去依赖UI模块进行功能组装。

  1. 海外谷歌支付流程,防止掉单的几种方式。

回:主要是客户端回调问题,点击下单-》查询一遍有没有掉单-》去自己后台下单-》拉起google支付-》支付成功--》客户端回调--》回调给自己后端--》发货--》调用消耗商品操作--》完成支付流程。

防止掉单:客户端轮询查订单信息,在启动或者登录后再查一遍是否有掉单情况(有没有订单未消费)。



性能优化

在安卓应用中,可以通过以下方面来做性能优化:

  1. 布局优化:减少布局层次结构、减少不必要的布局操作。
  2. 绘图优化:避免在UI线程上执行耗时绘图操作、减少不必要的绘图操作。
  3. 线程优化:合理使用线程,避免在UI线程上执行耗时操作,使用线程池避免线程创建和销毁的开销。
  4. 内存优化:及时释放不需要的对象、合理使用内存缓存、避免内存泄漏等。
  5. 网络优化:减少网络请求次数、使用缓存、使用合适的网络请求库等。
  6. 数据库优化:合理使用数据库、优化查询、使用索引等。
  7. APK瘦身:减少APK包的大小,去掉不必要的资源、代码等。
  8. 热修复:及时修复应用中的崩溃问题,提高应用的稳定性和用户体验。

在安卓开发中,有很多性能优化工具可供使用。以下是一些常见的性能优化工具:

  1. Android Profiler:Android Studio 自带的工具,可以帮助开发者实时监控应用程序的 CPU、内存、网络和电池等方面的性能数据。
  2. Traceview:Android Studio 自带的工具,用于分析 Android 应用程序的性能数据,包括方法执行时间、调用堆栈等信息。
  3. Systrace:Android Studio 自带的工具,用于分析 Android 应用程序的系统跟踪信息,例如 CPU 使用率、内存使用率、I/O 操作等。
  4. LeakCanary:一个内存泄漏检测库,可以帮助开发者快速定位内存泄漏问题。
  5. AndroidLint:Android Studio 自带的静态代码分析工具,可以检测代码中的一些潜在问题,例如性能问题、安全问题等。
  6. Memory Profiler:Android Studio 自带的工具,用于分析应用程序的内存使用情况,包括内存泄漏问题、对象分配等。
  7. GPU 监视器:Android Studio 自带的工具,可以帮助开发者分析应用程序的 GPU 性能。
  8. Battery Historian:一个用于分析 Android 设备电池使用情况的工具,可以帮助开发者分析应用程序在电池寿命方面的影响。
  9. Lint:Android Studio 自带的工具,用于检测代码中的一些问题,例如未使用的资源、未使用的代码等。
  10. RenderDoc:一个用于分析应用程序的 GPU 渲染性能的工具,可以帮助开发者分析应用程序的渲染性能。

这些工具可以帮助开发者定位应用程序的性能问题,并提供相应的解决方案。但需要注意的是,这些工具只是辅助性的,最终的性能优化还需要开发者进行具体的代码实现。


内存优化

下面是一些 Android 中内存优化的细节:

  1. 尽可能避免使用静态变量:静态变量会一直存在于内存中,直到应用程序关闭,这会占用大量的内存资源。因此,应尽可能避免使用静态变量,可以通过使用单例模式来避免使用静态变量。
  2. 尽可能使用轻量级数据结构:轻量级数据结构,例如 SparseArray 和 ArrayMap 等,可以减少内存的使用,提高应用程序的性能。
  3. 及时释放不再使用的资源:当应用程序不再使用资源时,应及时释放这些资源,以释放内存资源。
  4. 避免频繁的创建和销毁对象:创建和销毁对象需要消耗大量的内存资源,因此,应尽量避免频繁的创建和销毁对象,可以通过使用对象池等技术来避免频繁的创建和销毁对象。
  5. 使用 Bitmap 缩放:在加载大图像时,可以通过 Bitmap 缩放技术来减少内存的使用,提高应用程序的性能。
  6. 使用软引用或弱引用:软引用和弱引用可以帮助应用程序释放内存资源,避免内存泄漏问题。当内存不足时,GC 会回收软引用和弱引用所指向的对象,以释放内存资源。
  7. 使用内存缓存技术:在加载图片等资源时,可以使用内存缓存技术来减少网络请求,提高应用程序的性能。
  8. 使用 ProGuard 优化:ProGuard 是一个 Java 代码混淆工具,可以帮助应用程序减少 APK 大小,提高应用程序的性能。
  9. 及时关闭无用的服务和线程:当应用程序不再使用服务和线程时,应及时关闭这些服务和线程,以释放内存资源。
  10. 使用 LeakCanary 检测内存泄漏问题:LeakCanary 是一个内存泄漏检测库,可以帮助开发者快速定位内存泄漏问题,以减少内存泄漏问题对应用程序性能的影响。


在 Android 应用开发中,布局优化也是一项非常重要的工作,它直接关系到应用的运行效率和用户体验。以下是一些常见的布局优化细节:

  1. 减少嵌套层数:减少布局嵌套层数可以减小布局层级,提高布局性能。
  2. 使用 ConstraintLayout:ConstraintLayout 支持多个 View 之间相对位置的约束,使用 ConstraintLayout 可以减少布局嵌套,提高布局性能。
  3. 使用 RecyclerView:RecyclerView 是 Android 官方提供的用于展示大量数据列表的组件,它支持视图复用、惰性加载等功能,可以有效提高布局性能。
  4. 避免过度绘制:在布局中,避免使用不必要的背景或者绘制过于复杂的 View,可以减少布局的过度绘制。
  5. 避免布局中出现过多的 View:布局中过多的 View 会增加布局的复杂度,降低布局性能,可以使用合适的布局方式减少 View 的数量。
  6. 使用缓存技术:可以使用缓存技术来优化布局性能,例如在 ListView 中使用 ViewHolder 缓存 ItemView,可以减少 View 的创建和销毁。
  7. 使用 ViewStub:ViewStub 是一个轻量级的 View,可以在需要时动态加载布局,减少布局文件中的无用 View。
  8. 使用 include 标签:在布局中,使用 include 标签可以复用布局文件,减少布局文件的重复。
  9. 使用 merge 标签:在布局中,使用 merge 标签可以减少布局嵌套层数,提高布局性能。标签在UI的结构优化中起着非常重要的作用,它可以删减多余的层级,优化UI。多用于替换FrameLayout或者当一个布局包含另一个时,标签消除视图层次结构中多余的视图组。例如你的主布局文件是垂直布局,引入了一个垂直布局的include,这是如果include布局使用的LinearLayout就没意义了,使用的话反而减慢你的UI表现。这时可以使用标签优化。

  10. 使用权重值:在 LinearLayout 中,使用权重值可以实现按比例分配布局空间,避免过度布局。

安卓10到安卓12适配

安卓12适配

1.android:exported 属性需要显式声明。以及应用休眠(隔几个月不启动将重置权限和禁止后台通知)。

2.PendingIntent指定flag :PendingIntent.FLAG_MUTABLE 或PendingIntent.FLAG_IMMUTABLE,否则报错。

3.位置权限申请变更:位置权限分为了大致位置和确切位置。申请权限需要适配。

4.前台服务启动限制:

除了一些特殊情况外,以 Android12 为目标平台的应用将无法在后台运行时启动前台服务。如果应用尝试在后台运行时启动前台服务,将会引发异常。针对此情况,Android12官网也给出了前台服务的推荐替代方案:WorkManager

5.SplashScreen API,让App开发者可以自主设置启动应用的画面主题和外观

6.通知trampoline限制:,以Android12为目标平台的应用无法从用作通知 trampoline的服务或者广播中启动activity,也就是说应用构建的通知 setContentIntent()参数必须是 PendingIntent.getActivity。

安卓11适配:

强制分区存储:Android 11 需要使用 MediaStore API 访问访问共享存储空间中的媒体文件。除此以外还可以用

粗暴一点:获取外部存储管理权限。如果你的应用是手机管家、文件管理器这类需要访问大量文件的app,可以申请MANAGE_EXTERNAL_STORAGE权限,将用户引导至系统设置页面开启。

2.增加了单次授权。

3.请求位置权限调整

4.启动别的应用需要在清单文件添加标签,并添加指定包名

5.APK签名

Android 11 为目标平台的应用,仅通过v1 签名的应用无法在Android 11的设备上安装或更新。必须使用v2或更高版本进行签名。

同时Android 11 添加了对 APK 签名方案 v4 的支持。

其他安卓版本变化:

Android 6.0

应用权限管理
官方指纹支持
Doze电量管理
运行时权限机制->需要动态申请权限

Android 7.0

多窗口模式
支持Java 8语言平台
需要使用FileProvider访问照片
安装apk需要兼容

Android 8.0

通知
画中画
自动填充
后台限制
自适应桌面图标->适配
隐式广播限制
开启后台Service限制

Android 9.0

利用 Wi-Fi RTT 进行室内定位
刘海屏 API 支持
多摄像头支持和摄像头更新
不允许调用hide api
限制明文流量的网络请求 http

Android 10

暗黑模式
隐私增强(后台能否访问定位)
限制程序访问剪贴板
应用黑盒
权限细分需兼容
后台定位单独权限需兼容
设备唯一标示符需兼容
后台打开Activity 需兼容
非 SDK 接口限制 需兼容

ANR问题解决:

ANR原因

从应用的角度上来讲,它的原因可以归结为以下几种:

应用在主线程上非常缓慢地执行涉及 I/O 的操作。

应用在主线程上进行长时间的计算。

主线程在对另一个进程进行同步 binder 调用,而后者需要很长时间才能返回。

主线程处于阻塞状态,等待子线程的长时间耗时操作完成。

主线程在进程中或通过 binder 调用与另一个线程之间发生死锁。主线程不只是在等待长操作执行完毕,而且处于死锁状态。

如何检测和诊断ANR问题

1、开发中检测ANR

StrictMode(严格模式)

开发中由于个人原因,多多少少都会可能写出造成ANR的代码,要想在开发中及时发现问题,可以使用StrictMode有助于您在开发应用时发现主线程上的意外 I/O 操作。

启用后台 ANR 对话框

只有在设备的开发者选项中启用了显示所有 ANR 时,Android 才会针对花费过长时间处理广播消息的应用显示 ANR 对话框。我们可以通过打开这个选项,在开发中及早发现相关问题。

TraceView

TraceView 是 Android SDK 中内置的一个工具,它可以加载 trace 文件,用图形的形式展示代码的执行时间、次数及调用栈,便于我们分析。(注意:Android Studio3.2之后已经弃用)

CPU Profiler

Android Studio3.2之后,CPU Profiler替代了TraceView,我们可以通过在通过记录应用交互过程获取相关方法执行顺序和耗时图,从而分析哪些方法耗时过长导致ANR。

如何解决ANR问题

1、修改主线程上耗时的代码

通过TraceView或者CPU Profiler可以找到应用中主线程忙碌时间超过5s的位置,然后把此处代码移到子线程操作。如一些网络操作、耗时计算等。

2、锁的竞争导致堵塞

如果主线程参与锁的竞争,有可能会导致主线程持续等待锁而造成堵塞的问题,从而引发ANR。所以最好还是避免主线程出现竞争锁的情况。

3、死锁

如果某资源被另一个线程所持有,而该线程又在等待主线程的资源,就会陷入死锁情况导致ANR。

4、广播接收器执行速度慢

如在广播接收器的onReceive()执行了长时间的耗时操作,就会可能导致ANR,所以应该把耗时操作放到子线程操作。

如何实现ANR监控

ANR检测方案有开源的BlockCanary 、ANR-WatchDog、SafeLooper,xCrash(爱奇艺), 还有根据谷歌原生系统接口监测的方案:FileObserver。

ANR-WatchDog 原理:如下图所示。

ANR-WatchDog 原理

展开阅读全文

页面更新:2024-03-13

标签:线程   主线   应用程序   布局   加载   内存   性能   事件   操作   内容   图片

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top