近期由于项目需求,我们对网易新闻客户端做了 android 8.0的适配工作。经过一段时间的灰度完美上线了,大家都很开心。但好景不长,反馈系统了出现了几条让我们关注的问题,8.0系统上桌面 Widget 经常无法刷新数据。
看到此反馈,我们拉去了反馈用户的日志,发现确实是产生了问题,又查看了桌面 widget 的代码实现,发现这个问题果然是我们没有考虑到导致的。
网易新闻从2016年开始接入网络性能监控平台,便于提供网络监控和预警的功能。然而由于客户端对接的服务后台有很多,比如跟帖后台,广告后台,统计后台等等,再加上有不同的 CDN 服务提供商和内外部的 APM(性能监控)服务商;对接后台的增多就导致了我们在排查网络问题的时候难度很大,特别是针对一些非地域性问题,某一个用户单独出现的网络异常问题排查成本非常大。举一个比较具体的实例问题: 针对反馈平台用户反馈的网络图片无法显示问题,我们需要排查用户请求的状况,找出出错的环节。
针对上述问题我们一般的做法是: 拉取用户日志,查看网络请求是否正常发送,服务端正常返回。如果发送请求是正常的,我们需要排查服务端日志,是否有接收到这个请求。服务端如果没有接收到此请求,我们就需要调取 CDN 服务商的日志,查看 CDN 服务器是否接收到了此请求。在这样的一个排查过程中我们传统意义上的依据只有用户 ip 和请求的大概时间。然而通过 ip 和时间不能精确的从服务端日志找到这个请求,于是排查就陷入了僵局。
为了解决上述状况,我们制定了一套请求唯一标识的方案。何为请求唯一标识? 唯一标识就是指,客户端发送每一个请求之前,给此请求生成唯一 traceId,此 id 能够唯一标识一个请求。在 CDN 服务和我们后台服务以及我们的 APM 性能监控平台中,我们统一在日志中记录请求和其唯一 traceId。这样一来,我们只要知道了这个请求的唯一 traceId, 无论在哪个服务节点,我们都可以在日志中获取精准的该请求的详细日志信息。比如: APM 性能监控平台上报某用户某个请求是失败的,此时我们只需要拿此请求的 traceId 从后台日志和 CND 日志中查看该请求的信息,这样就清晰的定位了问题发生的环节,这样找到了出现异常的环节,剩下的事情就是具体的排查和解决了。具体的实现方案如下图解:
最近测试团队的妹子再测试 android 8.0的适配工作,提过来一个特别诡异的 bug,这里 mark 一下。
bug 如下: 从某个页面分享到 qq 好友之后,再返回此页面,Actionbar部分莫名其妙颜色发生了变化。具体情况如下图
对于这个 bug 最开始考虑是不是分享回来后不小心改了 actionBar 的颜色;于是进行排查,但是结果是悲伤的,没有发现任何关于更改 ActionBar 颜色的代码。
于是向测试小姐姐询问了,具体产生的机型和系统版本。最后发现只有在 android 8.0的系统上出现这个问题。于是拿来8.0系统进行 debug 调试,依然没有发现任何问题。
后来团队小伙伴,查阅了一系列的资料后发现这个应该是由于 Android 8.0输入和导航的变更引起的。
官方文档描述如下:
尤其要指出的是,我们对元素焦点行为做出以下变更:
要实现ViewPager 添加一个头部,并和 viewPager 中子 view 的联动效果;可能大家第一印象都是又得处理touch 事件了。不错,处理事件是万能的,也是所有这一些列问题的基础。不过处理事件对于很多人来说确实还是一个复杂而有难度的过程。其实 google 也意识到了这一点,之后出的一些框架和技术都是尽量做了封装,尽量避免让开发者去做这些复杂的事件。
今天我们就使用嵌套滚动实现 viewPager 添加一个 header 并实现和其子 View 的联动效果。
先看一下效果图:
首先不了解嵌套滚动的基本用法的同学,先看一下嵌套滚动的基本用法和几个回掉方法的含义,然后再向下看。
先说一下实现思路,然后再看代码吧
嵌套滚动是 Android OS 5.0之后,google 为我们提供的新特性。这种机制打破了我们对之前 Android 传统的事件处理的认知。从一定意义上可以理解为嵌套滚动是逆向的事件传递机制。
如上图所示,其原理就是这样。那么下边我们从代码的层面看一下实现。
代码中主要涉及到了四个类:
NestedScrollingChild、NestedScrollingChildHelper、
NestedScrollingParent、NestedScrollingParentHelper
先看NestedScrollingChild 接口中定义的方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53public interface NestedScrollingChild {
/**
* 设置是否启用嵌套滚动
*/
public void setNestedScrollingEnabled(boolean enabled);
/**
* 判断是否启用嵌套滚动
*/
public boolean isNestedScrollingEnabled();
/**
* 开始嵌套滚动
* @param axes 标识方向,有 x, y 方形和默认值0
*/
public boolean startNestedScroll(int axes);
/**
* 嵌套滚动结束
*/
public void stopNestedScroll();
/**
* 判断父 view 是否支持嵌套滚动
*/
public boolean hasNestedScrollingParent();
/**
* 分发嵌套滚动,一般再 onTouch/onInterceptTouchEvent/dispatchTouchEvent 中调用
* @param dxConsumed x轴滚动的距离
* @param dyConsumed y 轴滚动的距离
* @param dxUnconsumed x 轴上未消费的距离
* @param dyUnconsumed y 轴上未消费的距离
* @param offsetInWindow 子 View 的窗体偏移
*/
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);
/**
* 滚动之前调用,进行分发预滚动事件
*/
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
/**
* 滑动时调用 ,分发滑动事件
*/
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
/**
* 滚动之前调用,分发预滚动事件
*/
public boolean dispatchNestedPreFling(float velocityX, float velocityY);
}
线上版本报了一个很奇怪的异常Java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll
看到这个确实是源码里抱出来的,查了很久后来发现: 在 bindView 的时候不能直接调用 notify 更新 view,如果在 bindView 的时候直接调用notify 的操作,就会出现上边异常。
解决方案:
将 notify 的操作放置到Runnable 队列里去执行,以保证本次 notify 执行完成后再执行下一次 notify 的操作。