Robolectric单元测试报错“org.mockito.exceptions.base.MockitoException Caused by: java.lang.ClassCastException”

使用Robolectric进行Android代码测试的时候,随着测试用例的增多,可能会报告如下错误(Windows下常见):

原因为Mockto使用了编译缓存导致加载类的时候出现异常。解决方法是禁止Mockto缓存测试类的代码。

Android测试项目的src/test/java下创建一个名为org.mockito.configuration的包,然后实现一个名为MockitoConfiguration.java的类,如下:

这样当再次执行测试用例的时候,就已经不使用缓存了。

参考链接


Android最简单的HTTP服务器

目前在对Android的代码进行功能测试的时候,需要服务器返回一个数据来测试整个流程是否正确。不希望引入第三方的JAR包,因此需要一个特别简单的HTTP服务器。

网上查询了一下,找到可用的代码如下:

参考链接


Create a simple HTTP Web Server in Java

IntelliJ IDEA 提示 'try' can use automatic resource management Java7新特性

IntelliJ IDEA会提示

Java 7 build 105版本开始,Java 7的编译器和运行环境支持新的try-with-resources语句,称为ARM 块(Automatic Resource Management) ,自动资源管理。

新的语句支持包括流以及任何可关闭的资源。

使用try-with-resources语句来简化代码如下: 

在这个例子中,数据流会在try执行完毕后自动被关闭,前提是,这些可关闭的资源必须实现java.lang.AutoCloseable接口。

注:目前java.lang.AutoCloseable接口的子接口或实现类如下:

所有已经子接口: 

 所有已知实现类: 

对于Android用户来说,只有编译工程的 minSdkVersion大于 19(Android 4.4)的时候才能生效。

参考链接


Android ViewPager存在界面卡住的BUG

最近开发的项目出现界面莫名其秒的卡住,直到发生ANR异常退出的问题。
问题排查许久,才发现是由于使用的ViewPager导致的。
ViewPager中使用setCurrentItem长距离设置当前显示的位置的时候,比如0->600,600->10001000->500 这样的长距离跳转。会导致函数卡住在

函数中很长时间, 甚至一直不能跳出循环。具体卡住的的循环代码如下:

上述代码中循环体会一直循环到N结束为止。而这个N是通过

赋值的。恰好,我们的代码中返回的是Integer.MAX_VALUE。咨询了同事,当时设置这个数值的目的是为了解决循环滚动展示才设置的。代码是直接抄的 Android无限广告轮播 自定义BannerView / 如何优雅的实现一个可复用的 PagerAdapter

通过Google搜索关键词 BannerAdapter extends PagerAdapter 可以找到类似的代码。

具体的复现BUG的例子代码如下:

完整的项目代码可以点击此处下载 ViewPager / ViewPagerAppCompatV7。项目编译完成之后,多点击几次 Change Size 按钮就可以复现界面长时间卡住的情况。第二个项目是支持appcompat-v7:18.0.0的版本,目的是观察各个版本的代码是否存在不同,结论是,都一样

这个问题目前的解决方法就是保证ViewPager中的数据不要太多,比如底层分页显示,每页不要太多。另外就是不要大距离跳转,否则会有很多问题

如果能把android.support升级到androidx的话,可以试试最新的ViewPager2,最新的ViewPager2是用RecyclerView 实现的,应该不会有以前的问题了。

这个问题,我已经向Google提交了BUG,估计最后的答复也是要升级到androidx

参考链接


Android无限广告轮播 - ViewPager源码分析

接口幂等性这么重要,它是什么?怎么实现?

什么是幂等性?

对于同一笔业务操作,不管调用多少次,得到的结果都是一样的。

幂等性设计

我们以对接支付宝充值为例,来分析支付回调接口如何设计?

如果我们系统中对接过支付宝充值功能的,我们需要给支付宝提供一个回调接口,支付宝回调信息中会携带(out_trade_no【商户订单号】,trade_no【支付宝交易号】),trade_no在支付宝中是唯一的,out_trade_no在商户系统中是唯一的。

回调接口实现有以下实现方式。

方式1(普通方式)

过程如下:

1.接收到支付宝支付成功请求
2.根据trade_no查询当前订单是否处理过
3.如果订单已处理直接返回,若未处理,继续向下执行
4.开启本地事务
5.本地系统给用户加钱
6.将订单状态置为成功
7.提交本地事务

上面的过程,对于同一笔订单,如果支付宝同时通知多次,会出现什么问题?当多次通知同时到达第2步时候,查询订单都是未处理的,会继续向下执行,最终本地会给用户加两次钱。

此方式适用于单机其,通知按顺序执行的情况,只能用于自己写着玩玩。

方式2(jvm加锁方式)

方式1中由于并发出现了问题,此时我们使用java中的Lock加锁,来防止并发操作,过程如下:

1.接收到支付宝支付成功请求
2.调用java中的Lock加锁
3.根据trade_no查询当前订单是否处理过
4.如果订单已处理直接返回,若未处理,继续向下执行
5.开启本地事务
6.本地系统给用户加钱
7.将订单状态置为成功
8.提交本地事务
9.释放Lock锁

分析问题:
Lock只能在一个jvm中起效,如果多个请求都被同一套系统处理,上面这种使用Lock的方式是没有问题的,不过互联网系统中,多数是采用集群方式部署系统,同一套代码后面会部署多套,如果支付宝同时发来多个通知经过负载均衡转发到不同的机器,上面的锁就不起效了。此时对于多个请求相当于无锁处理了,又会出现方式1中的结果。此时我们需要分布式锁来做处理。

方式3(悲观锁方式)

使用数据库中悲观锁实现。悲观锁类似于方式二中的Lock,只不过是依靠数据库来实现的。数据中悲观锁使用for update来实现,过程如下:

1.接收到支付宝支付成功请求
2.打开本地事物
3.查询订单信息并加悲观锁

4.判断订单是已处理
5.如果订单已处理直接返回,若未处理,继续向下执行
6.给本地系统给用户加钱
7.将订单状态置为成功
8.提交本地事物

重点在于for update,对for update,做一下说明:
1.当线程A执行for update,数据会对当前记录加锁,其他线程执行到此行代码的时候,会等待线程A释放锁之后,才可以获取锁,继续后续操作。
2.事物提交时,for update获取的锁会自动释放。

方式3可以正常实现我们需要的效果,能保证接口的幂等性,不过存在一些缺点:
1.如果业务处理比较耗时,并发情况下,后面线程会长期处于等待状态,占用了很多线程,让这些线程处于无效等待状态,我们的web服务中的线程数量一般都是有限的,如果大量线程由于获取for update锁处于等待状态,不利于系统并发操作。

方式4(乐观锁方式)

依靠数据库中的乐观锁来实现。

1.接收到支付宝支付成功请求
2.查询订单信息

3.判断订单是已处理
4.如果订单已处理直接返回,若未处理,继续向下执行
5.打开本地事物
6.给本地系统给用户加钱
7.将订单状态置为成功,注意这块是重点,伪代码:

注意:
update t_order set status = 1 where order_id = trade_no where status = 0; 是依靠乐观锁来实现的,status=0作为条件去更新,类似于java中的cas操作;关于什么是cas操作,可以移步:什么是 CAS 机制 ( http://www.itsoku.com/article/63 )?
执行这条sql的时候,如果有多个线程同时到达这条代码,数据内部会保证update同一条记录会排队执行,最终最有一条update会执行成功,其他未成功的,他们的num为0,然后根据num来进行提交或者回滚操作。

方式5(唯一约束方式)

依赖数据库中唯一约束来实现。

我们可以创建一个表:

对于任何一个业务,有一个业务类型(ref_type),业务有一个全局唯一的订单号,业务来的时候,先查询t_uq_dipose表中是否存在相关记录,若不存在,继续放行。

过程如下:

1.接收到支付宝支付成功请求
2.查询t_uq_dipose(条件ref_id,ref_type),可以判断订单是否已处理

3.判断订单是已处理
4.如果订单已处理直接返回,若未处理,继续向下执行
5.打开本地事物
6.给本地系统给用户加钱
7.将订单状态置为成功
8.向t_uq_dipose插入数据,插入成功,提交本地事务,插入失败,回滚本地事务,伪代码:

说明:
对于同一个业务,ref_type是一样的,当并发时,插入数据只会有一条成功,其他的会违法唯一约束,进入catch逻辑,当前事务会被回滚,最终最有一个操作会成功,从而保证了幂等性操作。
关于这种方式可以写成通用的方式,不过业务量大的情况下,t_uq_dipose插入数据会成为系统的瓶颈,需要考虑分表操作,解决性能问题。
上面的过程中向t_uq_dipose插入记录,最好放在最后执行,原因:插入操作会锁表,放在最后能让锁表的时间降到最低,提升系统的并发性。

关于消息服务中,消费者如何保证消息处理的幂等性?
每条消息都有一个唯一的消息id,类似于上面业务中的trade_no,使用上面的方式即可实现消息消费的幂等性。

总结

1.实现幂等性常见的方式有:悲观锁(for update)、乐观锁、唯一约束
2.几种方式,按照最优排序:乐观锁 > 唯一约束 > 悲观锁

参考链接


接口幂等性这么重要,它是什么?怎么实现?

Java Calendar 获取上下午

参考链接


Java Calendar 获取上下午

Java中double/float四舍五入取整(ceil/floor/round)

执行测试:

参考链接


JAVA中double转int类型按四舍五入取整(实用)

macOS Catalina(10.15.4)/IntelliJ IDEA 2018.3/Tomcat 9.0.33/Maven项目调试报错"Caused by: java.util.zip.ZipException: zip file is empty"

macOS Catalina(10.15.4)/IntelliJ IDEA 2018.3/Tomcat 9.0.33/Maven项目调试时报错,这个项目以前是可以正常调试的,一段时间之后,就不能正常调试之下了

继续阅读macOS Catalina(10.15.4)/IntelliJ IDEA 2018.3/Tomcat 9.0.33/Maven项目调试报错"Caused by: java.util.zip.ZipException: zip file is empty"

struts2升级之后报错“java.lang.NoSuchMethodError: org.apache.commons.lang3.reflect.MethodUtils.getAnnotation”

struts2 从如下版本:

升级到

之后报错如下:

解决方法为升级

到更高的版本,如下:

参考链接


java.lang.NoSuchMethodError: org.apache.commons.lang3.math.NumberUtils.isCreatable(Ljava/lang/String

ubuntu 16.04编译使用BinNavi

 

如果代码下载存在困难,可以本站下载一份拷贝。 点击此处下载 binnavi

参考链接


逆向分析神器BinNavi开源了