`
Z_萧晓
  • 浏览: 10766 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

阿里Android 26条规范经验及优化

阅读更多

阿里Android 26条规范经验及优化

1、Activity 间的数据通信,对于数据量比较大的,避免使用 Intent + Parcelable的方式,可以考虑 EventBus 等替代方案,以免造成 TransactionTooLargeException

 

2、Activity 间通过隐式 Intent 的跳转,在发出 Intent 之前必须通过 resolveActivity检查,避免找不到合适的调用组件,造成 ActivityNotFoundException 的异常。

public void viewUrl(String url, String mimeType) {
 Intent intent = new Intent(Intent.ACTION_VIEW);
 intent.setDataAndType(Uri.parse(url), mimeType);
 if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_
ONLY) != null) {
 try {
 startActivity(intent);
 } catch (ActivityNotFoundException e) {
 if (Config.LOGD) {
 Log.d(LOGTAG, "activity not found for " + mimeType + " over " +
Uri.parse(url). getScheme(), e);
 }
 }
 }
}

3、避免在 BroadcastReceiver#onReceive()中执行耗时操作,如果有耗时工作,应该创建 IntentService 完成,而不应该在 BroadcastReceiver 内创建子线程去做。

 

说明:

 

由于该方法是在主线程执行,如果执行耗时操作会导致 UI 不流畅。可以使用IntentService 、 创 建 HandlerThread 或者调用 Context#registerReceiver
(BroadcastReceiver, IntentFilter, String, Handler)方法等方式,在其他 Wroker 线程执行 onReceive 方法。BroadcastReceiver#onReceive()方法耗时超过 10 秒钟,可能会被系统杀死

 

4、避免使用隐式 Intent 广播敏感信息,信息可能被其他注册了对应BroadcastReceiver 的 App 接收。

 

说明:

 

通过 Context#sendBroadcast()发送的隐式广播会被所有感兴趣的 receiver 接收,恶意应用注册监听该广播的 receiver 可能会获取到 Intent 中传递的敏感信息,并进行其他危险操作。如果发送的广播为使用 Context#sendOrderedBroadcast()方法发送的有序广播,优先级较高的恶意 receiver 可能直接丢弃该广播,造成服务不可用,或者向广播结果塞入恶意数据。如果广播仅限于应用内,则可以使用LocalBroadcastManager#sendBroadcast()实现,避免敏感信息外泄和 Intent 拦截的风险

Intent intent = new Intent("my-sensitive-event");
intent.putExtra("event", "this is a testevent");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent

5、不要在 Activity#onDestroy()内执行释放资源的工作,例如一些工作线程的销毁和停止,因为 onDestroy()执行的时机可能较晚。可根据实际需要,在Activity#onPause()/onStop()中结合 isFinishing()的判断来执行。

 

6、如非必须,避免使用嵌套的 Fragment。

 

说明:

 

嵌套 Fragment 是在 Android API 17 添加到 SDK 以及 Support 库中的功能,Fragment 嵌套使用会有一些坑,容易出现 bug,比较常见的问题有如下几种:

 

1)onActivityResult()方法的处理错乱,内嵌的 Fragment 可能收不到该方法的回调,
需要由宿主 Fragment 进行转发处理;
2) 突变动画效果;
3)被继承的 setRetainInstance(),导致在 Fragment 重建时多次触发不必要的逻辑。

非必须的场景尽可能避免使用嵌套 Fragment,如需使用请注意上述问题。

 

7、当前Activity的onPause方法执行结束后才会执行下一个Activity的onCreate方法,所以在 onPause 方法中不适合做耗时较长的工作,这会影响到页面之间的跳转效率

 

8、不要在 Android 的 Application 对象中缓存数据。基础组件之间的数据共享请使用 Intent 等机制,也可使用 SharedPreferences 等数据持久化机制

class MyApplication extends Application {
 String username;
 String getUsername() {
 return username;
 }
 void setUsername(String username) {
 this.username = username;
 }
}

9、使用 Toast 时,建议定义一个全局的 Toast 对象,这样可以避免连续显示Toast 时不能取消上一次 Toast 消息的情况(如果你有连续弹出 Toast 的情况,避免使用 Toast.makeText)

 

10、使用 Adapter 的时候,如果你使用了 ViewHolder 做缓存,在 getView()的方法中无论这项 convertView 的每个子控件是否需要设置属性(比如某个 TextView设置的文本可能为 null,某个按钮的背景色为透明,某控件的颜色为透明等),都需要为其显式设置属性(Textview 的文本为空也需要设置 setText(""),背景透明也需要设置),否则在滑动的过程中,因为 adapter item 复用的原因,会出现内容的显示错乱

 

11、Activity或者 Fragment 中动态注册BroadCastReceiver 时,registerReceiver()和 unregisterReceiver()要成对出现。

 

说明:

 

如果 registerReceiver()和 unregisterReceiver()不成对出现,则可能导致已经注册的receiver 没有在合适的时机注销,导致内存泄漏,占用内存空间,加重 SystemService负担。

部分华为的机型会对 receiver 进行资源管控,单个应用注册过多 receiver 会触发管控模块抛出异常,应用直接崩溃。

 

12、布局中不得不使用 ViewGroup 多重嵌套时,不要使用 LinearLayout 嵌套,改用 RelativeLayout,可以有效降低嵌套数。

 

说明:

 

Android 应用页面上任何一个 View 都需要经过 measure、layout、draw 三个步骤才能被正确的渲染。从 xml layout 的顶部节点开始进行 measure,每个子节点都需要向自己的父节点提供自己的尺寸来决定展示的位置,在此过程中可能还会重新measure(由此可能导致 measure 的时间消耗为原来的 2-3 倍)。节点所处位置越深,套嵌带来的 measure 越多,计算就会越费时。这就是为什么扁平的 View 结构会性能更好。

 

同时,页面拥上的 View 越多,measure、layout、draw 所花费的时间就越久。要缩短这个时间,关键是保持 View 的树形结构尽量扁平,而且要移除所有不需要渲染的View。理想情况下,总共的 measure,layout,draw 时间应该被很好的控制在 16ms以内,以保证滑动屏幕时 UI 的流畅。

 

要找到那些多余的 View(增加渲染延迟的 view),可以用 Android Studio Monitor里的 Hierarachy Viewer 工具,可视化的查看所有的 view

 

13、在 Activity 中显示对话框或弹出浮层时,尽量使用 DialogFragment,而非
Dialog/AlertDialog,这样便于随Activity生命周期管理对话框/弹出浮层的生命周期

 

14、文本大小使用单位 dp,view 大小使用单位 dp。对于 Textview,如果在文字大小确定的情况下推荐使用 wrap_content 布局避免出现文字显示不全的适配问题。

 

15、线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

 

说明:

 

Executors 返回的线程池对象的弊端如下:

1)FixedThreadPool 和 SingleThreadPool : 允 许 的 请 求 队 列 长 度 为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM;
2)CachedThreadPool 和 ScheduledThreadPool : 允 许 的 创 建 线 程 数 量 为Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

正例:

int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
int KEEP_ALIVE_TIME = 1;
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES, 
NUMBER_OF_CORES*2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, 
taskQueue, new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());

反例:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

16、当使用外部存储时,必须检查外部存储的可用性

// 读/写检查
public boolean isExternalStorageWritable() {
 String state = Environment.getExternalStorageState();
 if (Environment.MEDIA_MOUNTED.equals(state)) {
 return true;
 }
 return false;
}
// 只读检查
public boolean isExternalStorageReadable() {
 String state = Environment.getExternalStorageState();
 if (Environment.MEDIA_MOUNTED.equals(state) ||
 Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
 return true;
 }
 return false;
}

17、SharedPreference 提 交 数 据 时 , 尽 量 使 用 Editor#apply() ,而非Editor#commit()。一般来讲,仅当需要确定提交结果,并据此有后续操作时,才使用 Editor#commit()。

 

说明:

 

SharedPreference 相关修改使用 apply 方法进行提交会先写入内存,然后异步写入磁盘,commit 方法是直接写入磁盘。如果频繁操作的话 apply 的性能会优于 commit,apply 会将最后修改内容写入磁盘。但是如果希望立刻获取存储操作的结果,并据此做相应的其他操作,应当使用 commit。

 

18、数据库 Cursor 必须确保使用完后关闭,以免内存泄漏。
多线程操作写入数据库时,需要使用事务,以免出现同步问题。
执行 SQL 语句时,应使用 SQLiteDatabase#insert()、update()、delete(),不要使用 SQLiteDatabase#execSQL(),以免 SQL 注入风险

 

19、加载大图片或者一次性加载多张图片,应该在异步线程中进行。图片的加载,涉及到 IO 操作,以及 CPU 密集操作,很可能引起卡顿。

 

20、在 ListView,ViewPager,RecyclerView,GirdView 等组件中使用图片时,应做好图片的缓存,避免始终持有图片导致内存泄露,也避免重复创建图片,引起性 能 问 题 。 建 议 使 用 Fresco (https://github.com/facebook/fresco )Glide(https://github.com/bumptech/glide)等图片库

 

21、png 图片使用 tinypng 或者类似工具压缩处理,减少包体积

 

22、在 Activity.onPause()或 Activity.onStop()回调中,关闭当前 activity 正在执行的的动画。

 

23、使用 inBitmap 重复利用内存空间,避免重复开辟新内存

private static void addInBitmapOptions(BitmapFactory.Options options,
 ImageCache cache) {
 // inBitmap 只处理可变的位图,所以强制返回可变的位图
 options.inMutable = true;
 if (cache != null) {
 Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
 if (inBitmap != null) {
 options.inBitmap = inBitmap;
 }
 }
}

24、使用 ARGB_565 代替 ARGB_888 大多数场景使用的是
ARGB_8888 和 RGB_565,RGB_565 能够在保证图片质量的情况下大大减少内存的开销,是解决 oom 的一种方法。

 

但是一定要注意 RGB_565 是没有透明度的,如果图片本身需要保留透明度,那么就不能使用 RGB_565

 

1)Glide 默认的 Bitmap 格式是 RGB_565 格式,而 Picasso 默认的是 ARGB_8888 格式,这个内存开销要小一半
2)在磁盘缓存方面,Picasso 只会缓存原始尺寸的图片,而 Glide 缓存的是多种规格,也就意味着 Glide 会根据你 ImageView 的大小来缓存相应大小的图片尺寸,比如你 ImageView 大小是200200,原图是 400400 ,而使用 Glide 就会缓存 200200 规格的图,而 Picasso 只会缓存 400400 规格的。这个改进就会导致 Glide 比 Picasso 加载的速度要快,毕竟少了每次裁剪重新渲染的过程。
3)最重要的一个特性是 Glide 支持加载 Gif 动态图,而 Picasso 不支持该特性。

 

25、图片压缩,减少内存占用

使用 inSampleSize 采样率压缩

public static Bitmap getFitSampleBitmap(String file_path, int width, int height) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file_path, options);
options.inSampleSize = getFitInSampleSize(width, height, options);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(file_path, options);
}
public static int getFitInSampleSize(int reqWidth, int reqHeight, BitmapFactory.Options options) {
int inSampleSize = 1;
if (options.outWidth > reqWidth || options.outHeight > reqHeight) {
int widthRatio = Math.round((float) options.outWidth / (float) reqWidth);
int heightRatio = Math.round((float) options.outHeight / (float) reqHeight);
inSampleSize = Math.min(widthRatio, heightRatio);
}
return inSampleSize;
}

使用矩阵

大图小用用采样,小图大用用矩阵。

 

还是用前面模糊图片的例子,我们不是采样了么?内存是小了,可是图的尺寸也小了啊,我要用 Canvas 绘制这张图可怎么办?当然是用矩阵了

Matrix matrix = new Matrix();
matrix.preScale(2, 2, 0, 0);
canvas.drawBitmap(bitmap, matrix, paint);

这样,绘制出来的图就是放大以后的效果了,不过占用的内存却仍然是我们采样出来的大小。如果我要把图片放到 ImageView 当中呢?一样可以,请看:

Matrix matrix = new Matrix();
matrix.postScale(2, 2, 0, 0);
imageView.setImageMatrix(matrix);
imageView.setScaleType(ScaleType.MATRIX);
imageView.setImageBitmap(bitmap);

合理选择Bitmap的像素格式

其实前面我们已经多次提到这个问题。ARGB8888格式的图片,每像素占用 4 Byte,而 RGB565则是 2 Byte。我们先看下有多少种格式可选:

 

格式:描述


ALPHA_8:只有一个alpha通道

ARGB_4444:这个从API 13开始不建议使用,因为质量太差
ARGB_8888:ARGB四个通道,每个通道8bit
RGB_565:每个像素占2Byte,其中红色占5bit,绿色占6bit,蓝色占5bit

 

这几个当中,ALPHA8 没必要用,因为我们随便用个颜色就可以搞定的。ARGB4444 虽然占用内存只有 ARGB8888 的一半,不过已经被官方嫌弃,失宠了。。『又要占省内存,又要看着爽,臣妾做不到啊T T』。ARGB8888 是最常用的,大家应该最熟悉了。RGB565 看到这个,我就看到了资源优化配置无处不在,这个绿色。。(不行了,突然好邪恶XD),其实如果不需要 alpha 通道,特别是资源本身为 jpg 格式的情况下,用这个格式比较理想。

 

26、当 View Animation 执行结束时,调用 View.clearAnimation()释放相关资源。

 

不管怎么说,大家技术还是要学好的。小编下面给大家分享一份成为高级工程师学习路线,如果想学习高级UI、性能优化、移动架构师、 NDK、混开发等Android高阶开发的朋友可以加下我的Android架构群:887084983,还有免费的学习资料及面试资料领取~

 

如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

 

Android架构师之路很漫长,一起共勉吧!

0
0
分享到:
评论

相关推荐

    阿里Android开发规范

    **阿里Android开发规范详解** 在Android开发领域,代码规范的重要性不言而喻。它不仅可以提升代码的可读性,使得团队协作更为顺畅,还能降低维护成本,提高软件质量。阿里作为国内知名的互联网企业,有着一套完善的...

    阿里巴巴Android开发规范手册

    《阿里巴巴Android开发规范手册》是阿里巴巴集团为Android开发者提供的一份详尽的开发指南,旨在提升团队协作效率,保证代码质量,以及优化软件性能。这份规范涵盖了从编码风格、命名规则到项目结构、异常处理等多个...

    阿里Android开发手册最新版

    《阿里巴巴Android开发手册》是Android开发者们不可或缺的参考资料,它汇集了阿里巴巴集团在Android开发领域的最佳实践和规范,旨在提升代码质量、优化开发流程、确保软件稳定性和性能。本手册覆盖了从编码风格、...

    阿里Android+Java规范

    阿里Android和Java规范是软件开发领域中的重要指南,旨在提高代码质量、提升团队协作效率以及维护项目的可持续性。这两份规范分别针对Android应用开发和Java后端开发,为开发者提供了详细的编码、设计、测试和文档...

    阿里巴巴Android开发代码规范(1.0.1)

    总的来说,阿里巴巴Android开发代码规范1.0.1版为开发者提供了一套完整的指南,从基础的命名规范到高级的架构设计和性能优化,全方位地提升了开发者的专业素养,对于打造高质量的Android应用具有重要的指导意义。...

    阿里Android开发手册.pdf

    《阿里巴巴Android开发手册》是Android开发者们不可或缺的参考资料,它由阿里巴巴集团精心编撰,旨在为开发者提供一套完整的Android开发规范和技术指南。手册涵盖了从基础编程到高级优化的多个方面,帮助开发者提升...

    阿里Android规范

    阿里Android规范是一套由阿里巴巴集团推出的针对Android应用开发的详细指南,旨在提升代码质量、提高开发效率、确保软件稳定性及可维护性。这份规范涵盖了编码风格、项目结构、设计模式、性能优化、测试策略等多个...

    阿里巴巴Android开发手册 (规约)

    《阿里巴巴Android开发手册》是阿里巴巴集团为Android开发者制定的一套规范和最佳实践,旨在提高代码质量和团队协作效率。这份手册涵盖了编码规约、异常处理、性能优化、安全指南等多个方面,是Android开发者不可或...

    阿里云Android技术手册

    阿里云Android开发技术手册,大厂的代码规范以及优化原则

    Android Studio 阿里java代码规范化插件与开发手册、android开发手册

    本资源包含三份关键文档:阿里Android开发手册、阿里Java开发规范以及Alibaba Java Coding Guidelines的插件,旨在帮助开发者遵循阿里巴巴的编码标准。 1. **阿里Android开发手册**: 这份手册详细阐述了Android...

    阿里巴巴编码规范AS工具插件

    该插件名为"Alibaba Java Coding Guidelines",版本为1.0.5,旨在帮助开发人员在使用IntelliJ IDEA或Android Studio等集成开发环境(IDE)时,自动检查并提示代码是否符合阿里巴巴的编码规范。 阿里巴巴作为全球...

    阿里巴巴Android面试题集(答案解析)1

    阿里巴巴的Android面试题集主要涵盖了计算机基础、数据结构与算法、Java编程、Android技术以及一些扩展领域的知识。以下是对这些知识点的详细解析: **第一章 计算机基础面试题** 这部分通常包括网络、操作系统和...

    阿里巴巴Android开发手册.pdf

    《阿里巴巴Android开发手册》是Android开发者们不可或缺的参考资料,它由阿里巴巴集团的移动开发团队精心编纂,旨在规范和提升Android应用的开发质量和效率。这份手册涵盖了从编码规范、项目结构到性能优化等多个...

    阿里巴巴安卓开发规范

    这份规范源自阿里巴巴集团在长期的Android开发实践中积累的经验和最佳实践,对于任何想要在Android平台上进行专业开发的工程师来说,都具有极高的参考价值。 1. **命名规范**: - 命名应清晰、简洁且具有描述性,...

    阿里Android开发手册

    《阿里Android开发手册》是阿里巴巴集团为Android开发者提供的一份详尽且实用的开发指南,旨在提升开发效率、代码质量和团队协作能力。这份手册涵盖了Android应用开发的各个方面,包括编码规范、性能优化、稳定性...

    阿里巴巴 编程规范 android java

    《阿里巴巴Android开发手册》是阿里巴巴集团无线开发同学在手淘、天猫、闲鱼、钉钉等APP开发优化过程中经验的结晶,这些经验指导他们写出更好的代码来保障App的性能、安全和稳定性,现在手册已开放。本认证是对您的...

    Android 开发规范与优化

    综合了阿里巴巴的开发规范,并且提出了优化方案。期待大家追加

Global site tag (gtag.js) - Google Analytics