正确的使用枚举类型
枚举类型(Enums),是 Java 语言中的一个特性。但 Android 官方强烈建议不要在 Android 应用里面使用到 Enums,是因为枚举类型在编译之后会生成很多内部类,在移动设备上内存比较珍贵显然会很占用内存。想了解枚举的实现细节可以查看 Java 枚举类型的实现原理 这篇文章。
所以了解了枚举类型的实现原理可以发现,在 Android 程序里不是不可以使用枚举类型,而是推荐使用。合理的使用枚举类型可以做到一些很优雅的操作,比如单例模式。
public enum EnumSingleton {
INSTANCE;
}
我们把字节码反编译后可以看到:
public final class EnumSingleton extends Enum {
public static final EnumSingleton INSTANCE;
public static EnumSingleton[] values();
public static EnumSingleton valueOf(String s);
static {};
}
由反编译后的代码可知,INSTANCE 被声明为 static 的,在类加载过程中,虚拟机会保证一个类的_<clinit>()
_ 方法在多线程环境中被正确的加锁、同步。所以,枚举实现是在实例化时是线程安全。
Java 虚拟机规范中规定,每一个枚举类型极其定义的枚举变量在 JVM 中都是唯一的,因此在枚举类型的序列化和反序列化上,Java 做了特殊的规定。
在序列化的时候 Java 仅仅是将枚举对象的 name 属性输出到结果中,反序列化的时候则是通过java.lang.Enum
的 valueOf 方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了 writeObject、readObject、readObjectNoData、writeReplace 和 readResolve 等方法。
普通的 Java 类的反序列化过程中,会通过反射调用类的默认构造函数来初始化对象。所以,即使单例中构造函数是私有的,也会被反射给破坏掉。由于反序列化后的对象是重新 new 出来的,所以这就破坏了单例。
但是,枚举的反序列化并不是通过反射实现的。所以,也就不会发生由于反序列化导致的单例破坏问题。
感兴趣的可以参看 stackoverflow 上的回答 What is an efficient way to implement a singleton pattern in Java?
减小 Bitmap 对象的内存占用
Bitmap 是一个极容易消耗内存的大胖子,关于 Bitmap 内存占用大小详情请参阅 Android 坑档案:你的 Bitmap 究竟占多大内存?,所以减小创建出来的 Bitmap 的内存占用是很重要的,通常来说有下面 2 个措施:
- 缩放比例
在把图片载入内存之前,我们需要先计算出一个合适的缩放比例,避免不必要的大图载入。
- 解码格式
选择 ALPHA_8/ARGB_4444/ARGB_8888/RBG_565,存在很大差异。
| 模式 | 描述 | 占用字节 |
| — | — | — |
| ALPHA_8 | Alpha 由 8 位组成 | 1B |
| ARGB_4444 | 4 个 4 位组成 16 位,每个色彩元素站 4 位 | 2B |
| ARGB_8888 | 4 个 8 为组成 32 位,每个色彩元素站 8 位(默认) | 4B |
| RGB_565 | R 为 5 位,G 为 6 位,B 为 5 位共 16 位,没有Alpha | 2B |
使用更小的图片
在设计给到资源图片的时候,我们需要特别留意这张图片是否存在可以压缩的空间,是否可以使用一张更小的图片。
尽量使用更小的图片不仅仅可以减少内存的使用,还可以避免出现大量的 InflationException。假设有一张很大的图片被 XML 文件直接引用,很有可能在初始化视图的时候就会因为内存不足而发生 InflationException,这个问题的根本原因其实是发生了 OOM。
- JPG vs PNG vs WebP
不了解这三种图片格式的建议看下 JPG 和 PNG 有什么区别?、WebP 原理和 Android 支持现状介绍 这两篇文章。
- 图片压缩
图片压缩相关知识推荐看下腾讯音乐技术团队的 Android 中图片压缩分析(上)、 Android 中图片压缩分析(下) 两篇文章。
了解了图片压缩的相关知识,我们可以自己写算法来实现图片压缩,也可以使用优秀的开源库,比如:鲁班。
内存对象的重复利用
除了减小对象对内存的占用,合理的复用内存对象也是很好避免内存溢出的方式。
大多数对象的复用,最终实施的方案都是利用对象池技术,要么是在编写代码的时候显式的在程序里面去创建对象池,然后处理好复用的实现逻辑,要么就是利用系统框架既有的某些复用特性达到减少对象的重复创建,从而减少内存的分配与回收。
LruCache
在 Android 中最常用的一个缓存算法是 LRU(Least Recently Use),就是当超出缓存容量的时候,就优先淘汰链表中最近最少使用的那个数据。
LruCache bitmapCache = new LruCache<String, Bitmap>();
// 根据内存空间设置缓存大小
ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int availMemInBytes = am.getMemoryClass() * 1024 *1024;
LruCache bitmapCache = new LruCache<String, Bitmap>(availMemInBytes/8);
使用 LruCache 可以缓存 Bitmap 对象,相同LruCache 只是对内存中对象有效,如果我们想把图片、视频等文件缓存在磁盘上可以使用 JakeWharton 大神开源的 DiskLruCache。
使用 Glide
Glide 是一个快速高效的 Android 图片加载库,注重于平滑的滚动。Glide 提供了易用的 API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。
Glide 也是 Google 推荐过的开源项目,详见:Glide。
复用系统自带的资源
Android 系统本身内置了很多的资源(例如:字符串、颜色、图片、动画、样式以、简单布局等等),这些资源都可以在应用程序中直接引用。
这样做不仅仅可以减少应用程序的自身负重,减小 APK 的大小,另外还可以一定程度上减少内存的开销,复用性更好。但是也有必要留意 Android 系统的版本差异性,对那些不同系统版本上表现存在很大差异,不符合需求的情况,还是需要应用程序自身内置进去。
复用 ConvertView
在 ListView、GridView 等出现大量重复子组件的视图里面对 ConvertView 的复用。
onLowMemory()
OnLowMemory 是 Android 提供的API,在系统内存不足,所有后台程序(优先级为 Background 的进程,不是指后台运行的进程)都被杀死时,系统会调用 OnLowMemory。
系统提供的回调有:Application、Activity、Fragementice、Service、ContentProvider。
onTrimMemory()
OnTrimMemory 是 Android 4.0 之后提供的 API,系统会根据不同的内存状态来回调。
系统提供的回调有:Application、Activity、Fragementice、Service、ContentProvider。
OnTrimMemory的参数是一个 int 数值,代表不同的内存状态。
当 App 在前台运行时,该函数的 level (从低到高)有:
- TRIM_MEMORY_RUNNING_MODERATE
系统开始运行在低内存状态下 App 正在运行,不会被杀掉。
- TRIM_MEMORY_RUNNING_LOW
系统运行在更加低内存状态下,App 在运行,不会被杀掉 App 可以清理一些资源来保证系统的流畅。
- TRIM_MEMORY_RUNNING_CRITICAL
系统运行在相当低内存状态下,App 在运行,且系统不认为可以杀掉此 App,系统要开始杀掉后台进程。此时,App 应该去释放一些不重要的资源。
当 App 在后台运行时,level 状态有:
- TRIM_MEMORY_UI_HIDDEN:
App 的 UI 不可见,App 可以清理 UI 使用的较大的资源。
当 App 进入后台 LRU List 时:
- TRIM_MEMORY_BACKGROUND
系统运行在低内存下,App 进程在 LRU List 开始处附近,尽管 App 没有被杀掉的风险,但是系统也许已经正在杀后台进程。App 应该清理一些容易恢复的资源。
- TRIM_MEMORY_MODERATE
系统运行在低内存下,App 进程在 LRU List 中间处附件,App 此时有被杀的可能。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后
总而言之,成功是留给准备好的人的。无论是参加什么面试,都要做好充足的准备,注意好面试的礼仪和穿着,向面试官表现出自己的热忱与真诚就好。即使最后没有过关,也要做好经验的总结,为下一次面试做好充足准备。
这里我为大家准备了一些我在面试后整理的面试专题资料,除了面试题,还总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料分享给大家,希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
毕竟不管遇到什么样的面试官,去面试首先最主要的就是自己的实力,只要实力够硬,技术够强,就不怕面试拿不到offer!
为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
+项目实战源码》]( )收录**
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算