共享元素动画在Android10上异常的解决方案(全文唯一仅此一份)

问题描述

最近在项目中遇到一个关于共享元素动画失效的问题,在Activity跳转时使用ActivityOptionsCompat.makeSceneTransitionAnimation做转场动画,
对于Activity共享元素动画的使用不懂的童鞋自行谷歌,网上资料太多,这里就不做描述了。

一般情况下使用ActivityOptionsCompat.makeSceneTransitionAnimation做转场动画时,当Activity返回时也会伴随返回的动画。但是就是这样的一个简单的API,
居然在Android 10上测试出现了异常:

如果使用了ActivityOptionsCompat.makeSceneTransitionAnimation跳转的Activity没有再次跳转到别的Activity的话,返回时系统是带上响应的共享元素动画的,
但是如果跳转的Activity再次跳转了其他的Activity,然后再返回的话共享元素的返回动画就失效了(包括按了Home键退回桌面,然后再次进入也会失效)。

笔者的测试手机是华为nova 5i,系统是Android 10。

问题追踪

1、 大神方案

当笔者看到这个bug的时候,首先想到的可能是我打开的方式不对,于是谷歌百度了一波,结果确实无功而返。

于是笔者第一想到的就是是不是自己的使用方式不对,于是看看大神是怎么使用的,然后参考了一下Android 大神郭霖开源的一个开源项目giffun,
发现giffun也存在同样的异常...

为此我专门给giffun提了一个issue:

https://github.com/guolindev/giffun/issues/67

绝望了。。。。。。。

2、 竞品对比

既然查找不到相关的资料,那么就看看竞品是否也有这样的问题的,如果竞品也有这样的问题话就可以拿着竞品去忽悠产品了,心里美滋滋。

于是笔者对比了一下竞品的产品是否也存在这么的一个bug,发现竞品没有一样的bug,它们的共享元素动画一切正常。
后面通过研究了它们的APK才发现它们的共享元素动画是使用Fragment做的,所以尽量使用Fragment的一个好处又体现出来了。

但是笔者今天要说的将Activity的转场动画改为Fragment这样就完事了,今天将带大家一步一步分析共享元素动画是如何失效的,如何修复这样的一个bug。

3、 源码先行

既然网上没有这样的资料,那就自己动手,丰衣足食了。首先我们初步看看Activity的源码,发现如果是返回带执行共享元素动画的话执行的方法是finishAfterTransition
finishAfterTransition里面调用了ActivityTransitionStatestartExitBackTransition方法。

然而看源码并没有看出什么破绽,毕竟大多数手机是正常的,只有在Android 10上才出现了异常。既然这条路走不通,那我们就换一个途径吧。

4、 Debug大法

我们使用Android Studio的断点调试功能,在ActivityTransitionStatestartExitBackTransition方法里面打断点调试,
对比发现最终返回时没有执行共享元素动画的原因是startExitBackTransition方法内的pendingExitNames变量为空就直接返回了false,也就是不执行共享元素动画。

5、 万变不离其宗,再次回到源码

那么为什么pendingExitNames变量会变成了空呢?我们再次回到源码分析,pendingExitNames变量是通过
ActivityTransitionStatestartExitBackTransitiongetPendingExitNames方法获取的,方法如下:

    if (mPendingExitNames == null && mEnterTransitionCoordinator != null) {
            mPendingExitNames = mEnterTransitionCoordinator.getPendingExitSharedElementNames();
        }
        return mPendingExitNames;
    }

很简单的代码,如果mEnterTransitionCoordinator是空的话,那么getPendingExitNames方法必定会返回空。

那么我们继续追踪一下mEnterTransitionCoordinator变量是如何赋值的,跟踪发现是在ActivityTransitionStateenterReady(Activity activity)方法
中对mEnterTransitionCoordinator做了赋值操作,然后在ActivityTransitionStateonStop方法中被置空了,而ActivityTransitionStateonStop方法
又被ActivityonStop方法调用了,至此大概就能解析的解析得通为什么经过跳转后Activity的返回共享元素失效了,原来是被Activity的onStop生命周期给影响了。

ActivityTransitionState的enterReady代码:

public void enterReady(Activity activity) {
        if (mEnterActivityOptions == null || mIsEnterTriggered) {
            return;
        }
        mIsEnterTriggered = true;
        mHasExited = false;
        ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
        ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
        if (mEnterActivityOptions.isReturning()) {
            restoreExitedViews();
            activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
        }

        // 在这里对mEnterTransitionCoordinator赋值了
        // 在这里对mEnterTransitionCoordinator赋值了
        // 在这里对mEnterTransitionCoordinator赋值了

        mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
                resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(),
                mEnterActivityOptions.isCrossTask());
        if (mEnterActivityOptions.isCrossTask()) {
            mExitingFrom = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
            mExitingTo = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
        }

        if (!mIsEnterPostponed) {
            startEnter();
        }
    }

ActivityTransitionState的onStop代码:


  public void onStop() {
        restoreExitedViews();
        if (mEnterTransitionCoordinator != null) {
            mEnterTransitionCoordinator.stop();
            mEnterTransitionCoordinator = null;
        }
        if (mReturnExitCoordinator != null) {
            mReturnExitCoordinator.stop();
            mReturnExitCoordinator = null;
        }
    }

在这里给同学们留一个问题,既然是被Activity的onStop方法影响了,那么为什么有些系统可以,但是在Android 10系统上就不行了呢?这个问题留给大家去解答,因为最近项目非常忙,
笔者也没时间去深究这个问题了。

如何解决

既然找到了问题那么就好解决了,我们在Activity的onResume方法中调用一下ActivityTransitionStateenterReady方法,再次给mEnterTransitionCoordinator赋值不久完事了吗?

但是ActivityTransitionState类是系统的私有类,开发者是不能直接调用的,这时候我们就想到了Java的反射大法,是的笔者就是通过反射调用的。

在编写反射调用ActivityTransitionStateenterReady方法时候AS提示targetSdkVersion是28以上的会得到反射异常。这是因为Android 9以上对反射做了限制,这时候我们只需要将targetSdkVersion设置成28以下的即可。

主要代码:


 /**
     * Android10 Activity的onStop方法可能会导致共享元素动画失效,通过反射注入恢复共享元素动画
     * @param activity
     */
    public static void updateResume(Activity activity){
        try {
            Field activityTransitionStateField = Activity.class.getDeclaredField("mActivityTransitionState");
            activityTransitionStateField.setAccessible(true);
            Object mActivityTransitionState = activityTransitionStateField.get(activity);
            Class clazz = Class.forName("android.app.ActivityTransitionState");
            Method enterReady = clazz.getDeclaredMethod("enterReady",Activity.class);
            enterReady.setAccessible(true);
            enterReady.invoke(mActivityTransitionState,activity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

然后在Activity的onResume方法中调用一下反射方法即可。实测返回是共享元素动画正常,但是会导致什么未知bug目前还不可知,欢迎大家深究讨论。。。

关注我,一起学习,不止于技术!?。?/p>

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