RecycleView设置顶部分割线(记录一个坑)

大家都知道,想给RecycleView设置分割线可以重写RecyclerView.ItemDecoration

项目过程中,遇到一个需求:RecycleView顶部有一条灰色的间隔,我想到了给RecycleView设置分割线的方法,当然只给第一个Item设置,而且在上方。

 

在onDrawOver方法中可以绘制分割线。

这里有一个需要注意的坑,调试了很久,最终才发现,难受。

在onDrawOver里面,一开始我设置top = 0;因为绘制在顶部嘛。结果出现了一个现象,顶部分割线一直停留在顶部,不会跟着移动。最后改为int top = child.getTop() – params.topMargin – mDivider.getIntrinsicHeight();才成功了。为什么呢?

因为直接写top = 0;这是绝对位置了,要让分割线也跟着滑动,需要用的是相对位置,相对于item的位置,这样才能够跟着item滑动

 

调用

recyclerView.addItemDecoration(new MyDividerItemDecoration(this, R.drawable.item_decoration));

item_decoration代码如下:


 

或者我们可以直接代码中创建Drawable,然后传进去:


 

尊重劳动成果,转载请标明出处:http://77blogs.com/?p=569

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

Java list.remove( )方法需要注意的地方

List integerList = new ArrayList<>();

当我们要移除某个Item的时候

remove(int position):移除某个位置的Item

remove(object object):移除某个对象

 

那么remove(12)到底是移除第12的item,还是移除内容为12的Item。

那就要看12到底是int类型还是Integer类型,如果是int类型那么就是移除第12的item,如果是第Integer类型,那么就是移除内容为12的Item。

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

调用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

TortoiseGit 连接Git服务器不用每次输入用户名和密码的方法

每次git push 都要输入用户名和密码。

虽然安全,但在自己电脑上每次都输有些麻烦,如何记住用户名和密码呢?

试了很多方法,找到这个最简单,亲测可行。

当你配置好git后,在C盘C:\Users\administrator下的 .gitconfig 的文件(如果找不到,直接搜索),里面会有你先前配好的name 和email,只需在下面加增加credential:

[user]
name = xxxxxx
email = xxxxxx@abc.com

[credential]
helper = store

下次再输入用户名和密码时,git就会记住,从而在

C:\Users\administrator目录下形成一个.git-credentials 文件,里面就是保存的你的用户名和密码。

https://username:yourpassword@git.oschina.net

这样以后再连接git.oschina代码库时,就不用再输入用户名和密码了!

2、在命令行输入下面的命令,重新输入一次密码即可记住

转载自:https://www.cnblogs.com/bldf/p/6052459.html

Android Studio的git功能的使用介绍

本文介绍Android Studio(下面简称AS)中git工具的一些简单使用。因为AS为git的使用提供了很多人性化的图形界面操作,在很大程度上可以增加开发效率。本文面向新手,题主自己也是新手一枚,如有错误还望指正。


首先,往下看之前,你需要了解git的一些基本使用。如果你还不清楚git的基本使用,网上教程很多。

初次使用AS自带的git工具的配置

初次使用AS自带的git工具需要设置一些配置,如果你已配置过,可跳过该部分内容。

首先你需要下载git,然后打开AS的git设置,路径如下,选择你安装在你电脑上的git的路径,选择完可以点击”test”,显示成功则表示AS的git功能已可以使用。
“setting”->”version control”->”git”->”path to git executable”

AS的git使用介绍

1.fetch+marge/pull

2.add/commit

3.push

以上是git基本命令,如果你不熟悉,建议先百度学习下git的基本使用。上述命令在git的命令行工具下经常使用,如果不想用命令行也可以使用AS的图形界面操作,”右键”->”git”(如下图)。里面有很多git的命令,可以直接点击使用。

底栏靠右边有显示当前你处于哪个分支,如图。左键点击可以进入选择切换、新建、合并分支等功能。

上面介绍的功能更多情况下都可以直接在命令行工具下完成,毕竟并不会很麻烦。这里只是简单介绍这些命令在AS的哪些地方,如果你确实懒得用命令行,可以自行尝试在AS上使用git的基础命令试试。下面介绍一些相对于命令行操作下更方便,更实用的一些图形化操作。

1.解决冲突

冲突在git的使用过程中是特别常见的现象,团队里每个人所负责的代码块或多或少都会有交叉,所以能更快捷的解决冲突自然是更好的了。
如果有冲突的代码块,git会将相关的代码块用”>>>>>>>”和”=======”包围起来,告诉你这个地方有冲突了,需要你手工去解决。但当你代码量比较多的时候,或者有冲突的两个代码块距离比较远的时候,或多或少都会有些麻烦。毕竟你需要去查看起冲突的代码块,对比,排查原因,再手工修改。
AS提供的图形界面操作可以让人很直观的发现哪些代码块起冲突了,这里就用图简单展示下,你可以自己去尝试下各种冲突,然后实践下,自己体会是不是会比命令行操作更方便。
如果出现冲突就会出现下图,AS提供三种解决方案,”Accept ****”就是字面上的意义,”Merge”是手动解决,AS会提供更直观的图形界面,如第二张图。

如图所示,最左边是你本地项目当前所处的分支,最右边是你要与你当前分支进行合并的分支。中间则是合并后的结果预览。图上那些有颜色覆盖的代码就是有冲突的地方,不同颜色代表不同意思,这里就不详说了,等你使用多了自然就熟悉了。
分隔线里的”Ⅹ >>”是AS提供给你的操作方法,多的也不说了,建议你多自己尝试下。

2.查看各个版本代码

项目的开发经常是迭代开发,有时经常需要查看一下以前版本的代码。或者当我正在开发时,远程仓库更新了最新版本,我想查看一下最新版本添加了哪些代码等等情况。AS都提供了很好的图形界面操作。

点击底栏靠左边的”version control”,出现如下图。其中,左边表示的是你的仓库的每一次commit记录,包括提交信息,提交者以及时间。右边可以查看任意版本的仓库对应修改的代码。这个特别方便,当你想查看某个版本是谁提交的,以及他做了哪些修改,你不必再去登陆github或其他托管仓库,AS里就可以查看。

3.比较不同版本库的代码之间的区别

有时我不仅仅想知道最新版本添加了哪些代码,还想知道添加的这些代码跟自己本地正在使用的版本代码相比,哪些地方被修改了。或者比较不同版本间的不同等情况。AS也提供了很人性化的图形界面操作。
选择你要比较的java文件也可以是整个项目,右键选择git,选择”compare with ****”命令,根据你的需要自行选择是与远程库的最新版本比较,还是与其他分支或其他版本库比较。

右边是你本地的代码,左边是你选择的要比较的版本库的同一java文件的代码。图上红框中的是版本号。该功能可以很方便的就看出不同版本的同一文件的区别。有颜色的代码就是修改过或者新添加或者删除的代码。

 

转载自:https://www.cnblogs.com/dasusu/p/5372840.html

使用git怎么更新Android studio

  1. 打开Android studio开发代码软件,进入到界面中之后,进行点击菜单中的“VCS”的选项。

    使用git怎么更新Android studio项目
  2. 就弹出了下拉菜单的选项,在下拉的菜单中有一项为“update project”的选项。

    使用git怎么更新Android studio项目
  3. 进入到update project的设置框中,更新类型分为合并,编辑,使用默认配置,工作目录的变更为暂存,搁置。可以按默认设置即可。

    使用git怎么更新Android studio项目
  4. 如果更新的文件较多的话,需要一段的时间,更新完成之后会提示更新完成。

    使用git怎么更新Android studio项目
  5. 第二种快捷的方式,直接点击菜单中更新的按钮。

    使用git怎么更新Android studio项目
  6. 会直接进入到update project的设置框中,根据需求进行设置。

设置抓包工具Fiddler的host

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

大家都知道在电脑本地上有一个Host文件。

Hosts文件主要作用是定义IP地址和主机名的映射关系,是一个映射IP地址和主机名的规定。可以用文本文件打开!当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,浏览器会立即打开对应网页,如果没有找到,则浏览器会将网址提交DNS服务器进行IP地址解析。这也是提高快速打开网页的方法!

Host文件的位置

windows xp/2003/vista/2008/7/8用户HOSTS文件是在“c:\windows\system32\drivers\etc,注意这个文件一定是在系统盘,如果你的系统在D盘请自行修改前面的盘符。

 

我们进行App开发,有时测试时需要连接到不同环境的网络,比如预发环境的网络。那我们就要进行设置才能在该环境下抓取到包。

1、修改Fiddler的Host。

点击Fiddler菜单上的Tools -> Host会出现Host文件,文件里写上

那就代表了当你访问www.baidu.com的时候,就会对应到IP地址114.2.33.443(乱写的),这个就是预发环境了。

 

这样当我们手机wify设置为代理模式(IP地址写上电脑的IP),与电脑在同个局域网后,我们就能在预发环境下也能抓取到包。

(具体连接操作看我另一片微博https://www.cnblogs.com/tangZH/p/9306761.html

怎么使用Fiddler进行抓包

  1. 启动Fiddler,打开菜单栏中的 Tools > Fiddler Options,打开“Fiddler Options”对话框。

     

  2.  在Fiddler Options”对话框切换到“Connections”选项卡,然后勾选“Allow romote computers to connect”后面的复选框,然后点击“OK”按钮。

     

     

     

  3.  在本机命令行输入:ipconfig,找到本机的ip地址。

     

     

     

  4.  打开android设备的“设置”->“WLAN”,找到你要连接的网络,在上面长按,然后选择“修改网络”,弹出网络设置对话框,然后勾选“显示高级选项”。

     

  5.  在“代理”后面的输入框选择“手动”,在“代理服务器主机名”后面的输入框输入电脑的ip地址,在“代理服务器端口”后面的输入框输入8888,然后点击“保存”按钮。

     

  6. 然后启动android设备中的浏览器,访问百度的首页,在fiddler中可以看到完成的请求和响应数据。

     

    最后注明一点,fiddler设置完之后需要重启才能够生效

配置ADB到Windows环境变量

adb 命令可以帮我们快速的管理连接的手机设备,例如执行一些安装apk,卸载apk命令,对于熟悉linux系统的人,可以方便的管理手机目录操作手机文件,还可以通过adb命令查看手机的系统日志等操作。

接下来讲一讲如何在windows系统下配置ADB环境变量:

  1. “计算机”右击属性进入系统设计界面,如下图所示。选择“高级系统设置”

    配置ADB到Windows环境变量
  2. 进入后选择“高级”–“环境变量”

    配置ADB到Windows环境变量
  3. 在系统变量中找到“path”,选中后点击“编辑”

    配置ADB到Windows环境变量
  4. 将ADB文件路径放在最后,注意使用英文输入法的  ;   隔开。

    笔者的ADB文件路径在:D:\platform-tools  请读者根据自己的路径参考。

    配置ADB到Windows环境变量
  5. 测试ADB

    在命令行中输入adb,看到如下信息说明完成配置ADB到Windows环境变量。

    配置ADB到Windows环境变量
    配置ADB到Windows环境变量