在Android中使用反射到底有多慢?

在Android中使用反射到底有多慢?

反射(Reflection)在Java和安卓开发过程中非常有用,但是反射的使用往往是APP严重性能问题的根本原因。下面我们通过分析几个真实的案例来帮助我们更直观的理解这个问题。

两个真实的案例

第一个案例是纽约时报安卓客户端。在NimbleDroid的帮助下,纽约时报的开发者发现Gson中的type adapter使用了反射,增加了APP700毫秒的启动时间,他们通过自行实现自定义的type adapter解决了这一问题。

第二个案例是大型图片分享平台Photobucket,反射的使用也是它们的一个巨大性能瓶颈:

com.photobucket.android Icicle Graph构造 com.photobucket.api.client.jersey.UserClient constructor 花费了660毫秒

我们可以看到构造 com.photobucket.api.client.jersey.UserClient 花费了660毫秒。进一步分析冰柱图,我们可以看到延迟的来源是对反射的使用:

com.photobucket.android Iricle Graph存在大量反射的调用,例如:java.lang.Class.getGenericInterfaces

getGenericInterfaces() 函数会返回一个类型直接实现的接口类型,这里对它进行了5次调用,每次耗时大约81毫秒。当然,如果只看单次调用花费的时间可能影响不大,但是所有调用的总时间就很长了,仅仅是对这个方法的调用就造成了大约600毫秒的启动延迟。现在让我们更深入的分析一下为什么这个方法的调用会耗费如此长的时间。

我们发现,这个库支持开发者通过注解(annotation)来定义REST API,但问题是这个库并没有在编译期间去处理这些注解,而是在运行时进行通过反射进行处理,从性能的角度来看,这一点是灾难性的。

微基准测试(Micro-benchmarks)

我们创建了一个简单地测试代码来测试反射究竟有多慢。

我们在 android.app.Activity 类中反复进行了10,000次操作,代码如下:

[代码]java代码:

?
1
2
3
4
Class<!--?--> clazz = android.app.Activity. class ;
for ( int i = 0 ; i < 10000 ; i++) {
     clazz.getFields();
}


我们还创建了两个测试用例,通过创建一个空的类型的实例,来测试反射造成的开销,代码如下:

[代码]java代码:

?
1
2
3
4
5
6
7
8
9
try {
     for ( int i = 0 ; i < 1_000_000; i++) {
         DummyItem. class .newInstance();
     }
} catch (InstantiationException e) {
     e.printStackTrace();
} catch (IllegalAccessException e) {
     e.printStackTrace();
}


下面是我们的测试结果(时间单位均为毫秒,用的是有真正使用的手机,所以结果更接近真实用户体验):

 NEXUS 5 (6.0) ARTGALAXY S5 (5.0) ARTGALAXY S3 mini (4.1.2) Dalvik
getFields1108162627083
getDeclaredFields3479517687
getGenericInterfaces16232927
getGenericSuperclass247298665
makeAccessible14147449
getObject21167127
setObject21201161
createDummyItems312358774
createDummyItemsWithReflection133263842891

显然,在Android中使用反射非常慢,使用反射的测试执行分别需要1332毫秒、6384毫秒、2891毫秒,而不使用反射则只需要312毫秒、358毫秒、774毫秒。一个有趣的现象是,安卓5.0 ART环境下,高端设备使用反射耗费的时间,比安卓4.1 Dalvik环境下的低端设备更长,只有安卓6.0 ART环境才提升了反射的性能,但是反射仍然非常慢。

更多的真实案例

ActiveAndroid是另一个使用了反射技术的库,让我们通过测试一些Google Play商店的APP来分析反射对APP启动速度的影响。

下面是Scribd的测试结果:

Scribd Iricle Graph调用 com.activeandroid.ActiveAndroid.initialize 耗费1093毫秒

Myntra也存在同样的问题:

Myntra Iricle Graph调用 com.activeandroid.ActiveAndroid.initialize 耗费1421毫秒

我们可以看到,ActiveAndroid的初始化耗时超过1秒。1秒其实已经很长了,尤其是考虑到用户对APP启动时间的期望是2秒以内

总结一下,在Android中使用反射非常之慢。为了向用户提供最流畅的用户体验,我们强烈建议:

尽可能避免反射的使用(以及使用了反射的第三方库),尤其是使用类型反射来对Java对象进行序列化操作。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值