Android 虚拟按键隐藏或显示之后共享元素动画异常解决方案

背景

本篇算是共享元素的第三篇文章。主要还是因为第一篇才会衍生出来了第二篇和第三篇文章,后两篇均属于bug的分析和解决。
1.Android 仿微信朋友圈图片拖拽返回
2.Android 共享元素动画分析及背景空白的解决方案
3.Android 虚拟按键隐藏或显示之后共享元素动画异常解决方案

在部分可以隐藏或者显示虚拟按键的手机上,只要显示或者隐藏虚拟按键,再执行共享元素,就会异常。如图:


SVID_20190605_144033_123.gif

按照惯例,发现了问题,看看别人是怎么解决的。既然是仿微信朋友圈的功能,因此看了下朋友圈的效果。如图


SVID_20190605_143621_123.gif

然后之前郭大佬开源了giffun,也看了下对应的效果。如图


SVID_20190605_143908_123.gif

备注:测试手机是8.0华为。而这个问题,只发生在页面A进入页面B的时候,页面B返回页面A则正常。同时,在隐藏或者显示虚拟键盘之后,将手机休眠再唤醒,再进入页面B,则不会发生这个问题。

对比了微信和giffun的效果,基本都会黑屏一下。但是微信的可以成功进入下一页面。而giffun,在测试的时候发现,偶尔也可以进入下一页面。为什么不同app之间会有差异,很正常,大家的用的库不一样,环境不一样等等。但是,都黑屏了,这就是共同点。

在这里,我可以口出狂言,只要不做任何处理,大家都有这个问题,而且这是属于官方的bug。很开心,大家都解决不了,那我也肯定解决不了了。所以 ---- 完!!!感谢大家点进来阅读。这就是篇骗访问量的文章。

分析

本着专研的精神,决定看看到底是什么问题导致的。因为上一篇已经对共享元素进行了源码分析,所以此处跳过大部分的源码分析。

还是和上次一样,看下页面A进入页面B共享元素的回调监听吧。

A: 
onMapSharedElements
onCaptureSharedElementSnapshot
B: 
onMapSharedElements

再看下应该出现的回调监听

A:
onMapSharedElements
onCaptureSharedElementSnapshot
onSharedElementsArrived
B:
onMapSharedElements
onSharedElementsArrived
onRejectSharedElements
onCreateSnapshotView
onSharedElementStart
onSharedElementEnd

从出现的结果来看,可以证明我在上一篇最后提出的问题。即页面A和B的onSharedElementsArrived以及之后的监听,都是在页面A收到页面B发送的MSG_SET_REMOTE_RECEIVER消息之后,再发送MSG_TAKE_SHARED_ELEMENTS消息给页面B之后产生的。

而现在后面的监听都没有回调,这就说明,页面B给页面A消息之后,就出现了问题,导致页面A没有给页面B发送消息。所以,根据上一篇分析,直接将代码定位到A页面收到B页面消息的地方

//ExitTransitionCoordinator.class
case MSG_SET_REMOTE_RECEIVER:
        stopCancel();
        mResultReceiver = resultData.getParcelable(KEY_REMOTE_RECEIVER);
        if (mIsCanceled) {
            mResultReceiver.send(MSG_CANCEL, null);
            mResultReceiver = null;
        } else {
            notifyComplete();
        }
        break;

调试发现mIsCanceled为true,所以notifyComplete出现了问题。继续跟踪。

//ExitTransitionCoordinator.class
protected void notifyComplete() {
    if (isReadyToNotify()) {
        if (!mSharedElementNotified) {
            mSharedElementNotified = true;
            delayCancel();
            if (mListener == null) {
                mResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, mSharedElementBundle);
                notifyExitComplete();
            } else {
                final ResultReceiver resultReceiver = mResultReceiver;
                final Bundle sharedElementBundle = mSharedElementBundle;
                mListener.onSharedElementsArrived(mSharedElementNames, mSharedElements,
                        new OnSharedElementsReadyListener() {
                            @Override
                            public void onSharedElementsReady() {
                                resultReceiver.send(MSG_TAKE_SHARED_ELEMENTS,
                                        sharedElementBundle);
                                notifyExitComplete();
                            }
                        });
            }
        } else {
            notifyExitComplete();
        }
    }
}

所以关键代码就是isReadyToNotify()的判断了。

//ExitTransitionCoordinator.class
protected boolean isReadyToNotify() {
    return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
}

调试发现mSharedElementBundle为null。所以根源已经找到,肯定有个地方将这个值重新设置为null了,导致共享元素的动画出现了异常。

//ExitTransitionCoordinator.class
@Override
protected void sharedElementTransitionComplete() {
    mSharedElementBundle = mExitSharedElementBundle == null
            ? captureSharedElementState() : captureExitSharedElementsState();
    super.sharedElementTransitionComplete();
}

@Override
protected void clearState() {
    mHandler = null;
    mSharedElementBundle = null;
    ****省略部分代码****
    mExitSharedElementBundle = null;
    super.clearState();
}

总共就两处赋值,第一处是正常的值,第二处为null,所以怀疑对象就是clearState这个方法了。经过长时间的调试,发现异常情况的时候,执行了activity.onStop()-->mActivityTransitionState.onStop()-->mActivityTransitionState.restoreExitedViews()-->mCalledExitCoordinator.resetViews()-->clearState()一系列操作。

发现了onStop(),没错是activity生命周期onStop(),怎么回事?页面跳转,执行一个onStop生命周期,不是再正常不过了么?怎么就出现问题了?一个虚拟按键的隐藏或者显示,一个在正常不过的onStop生命周期,怎么就导致了共享元素动画异常?这三者之间到底有何勾当!砸电脑ing

静静:你想我了吧[坏笑]

凡是出现问题,既要研究异常情况,也要对比正常情况。日志对比:

//正常情况
A:
onMapSharedElements
onCaptureSharedElementSnapshot
onSharedElementsArrived
B:
onMapSharedElements
onSharedElementsArrived
onRejectSharedElements
onCreateSnapshotView
onSharedElementStart
onSharedElementEnd
A:
onStop(因为项目中activity的背景是透明的,所以肯定不会出现onStop的情况,为了测试,改为了非透明)
//异常情况
A:
onMapSharedElements
onCaptureSharedElementSnapshot
onStop
B:
onMapSharedElements

也就是说,两者的差别是,onStop生命周期的调用时间不一致。

静静:你又想我了吧[坏笑]

说实话到了这里,我只能开启脑洞,往各个有可能的方向去尝试。结果发现,共享元素异常情况的时候,不仅仅onStop提前了,而且页面A的整个生命周期都重新调了一遍。那就好办了,等于就是页面A重新创建了。马上联想到手机旋转会导致页面重新创建,赶紧去manifest中找找android:configChanges有没有合适的属性。

屏幕快照 2019-06-05 上午11.53.34.png

所以,最终的解决方案就是,给页面A设置如下属性即可。而且只要虚拟按键显示或者隐藏之后再进行页面跳转,页面A都会重建,和共享元素没有太大关联,除非以后虚拟按键彻底退出历史舞台。所以为了防止重建,建议每个activity都添加此属性。

android:configChanges="screenLayout"

效果图:


SVID_20190605_144212_123.gif

总结

虚拟按键的隐藏或者显示,导致在页面跳转的时候,该页面进行了重建的过程,导致共享元素的关键变量mSharedElementBundle被重置为null,从而导致页面A没法发送MSG_TAKE_SHARED_ELEMENTS给页面B,从而也就导致了共享元素动画被中断的问题。

现在再回顾微信和giffun,黑屏的时候,页面A都重建了(微信的,仔细看左上角的loading。giffun,仔细看标题和右下角FAB按钮),所以和分析的相吻合。

在开发中,大家会遇到各种各样的问题,合理利用网络搜索可以解决大部分的问题。当搜不到的时候,不妨试试自己去找解决办法,从源码角度分析,一方面可以提高自己,另一方面说不定还真把问题解决了呢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 选择题 1.activity对一些资源以及状态的操作保存,最好是保存在生命周期的哪个函数中进行( a ) a.o...
    海晨忆阅读 1,064评论 0 1
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,201评论 0 17
  • 1.什么是Activity?问的不太多,说点有深度的 四大组件之一,一般的,一个用户交互界面对应一个activit...
    JoonyLee阅读 5,665评论 2 51
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,629评论 1 92
  • 1. 这次来杭州,参观了于谦祠。 我们大多数人,对历史上于谦的熟悉,大都来自于那首著名的诗: 千锤万凿出深山, 烈...
    昆仑濯羽阅读 703评论 7 5