findlibrary returned null

转载请标明出处,维权必究:http://77blogs.com/?p=478

该错误是在加载so库的时候出现的,就是找不到so库。

一、检查jinLibs目录下是否有so库

二、gradle的android{}里面是否有设置:


 

三、兼容性是否正确:

我们使用so库,需要设置该so库兼容的cpu架构,通常会在jniLibs文件夹里面新建文件夹:armeabi,armeabi-v7a,x86,然后把so库放在里面。

目前主流的Android设备肯定是armeabi-v7a架构的,然后就是x86和armeabi。

那么Android设备在运行程序时如何选择加载包中的哪个so呢?

X86:

x86设备会在项目中的 libs文件夹寻找是否含有x86文件夹,如果含有x86文件夹,则默认为该项目有x86对应的so可运行的,只有x86文件夹而文件夹下没有so,程序运行也是会出现findlibrary returned null的错误的;如果工程本身不含有x86文件夹,则会寻找armeabi或者armeabi-v7a文件夹,兼容运行。

 

armeabi-v7a:

现在大多数设备都是armeabi-v7a架构的,该Android设备当然优先寻找libs目录下的armeabi-v7a文件夹,同样,如果只有armeabi-v7a文件夹而没有 so会报错;如果找不到armeabi-v7a文件夹,则寻找armeabi文件夹,兼容运行该文件夹下的so,但是不能兼容运行x86的so。所以项目中如果只含有x86的so,在armeabi和armeabi-v7a也是无法运行的。

 

armeabi

如果项目只包含了 armeabi,那么在所有Android设备都可以运行

以上就是不同CPU架构运行时加载so的策略。

 

指定app兼容哪种架构:

app.gradle里面


 

指定要ndk需要兼容的架构,这样其他文件夹的依赖包里的so会被过滤掉,指定了之后,除了armeabi其它文件夹里的so包不会被打包进apk。

想知道自己的so包有没有被打包进去,一个简单的方法,就是将软件包的apk后缀改为zip后缀,然后解压,看看有没有。

 

平台的优缺点:

1、打包出的x86的so,总会比armeabi平台的体积更小,若是对这个有要求,可以在打so包的时候支持x86

2、armeabi-v7a 第7代 ARM v7,使用硬件浮点运算,具有高级扩展功能(支持 armeabi 和 armeabi-v7a,目前大部分手机都是这个架构),armeabi-v7a确实是可以兼容armeabi的,而且性能更优,若是对性能有要求,so包可以支持armeabi-v7a

 

java.lang.IllegalArgumentException: Called attach on a child which is not detached: ViewHolder

转载请标明出处,维权必究:http://77blogs.com/?p=482

在项目过程中出现了上述错误。

会出现这样的错误是在我使用:

notifyItemRemoved(position);

notifyItemRangeChanged(position, mList.size() – position);

的时候出现的,其实是因为我的RecycleView有FootView,而当我删除最后一个Item的时候,notifyItemRangeChanged(position, mList.size() 里面的position对应的Item就变成FootView了(调用notifyItemRemoved(position);并不会刷新position,可参考:http://77blogs.com/?p=483),由于FootView没有绑定ViewHolder,所以调用notifyItemRangeChanged(position, mList.size() – position)相当于刷新FootView,出现了该错误。

Android apk安装时出现“解析软件包错误”

链接:http://77blogs.com/?p=558

有时候在安装apk的时候会出现解析软件包出错

 

(Android studio)解决方法如下:

关闭Instant Run功能:

File-Settings-…看下图:

将红色框内的勾取消。

 

如果还是不行,那么便在工程的gradle.properties里添加 android.injected.testOnly=false

Android Studio调试手机或者安装APK的时候出现install failed test only

转载标明:http://77blogs.com/?p=561

1、检查\app\src\main\AndroidMainfest.xml中是否有testOnly属性为true,如果有去掉或者改为false

2、检查Android Studio和gradle版本是否为alpha版本,换为稳定版本

3、检查是否关闭Instant Run功能,关掉此功能

4、检查是否使用alpha版本的依赖库,换成稳定版本

5、如果以上均无法解决,就在gradle.properties文件中添加android.injected.testOnly=false

 

一般情况下3、5两个一起执行就Ok了。

 

关闭Instant Run功能的具体方法如下:

File-Settings-…如下图:将红色框内的箭头取消

 

Scrapped or attached views may not be recycled

在使用recycleView的时候出现了错误Scrapped or attached views may not be recycled

原因:

view没有被recycled,recyclerView的数据进行清空操作之后,在重新添加数据之前忘记了通知界面进行重新绘制,所以崩溃。
解决方法只要在clear数据之后代码中记得加notifyDataSetChanged() 即可。

 

参考链接:https://blog.csdn.net/u013106366/article/details/54024113

还发现一种情况是在上拉加载更多或者下拉刷新之后忘记使控件重置了。要记得上拉或者下拉之后加上代码:


 

转载请标明:http://77blogs.com/?p=567

java.util.ConcurrentModificationException 异常问题详解

转载自:https://www.cnblogs.com/snowater/p/8024776.html

 

环境:JDK 1.8.0_111

在Java开发过程中,使用iterator遍历集合的同时对集合进行修改就会出现java.util.ConcurrentModificationException异常,本文就以ArrayList为例去理解和解决这种异常。

一、单线程情况下问题分析及解决方案

1.1 问题复现

先上一段抛异常的代码。

复制代码

复制代码

在这个代码中展示了两种能抛异常的实现方式。

1.2、问题原因分析

先来看实现方法一,方法一中使用Iterator遍历ArrayList, 抛出异常的是iterator.next()。看下Iterator next方法实现源码

复制代码

复制代码

在next方法中首先调用了checkForComodification方法,该方法会判断modCount是否等于expectedModCount,不等于就会抛出java.util.ConcurrentModificationExcepiton异常。

我们接下来跟踪看一下modCount和expectedModCount的赋值和修改。

modCount是ArrayList的一个属性,继承自抽象类AbstractList,用于表示ArrayList对象被修改次数。

整个ArrayList中修改modCount的方法比较多,有add、remove、clear、ensureCapacityInternal等,凡是设计到ArrayList对象修改的都会自增modCount属性。

在创建Iterator的时候会将modCount赋值给expectedModCount,在遍历ArrayList过程中,没有其他地方可以设置expectedModCount了,因此遍历过程中expectedModCount会一直保持初始值20(调用add方法添加了20个元素,修改了20次)。

遍历的时候是不会触发modCount自增的,但是遍历到integer.intValue() == 5的时候,执行了一次arrayList.remove(integer),这行代码执行后modCount++变为了21,但此时的expectedModCount仍然为20。

在执行next方法时,遇到modCount != expectedModCount方法,导致抛出异常java.util.ConcurrentModificationException。

明白了抛出异常的过程,但是为什么要这么做呢?很明显这么做是为了阻止程序员在不允许修改的时候修改对象,起到保护作用,避免出现未知异常。引用网上的一段解释,点击查看解释来源

Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变。 当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。 所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

再来分析下第二种for循环抛异常的原因:

复制代码

复制代码

在for循环中一开始也是对expectedModCount采用modCount进行赋值。在进行for循环时每次都会有判定条件modCount == expectedModCount,当执行完arrayList.remove(integer)之后,该判定条件返回false退出循环,然后执行if语句,结果同样抛出java.util.ConcurrentModificationException异常。

这两种复现方法实际上都是同一个原因导致的。

1.3 问题解决方案

上述的两种复现方法都是在单线程运行的,先来说明单线程中的解决方案:

复制代码

复制代码

这种解决方案最核心的就是调用iterator.remove()方法。我们看看该方法源码为什么这个方法能避免抛出异常

复制代码

复制代码

在iterator.remove()方法中,同样调用了ArrayList自身的remove方法,但是调用完之后并非就return了,而是expectedModCount = modCount重置了expectedModCount值,使二者的值继续保持相等。

针对forEach循环并没有修复方案,因此在遍历过程中同时需要修改ArrayList对象,则需要采用iterator遍历。

上面提出的解决方案调用的是iterator.remove()方法,如果不仅仅是想调用remove方法移除元素,还想增加元素,或者替换元素,是否可以呢?浏览Iterator源码可以发现这是不行的,Iterator只提供了remove方法。

但是ArrayList实现了ListIterator接口,ListIterator类继承了Iter,这些操作都是可以实现的,使用示例如下:

复制代码

复制代码

二、 多线程情况下的问题分析及解决方案

单线程问题解决了,再来看看多线程情况。

2.1 问题复现

复制代码

复制代码

在个测试代码中,开启两个线程,一个线程遍历,另外一个线程遍历加修改。程序输出结果如下

复制代码

复制代码

2.2 问题分析

从上面代码执行结果可以看出thread2 遍历结束后,thread1 sleep完1000ms准备遍历第二个元素,next的时候抛出异常了。我们从时间点分析一下抛异常的原因

时间点 arrayList.modCount thread1 iterator.expectedModCount thread2 iterator.expectedModCount
thread start,初始化iterator 20 20 20
thread2.remove()调用之后 21 20 21

 

 

 

两个thread都是使用的同一个arrayList,thread2修改完后modCount = 21,此时thread2的expectedModCount = 21 可以一直遍历到结束;thread1的expectedModCount仍然为20,因为thread1的expectedModCount只是在初始化的时候赋值,其后并未被修改过。因此当arrayList的modCount被thread2修改为21之后,thread1想继续遍历必定会抛出异常了。

在这个示例代码里面,两个thread,每个thread都有自己的iterator,当thread2通过iterator方法修改expectedModCount必定不会被thread1感知到。这个跟ArrayList非线程安全是无关的,即使这里面的ArrayList换成Vector也是一样的结果,不信上测试代码:

复制代码

复制代码

执行后输出结果为:

复制代码

复制代码

test5()方法执行结果和test4()是相同的,那如何解决这个问题呢?

2.3 多线程下的解决方案

2.3.1 方案一:iterator遍历过程加同步锁,锁住整个arrayList

复制代码

复制代码

这种方案本质上是将多线程通过加锁来转变为单线程操作,确保同一时间内只有一个线程去使用iterator遍历arrayList,其它线程等待,效率显然是只有单线程的效率。

2.3.2 方案二:使用CopyOnWriteArrayList,有坑!要明白原理再用,否则你就呆坑里吧。

我们先来看代码,很有意思咯

复制代码

复制代码

先不分析,看执行结果,这个执行结果重点关注字体加粗部分。

复制代码

复制代码

我们先分析thread2的输出结果,第一次遍历将4 5 6都输出,情理之中;第一次遍历后删除掉了一个元素,第二次遍历输出4 6,符合我们的预期。

再来看下thread1的输出结果,有意思的事情来了,thread1 仍然输出了4 5 6,什么鬼?thread1和thread2都是遍历list,list在thread1遍历第二个元素的时候就已经删除了一个元素了,为啥还能输出5?

为了了解这个问题,需要了解CopyOnWriteArrayList是如何做到一边遍历的同时还能一边修改并且还不抛异常的。

在这里不想再深入分析CopyOnWriteArrayList代码,后续会专门出一篇博客来解释这个类的源码的。

这里说一下CopyOnWriteArrayList的解决思路,其实很简单:

CopyOnWriteArrayList本质上是对array数组的一个封装,一旦CopyOnWriteArrayList对象发生任何的修改都会new一个新的Object[]数组newElement,在newElement数组上执行修改操作,修改完成后将newElement赋值给array数组(array=newElement)。

因为array是volatile的,因此它的修改对所有线程都可见。

了解了CopyOnWriteArrayList的实现思路之后,我们再来分析上面代码test6为什么会出现那样的输出结果。先来看下thread1和thread2中用到的两种遍历方式的源码:

复制代码

复制代码

 

复制代码

复制代码

这两种遍历方式有个共同的特点:都在初始化的时候将当前数组保存下来了,之后的遍历都将会遍历这个数组,而不管array如何变化。

时间点 CopyOnWriteArrayList的array thread1 iterator 初始化的Object数组 thread2 第一次遍历forEach初始化的Object数组 thread2 第二次遍历forEach初始化的Object数组
thread start 假设为A A A /
thread2 调用remove方法之后 假设为B A A B

 

 

 

有了这个时间节点表就很清楚了,thread1和thread2 start的时候都会将A数组初始化给自己的临时变量,之后遍历的也都是这个A数组,而不管CopyOnWriteArrayList中的array发生了什么变化。因此也就解释了thread1在thread2 remove掉一个元素之后为什么还会输出5了。在thread2中,第二次遍历初始化数组变成了当前的array,也就是修改后的B,因此不会有Integer.valueOf(5)这个元素了。

从test6执行结果来看,CopyOnWriteArrayList确实能解决一边遍历一边修改并且还不会抛异常,但是这也是有代价的:

(1) thread2对array数组的修改thread1并不能被动感知到,只能通过hashCode()方法去主动感知,否则就会一直使用修改前的数据

(2) 每次修改都需要重新new一个数组,并且将array数组数据拷贝到new出来的数组中,效率会大幅下降

此外CopyOnWriteArrayList中的ListIterator实现是不支持remove、add和set操作的,一旦调用就会抛出UnsupportedOperationException异常,因此test6注释代码34-41行中如果运行是会抛异常的。

参考文献:

http://lz12366.iteye.com/blog/675016

http://www.cnblogs.com/dolphin0520/p/3933551.html

http://blog.csdn.net/androiddevelop/article/details/21509345

调用android的getColor()方法出现 java.lang.NoSuchMethodError: android.content.res.Resources.getColor

1.java.lang.NoSuchMethodError: android.content.res.Resources.getDrawable/getColor
或者 java.lang.NoSuchMethodError:android.content.Context.getDrawable/geColor

 

原因:Context类的getDrawable(res)/geColor(res)方法和Resources的getDrawable(res,theme)/getColor(res.theme)都是API21才添加的,低版本系统无法找到该方法所以报异常。

 

解决办法:
使用Resources的getDrawable(res),但是该方法在API22已废弃。
使用ContextCompat.getDrawable(context,res)。

原文链接:https://blog.csdn.net/qq_22256565/article/details/52133565