Android性能优化常见Q

本文探讨了Android应用中的内存管理,包括图片的三级缓存策略、内存溢出与内存泄露的区别、WebView的性能优化、大图处理以及Bitmap的合理加载。详细介绍了内存回收机制、GC算法及其应用场景,并提供了防止内存溢出的技巧,如使用BitmapFactory的缩放选项。此外,文章还对比了ListView与RecyclerView的性能差异,强调了启动应用时避免卡顿和黑白屏的处理方法。最后,讨论了加载图片时的注意事项,建议使用合适的图片加载库以提高性能。

1、图片的三级缓存中,图片加载到内存中,如果内存快爆了,会发生什么?怎么处理?

  • 参考回答:
    • 首先我们要清楚图片的三级缓存是如何的

       

      如果内存足够时不回收。内存不够时就回收软引用对象

2、内存中如果加载一张500*500的png高清图片.应该是占用多少的内存?

  • 参考回答:
    • 不考虑屏幕比的话:占用内存=500 * 500 * 4 = 1000000B ≈ 0.95MB
    • 考虑屏幕比的的话:占用内存= 宽度像素 x (inTargetDensity / inDensity) x 高度像素 x (inTargetDensity / inDensity)x 一个像素所占的内存字节大小

inDensity表示目标图片的dpi(放在哪个资源文件夹下),inTargetDensity表示目标屏幕的dpi

密度dpi范围
ldpi120dpi
mdpi160dpi
hdpi240dpi
xhdpi320dpi
xxhdpi480dpi
xxxhdpi640dpi

3、WebView的性能优化 ?

  • 参考回答:
    • 一个加载网页的过程中,native、网络、后端处理、CPU都会参与,各自都有必要的工作和依赖关系;让他们相互并行处理而不是相互阻塞才可以让网页加载更快:
      • WebView初始化慢,可以在初始化同时先请求数据,让后端和网络不要闲着。
      • 常用 JS 本地化及延迟加载,使用第三方浏览内核
      • 后端处理慢,可以让服务器分trunk输出,在后端计算的同时前端也加载网络静态资源。
      • 脚本执行慢,就让脚本在最后运行,不阻塞页面解析。
      • 同时,合理的预加载、预缓存可以让加载速度的瓶颈更小。
      • WebView初始化慢,就随时初始化好一个WebView待用。
      • DNS和链接慢,想办法复用客户端使用的域名和链接。

4、Bitmap如何处理大图,如一张30M的大图,如何预防OOM?

  • 参考回答:避免OOM的问题就需要对大图片的加载进行管理,主要通过缩放来减小图片的内存占用。
    • BitmapFactory提供的加载图片的四类方法(decodeFile、decodeResource、decodeStream、decodeByteArray)都支持BitmapFactory.Options参数,通过inSampleSize参数就可以很方便地对一个图片进行采样缩放
    • 比如一张10241024的高清图片来说。那么它占有的内存为102410244,即4MB,如果inSampleSize为2,那么采样后的图片占用内存只有5125124,即1MB(注意:根据最新的官方文档指出,inSampleSize的取值应该总是为2的指数,即1、2、4、8等等,如果外界输入不足为2的指数,系统也会默认选择最接近2的指数代替,比如2*)
    • 综合考虑。通过采样率即可有效加载图片,流程如下
      • 将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片
      • 从BitmapFactory.Options中取出图片的原始宽高信息,它们对应outWidth和outHeight参数
      • 根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize
      • 将BitmapFactory.Options的inJustDecodeBounds参数设为false,重新加载图片

5、内存回收机制与GC算法(各种算法的优缺点以及应用场景);GC原理时机以及GC对象

  • 参考回答:
    • 内存判定对象可回收有两种机制:
      • 引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。然而在主流的Java虚拟机里未选用引用计数算法来管理内存,主要原因是它难以解决对象之间相互循环引用的问题,所以出现了另一种对象存活判定算法。
      • 可达性分析法:通过一系列被称为『GCRoots』的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。其中可作为GC Roots的对象:虚拟机栈中引用的对象,主要是指栈帧中的本地变量、本地方法栈中Native方法引用的对象、方法区中类静态属性引用的对象、方法区中常量*引用的对象
    • GC回收算法有以下四种:
      • 分代收集算法:是当前商业虚拟机都采用的一种算法,根据对象存活周期的不同,将Java堆划分为新生代和老年代,并根据各个年代的特点采用最适当的收集算法。
      • 新生代:大批对象死去,只有少量存活。使用『复制算法』,只需复制少量存活对象即可。
        • 复制算法:把可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用尽后,把还存活着的对象『复制』到另外一块上面,再将这一块内存空间一次清理掉。实现简单,运行高效。在对象存活率较高时就要进行较多的复制操作,效率将会变低
      • 老年代:对象存活率高。使用『标记—清理算法』或者『标记—整理算法』,只需标记较少的回收对象即可。
        • 标记-清除算法:首先『标记』出所有需要回收的对象,然后统一『清除』所有被标记的对象。标记和清除两个过程的效率都不高,清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
        • 标记-整理算法:首先『标记』出所有需要回收的对象,然后进行『整理』,使得存活的对象都向一端移动,最后直接清理掉端边界以外的内存。标记整理算法会将所有的存活对象移动到一端,并对不存活对象进行处理,因此其不会产生内存碎片

6、内存泄露和内存溢出的区别 ?AS有什么工具可以检测内存泄露

  • 参考回答:
    • 内存溢出(out of memory):是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
    • 内存泄露(memory leak):是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。memory leak会最终会导致out of memory!
    • 查找内存泄漏可以使用Android Studio 自带的AndroidProfiler工具或MAT

7、性能优化,怎么保证应用启动不卡顿? 黑白屏怎么处理?

  • 参考回答:
    • 应用启动速度,取决于你在application里面时候做了什么事情,比如你集成了很多sdk,并且sdk的init操作都需要在主线程里实现所以会有卡顿的感觉。在非必要的情况下可以把加载延后或则开启子线程处理
    • 另外,影响界面卡顿的两大因素,分别是界面绘制和数据处理。
      • 布局优化(使用include,merge标签,复杂布局推荐使用ConstraintLayout等)
      • onCreate() 中不执行耗时操作 把页面显示的 View 细分一下,放在 AsyncTask 里逐步显示,用 Handler 更好。这样用户的看到的就是有层次有步骤的一个个的 View 的展示,不会是先看到一个黑屏,然后一下显示所有 View。最好做成动画,效果更自然。
      • 利用多线程的目的就是尽可能的减少 onCreate() 和 onReume() 的时间,使得用户能尽快看到页面,操作页面。
      • 减少主线程阻塞时间。
      • 提高 Adapter 和 AdapterView 的效率。
    • 黑白屏产生原因:当我们在启动一个应用时,系统会去检查是否已经存在这样一个进程,如果不存在,系统的服务会先检查startActivity中的intent的信息,然后在去创建进程,最后启动Acitivy,即冷启动。而启动出现白黑屏的问题,就是在这段时间内产生的。系统在绘制页面加载布局之前,首先会初始化窗口(Window),而在进行这一步操作时,系统会根据我们设置的Theme来指定它的Theme 主题颜色,我们在Style中的设置就决定了显示的是白屏还是黑屏。
      • windowIsTranslucent和windowNoTitle,将这两个属性都设置成true (会有明显的卡顿体验,不推荐)
      • 如果启动页只是是一张图片,那么为启动页专一设置一个新的主题,设置主题的android:windowBackground属性为启动页背景图即可
      • 使用layer-list制作一张图片launcher_layer.xml,将其设置为启动页专一主题的背景,并将其设置为启动页布局的背景。

8、强引用置为null,会不会被回收?

  • 参考回答:
    • 不会立即释放对象占用的内存。 如果对象的引用被置为null,只是断开了当前线程栈帧中对该对象的引用关系,而 垃圾收集器是运行在后台的线程,只有当用户线程运行到安全点(safe point)或者安全区域才会扫描对象引用关系,扫描到对象没有被引用则会标记对象,这时候仍然不会立即释放该对象内存,因为有些对象是可恢复的(在 finalize方法中恢复引用 )。只有确定了对象无法恢复引用的时候才会清除对象内存。

9、ListView跟RecyclerView的区别

  • 参考回答:
    • 动画区别:
      • RecyclerView中,内置有许多动画API,例如:notifyItemChanged(), notifyDataInserted(), notifyItemMoved()等等;如果需要自定义动画效果,可以通过实现(RecyclerView.ItemAnimator类)完成自定义动画效果,然后调用RecyclerView.setItemAnimator();
      • 但是ListView并没有实现动画效果,但我们可以在Adapter自己实现item的动画效果;
    • 刷新区别:
      • ListView中通常刷新数据是用全局刷新notifyDataSetChanged(),这样一来就会非常消耗资源;本身无法实现局部刷新,但是如果要在ListView实现局部刷新,依然是可以实现的,当一个item数据刷新时,我们可以在Adapter中,实现一个onItemChanged()方法,在方法里面获取到这个item的position(可以通过getFirstVisiblePosition()),然后调用getView()方法来刷新这个item的数据;
      • RecyclerView中可以实现局部刷新,例如:notifyItemChanged();
    • 缓存区别:
      • RecyclerView比ListView多两级缓存,支持多个离ItemView缓存,支持开发者自定义缓存处理逻辑,支持所有RecyclerView共用同一个RecyclerViewPool(缓存池)。
      • ListView和RecyclerView缓存机制基本一致,但缓存使用不同

10、ListView的adapter是什么adapter

参考回答:

  • BaseAdapter:抽象类,实际开发中我们会继承这个类并且重写相关方法,用得最多的一个适配器!
  • ArrayAdapter:支持泛型操作,最简单的一个适配器,只能展现一行文字〜
  • SimpleAdapter:同样具有良好扩展性的一个适配器,可以自定义多种效果!
  • SimpleCursorAdapter:用于显示简单文本类型的listView,一般在数据库那里会用到,不过有点过时,不推荐使用!

11.加载图片的时候需要注意什么?

尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。
因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source,decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,
无需再使用java层的createBitmap,从而节省了java层的空间。

12、LinearLayout、FrameLayout、RelativeLayout性能对比,为什么?

  • 参考回答:
    • RelativeLayout会让子View调用2次onMeasure,LinearLayout 在有weight时,也会调用子 View 2次onMeasure
    • RelativeLayout的子View如果高度和RelativeLayout不同,则会引发效率问题,当子View很复杂时,这个问题会更加严重。如果可以,尽量使用padding代替margin。
    • 在不影响层级深度的情况下,使用LinearLayout和FrameLayout而不是RelativeLayout。
<think>好的,我现在需要帮助用户了解Android原生性能优化的方法和机制。首先,我需要回忆一下Android性能优化常见技术,包括内存管理、渲染优化、计算效率提升等方面。根据用户的问题,他们可能想知道具体的优化手段和背后的机制,而不仅仅是表面的技巧。 首先,我应该从内存优化开始。Android中使用Java或Kotlin时容易发生内存泄漏,但在Native层(比如使用C/C++)同样需要注意。比如,JNI中的全局引用如果没有正确释放,会导致内存泄漏。此外,内存碎片化也是一个问题,可能需要使用对象池或内存池来优化。引用用户提供的资料中的缓存机制,比如Redis,可能在这里不太适用,但可以提到类似的多级缓存策略在Native层的应用。 接下来是渲染优化。UI线程的过度绘制会影响性能,特别是在自定义View或复杂布局时。离屏渲染和图层合并可能会引起GPU过载,需要减少不必要的绘制操作。或许可以提到使用SurfaceView或TextureView来提升渲染效率,以及利用硬件加速的特性。 然后是计算效率优化。算法优化是关键,比如选择合适的数据结构和算法,减少时间复杂度。此外,利用多线程技术如线程池管理,避免频繁创建和销毁线程。可能还需要提到NEON指令集的使用,在ARM处理器上加速计算密集型任务,比如图像处理或数学运算。 存储优化方面,文件I/O操作可能会成为瓶颈,尤其是在频繁读写的情况下。使用内存映射文件(mmap)或异步写入可以提高效率。此外,数据库查询的优化,比如索引的使用和批量操作,也是重要的一环。参考中的Kafka用于异步处理,可能可以类比到使用后台线程处理存储任务,避免阻塞主线程。 功耗优化也需要考虑,比如减少wakelock的使用,优化传感器和网络请求的频率。使用JobScheduler来批量处理任务,减少唤醒次数,从而延长电池寿命。 工具方面,Android Profiler是官方工具,可以监控CPU、内存和网络的使用情况。Systrace可以帮助分析渲染性能,而Perfetto则提供了更全面的性能分析功能。这些工具的使用方法可能需要简要说明,帮助用户定位问题。 最后,机制方面,系统级优化如Binder IPC的优化,减少跨进程通信的开销。ART虚拟机在安装时预编译字节码为机器码,提升运行效率,这也是一个关键点。硬件加速则涉及如何利用GPU和专用硬件来处理图形和计算任务。 在生成相关问题的时候,需要确保问题涵盖用户可能进一步感兴趣的方面,比如工具使用、功耗优化具体方法、多线程实现、内存泄漏检测等。同时,注意引用用户提供的资料,比如引用[1]提到的缓存和消息队列,可以联系到Native层是否适用类似的策略,或者引用[2]中提到的绕过聚合机制,但这里可能不太相关,所以可能不需要直接引用。 总结起来,我需要组织这些点,按照用户要求的格式,分点回答,确保使用正确的中文表达,并自然地加入引用。同时,检查LaTeX格式是否正确,确保行内公式用$...$,独立公式用$$...$$。不过当前问题中可能涉及公式的地方不多,但如果有提到算法复杂度,比如O(n log n)之类的,需要正确格式化。</think>### Android原生性能优化的方法和机制 #### 一、内存优化 1. **内存泄漏控制** - 在Native层(如C/C++/JNI)中,需手动管理内存。例如,JNI的全局引用(`NewGlobalRef`)必须及时释放(`DeleteGlobalRef`),否则会导致内存泄漏[^1]。 - 使用工具(如Android Profiler或Valgrind)检测Native层内存泄漏。 2. **内存碎片化优化** - 通过**对象池**或**内存池**复用内存块,减少频繁分配/释放造成的碎片。例如,图形处理中复用`Bitmap`对象。 3. **多级缓存策略** - 参考缓存优化思想,Native层可通过LRU缓存算法管理高频数据,如纹理资源或解码后的媒体帧。 #### 二、渲染优化 1. **减少过度绘制** - 使用`GPU过度绘制调试工具`识别冗余绘制,优化自定义View的`onDraw`方法,避免重复绘制背景。 2. **离屏渲染优化** - 避免滥用`Canvas.clipPath`等复杂操作,改用硬件加速的`View.setLayerType(LAYER_TYPE_HARDWARE)`提升性能。 3. **SurfaceView与TextureView** - 对高帧率场景(如视频播放),优先用`SurfaceView`直接操作显存,减少UI线程阻塞。 #### 三、计算效率优化 1. **算法优化** - 选择时间复杂度更优的算法。例如,排序算法优先用$O(n \log n)$的快速排序而非$O(n^2)$的冒泡排序。 2. **多线程与并发** - 使用线程池(如`ThreadPoolExecutor`)管理Native线程,避免频繁创建/销毁开销。参考异步消息处理思想,可将计算任务拆分到多个线程并行处理。 3. **NEON指令集加速** - 在ARM架构中,利用NEON指令并行化计算密集型任务(如矩阵运算)。例如: ```c // NEON示例:并行加法 float32x4_t a = vld1q_f32(array1); float32x4_t b = vld1q_f32(array2); float32x4_t result = vaddq_f32(a, b); ``` #### 四、存储优化 1. **文件I/O优化** - 使用`mmap`内存映射文件减少读写次数,尤其适用于大文件(如日志记录)。 - SQLite批量操作时启用事务,减少磁盘同步开销。 2. **异步写入** - 类似消息队列机制,将非实时存储任务放入后台线程,避免阻塞主线程。 #### 五、功耗优化 1. **传感器与网络优化** - 减少高频传感器(如GPS)的使用,改用低功耗的`Fused Location Provider`。 - 合并网络请求,使用`JobScheduler`批量执行。 #### 六、工具与调试 1. **性能分析工具** - **Android Profiler**:监控Native内存和CPU占用。 - **Systrace**:分析渲染耗时和线程调度。 - **Perfetto**:跟踪Native代码执行流程。 2. **内存检测工具** - **AddressSanitizer(ASan)**:检测Native层内存越界、泄漏等问题。 #### 七、系统级机制 1. **Binder IPC优化** - 减少跨进程通信(IPC)频率,合并数据传输(如使用`Bundle`或共享内存)。 2. **ART虚拟机优化** - AOT(Ahead-of-Time)编译将字节码预编译为机器码,提升启动速度。 3. **硬件加速** - 利用GPU和DSP处理图形渲染、AI推理等任务,例如通过Vulkan API实现高效渲染。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值