共享元素動畫在Android10上異常的解決方案(全文唯一僅此一份)

問題描述

最近在項目中遇到一個關于共享元素動畫失效的問題椭坚,在Activity跳轉時使用ActivityOptionsCompat.makeSceneTransitionAnimation做轉場動畫,
對于Activity共享元素動畫的使用不懂的童鞋自行谷歌搏色,網(wǎng)上資料太多善茎,這里就不做描述了。

一般情況下使用ActivityOptionsCompat.makeSceneTransitionAnimation做轉場動畫時频轿,當Activity返回時也會伴隨返回的動畫垂涯。但是就是這樣的一個簡單的API,
居然在Android 10上測試出現(xiàn)了異常:

如果使用了ActivityOptionsCompat.makeSceneTransitionAnimation跳轉的Activity沒有再次跳轉到別的Activity的話略吨,返回時系統(tǒng)是帶上響應的共享元素動畫的集币,
但是如果跳轉的Activity再次跳轉了其他的Activity,然后再返回的話共享元素的返回動畫就失效了(包括按了Home鍵退回桌面翠忠,然后再次進入也會失效)鞠苟。

筆者的測試手機是華為nova 5i,系統(tǒng)是Android 10

問題追蹤

1秽之、 大神方案

當筆者看到這個bug的時候当娱,首先想到的可能是我打開的方式不對,于是谷歌百度了一波考榨,結果確實無功而返跨细。

于是筆者第一想到的就是是不是自己的使用方式不對,于是看看大神是怎么使用的河质,然后參考了一下Android 大神郭霖開源的一個開源項目giffun冀惭,
發(fā)現(xiàn)giffun也存在同樣的異常...

為此我專門給giffun提了一個issue:

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

絕望了震叙。。散休。媒楼。。戚丸。划址。

2、 競品對比

既然查找不到相關的資料限府,那么就看看競品是否也有這樣的問題的夺颤,如果競品也有這樣的問題話就可以拿著競品去忽悠產(chǎn)品了,心里美滋滋胁勺。

于是筆者對比了一下競品的產(chǎn)品是否也存在這么的一個bug世澜,發(fā)現(xiàn)競品沒有一樣的bug,它們的共享元素動畫一切正常署穗。
后面通過研究了它們的APK才發(fā)現(xiàn)它們的共享元素動畫是使用Fragment做的宜狐,所以盡量使用Fragment的一個好處又體現(xiàn)出來了。

但是筆者今天要說的將Activity的轉場動畫改為Fragment這樣就完事了蛇捌,今天將帶大家一步一步分析共享元素動畫是如何失效的,如何修復這樣的一個bug咱台。

3络拌、 源碼先行

既然網(wǎng)上沒有這樣的資料,那就自己動手回溺,豐衣足食了春贸。首先我們初步看看Activity的源碼,發(fā)現(xiàn)如果是返回帶執(zhí)行共享元素動畫的話執(zhí)行的方法是finishAfterTransition遗遵。
finishAfterTransition里面調用了ActivityTransitionStatestartExitBackTransition方法萍恕。

然而看源碼并沒有看出什么破綻,畢竟大多數(shù)手機是正常的车要,只有在Android 10上才出現(xiàn)了異常允粤。既然這條路走不通,那我們就換一個途徑吧翼岁。

4类垫、 Debug大法

我們使用Android Studio的斷點調試功能,在ActivityTransitionStatestartExitBackTransition方法里面打斷點調試琅坡,
對比發(fā)現(xiàn)最終返回時沒有執(zhí)行共享元素動畫的原因是startExitBackTransition方法內的pendingExitNames變量為空就直接返回了false悉患,也就是不執(zhí)行共享元素動畫。

5榆俺、 萬變不離其宗售躁,再次回到源碼

那么為什么pendingExitNames變量會變成了空呢坞淮?我們再次回到源碼分析,pendingExitNames變量是通過
ActivityTransitionStatestartExitBackTransitiongetPendingExitNames方法獲取的陪捷,方法如下:

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

很簡單的代碼回窘,如果mEnterTransitionCoordinator是空的話,那么getPendingExitNames方法必定會返回空揩局。

那么我們繼續(xù)追蹤一下mEnterTransitionCoordinator變量是如何賦值的毫玖,跟蹤發(fā)現(xiàn)是在ActivityTransitionStateenterReady(Activity activity)方法
中對mEnterTransitionCoordinator做了賦值操作,然后在ActivityTransitionStateonStop方法中被置空了凌盯,而ActivityTransitionStateonStop方法
又被ActivityonStop方法調用了付枫,至此大概就能解析的解析得通為什么經(jīng)過跳轉后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方法影響了,那么為什么有些系統(tǒng)可以县忌,但是在Android 10系統(tǒng)上就不行了呢掂榔?這個問題留給大家去解答,因為最近項目非常忙症杏,
筆者也沒時間去深究這個問題了装获。

如何解決

既然找到了問題那么就好解決了,我們在Activity的onResume方法中調用一下ActivityTransitionStateenterReady方法厉颤,再次給mEnterTransitionCoordinator賦值不久完事了嗎穴豫?

但是ActivityTransitionState類是系統(tǒng)的私有類,開發(fā)者是不能直接調用的逼友,這時候我們就想到了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目前還不可知怨喘,歡迎大家深究討論津畸。。必怜。

關注我肉拓,一起學習,不止于技術J崆臁E尽卑惜!


image
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市驻售,隨后出現(xiàn)的幾起案子露久,更是在濱河造成了極大的恐慌,老刑警劉巖欺栗,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毫痕,死亡現(xiàn)場離奇詭異,居然都是意外死亡迟几,警方通過查閱死者的電腦和手機消请,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來类腮,“玉大人臊泰,你說我怎么就攤上這事⊙潦啵” “怎么了缸逃?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長厂抽。 經(jīng)常有香客問我需频,道長,這世上最難降的妖魔是什么筷凤? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任贺辰,我火速辦了婚禮,結果婚禮上嵌施,老公的妹妹穿的比我還像新娘。我一直安慰自己莽鸭,他們只是感情好吗伤,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著硫眨,像睡著了一般足淆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上礁阁,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天巧号,我揣著相機與錄音,去河邊找鬼姥闭。 笑死丹鸿,一個胖子當著我的面吹牛,可吹牛的內容都是我干的棚品。 我是一名探鬼主播靠欢,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼廊敌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了门怪?” 一聲冷哼從身側響起骡澈,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掷空,沒想到半個月后肋殴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡坦弟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年护锤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片减拭。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡蔽豺,死狀恐怖,靈堂內的尸體忽然破棺而出拧粪,到底是詐尸還是另有隱情修陡,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布可霎,位于F島的核電站魄鸦,受9級特大地震影響,放射性物質發(fā)生泄漏癣朗。R本人自食惡果不足惜拾因,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望旷余。 院中可真熱鬧绢记,春花似錦、人聲如沸正卧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炉旷。三九已至签孔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窘行,已是汗流浹背饥追。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留罐盔,地道東北人但绕。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像惶看,于是被迫代替她去往敵國和親壁熄。 傳聞我的和親對象是個殘疾皇子帚豪,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355