1.Fresco引發(fā)的血案
前面寫過(guò)一篇關(guān)于Fresco分析的文章,沒(méi)想到?jīng)]過(guò)幾天就發(fā)生了一場(chǎng)血案。事情是這樣的,昨天另一個(gè)小哥跟我說(shuō)他用Fresco加載gif不能顯示了,他也不知道為什么,也沒(méi)改什么東西,讓我?guī)兔匆幌?。其?shí)我們項(xiàng)目用Fresco已經(jīng)挺久的了,然后之前那個(gè)地方也沒(méi)有問(wèn)題,但是不知道為什么就是不顯示了。于是今天就開始了折騰。
2.解決過(guò)程
1.首先我懷疑是不是改了布局,導(dǎo)致控件沒(méi)有顯示出來(lái)。帶著這樣的懷疑,我設(shè)置了控件的背景為鮮明的紅色,發(fā)現(xiàn)是顯示出來(lái)的,并沒(méi)有什么問(wèn)題。當(dāng)然這個(gè)想法可能太簡(jiǎn)單了點(diǎn),但是這樣的原因也是很有可能的,所以當(dāng)一個(gè)控件不能顯示的時(shí)候,你一定要先確定改控件是不是Visible,如果是Visible那么再去找其他原因。
2.懷疑加載圖片的URI有問(wèn)題,通過(guò)跟蹤AbstractDraweeController中的
submitRequest()方向作為入口,不斷跟蹤到如下圖的地方,發(fā)現(xiàn)URI正確,且正確調(diào)用的相應(yīng)的方法。
既然url沒(méi)有錯(cuò),那我看看submit中的回調(diào)吧,如下圖,媽蛋居然調(diào)的是失敗,這我就懵逼了,這tm到底哪里出錯(cuò)了呢,就錯(cuò)了也不是咱自己的錯(cuò)吧,這鍋不得facebook來(lái)背嗎?但以我多年的職業(yè)經(jīng)驗(yàn)來(lái)看這個(gè)事情不是那么簡(jiǎn)單,于是我繼續(xù)斷點(diǎn)反復(fù)查看。我們通過(guò)對(duì)Fresco的分析會(huì)發(fā)現(xiàn),最終在處理請(qǐng)求時(shí)都會(huì)調(diào)用Producer類中的的produceResults來(lái)消費(fèi)請(qǐng)求,但是對(duì)于這個(gè)Producer我也不是很懂,但是就憑他要調(diào)用的這個(gè)方法,我斷點(diǎn)逐行分析,發(fā)現(xiàn)并沒(méi)有什么問(wèn)題。最終個(gè)條路也只能到這里結(jié)束。
3.實(shí)在沒(méi)什么頭緒了,于是去查看Fresco的文檔http://www.fresco-cn.org/docs/getting-started.html,文檔中關(guān)于加載gif的描述如下:
Uri uri;
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(uri)
.setAutoPlayAnimations(true)
. // 其他設(shè)置(如果有的話)
.build();
mSimpleDraweeView.setController(controller);
這代碼這么簡(jiǎn)單沒(méi)什么問(wèn)題啊。繼續(xù)看文檔,發(fā)現(xiàn)在問(wèn)題處理中說(shuō)可以啟動(dòng)日志,既然沒(méi)有辦法那就啟動(dòng)日志來(lái)看看唄。Fresco日志默認(rèn)是關(guān)閉的,啟動(dòng)日志方法如下,在Fresco初始化時(shí)做如下配置:
Set<RequestListener> requestListeners = new HashSet<>();
requestListeners.add(new RequestLoggingListener());
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context)
// other setters
.setRequestListeners(requestListeners)
.build();
Fresco.initialize(context, config);
FLog.setMinimumLoggingLevel(FLog.VERBOSE);
查看日志方法如下:
adb logcat -v threadtime | grep -iE 'LoggingListener|AbstractDraweeController|BufferedDiskCache'
ok打開日志后,再次加載,看到如下日志:
什么意思呢?大概就是在DecodeProducer中有一個(gè)空指針異常,調(diào)用了decodeGif在一個(gè)空引用上。于是查看DecodeProducer類找到doDecode方法,發(fā)現(xiàn)其中調(diào)用了mImageDecoder.decodeImage,繼續(xù)查看在decodeImage中調(diào)用到了我們要找的decodeGif方法,內(nèi)容如下:
根據(jù)這個(gè)方法再打斷點(diǎn)說(shuō)的就是這個(gè)mAnimatedImageFactory是空指針,那么這個(gè)AnimatedImageFactory是個(gè)什么鬼,從源碼我可以知道這個(gè)類就是用來(lái)解析動(dòng)圖的,目前可以解析gif和webP。 繼續(xù)查看調(diào)用可以看到這個(gè)factory是在ImagePipeline中創(chuàng)建的,如下:
就是在這里通過(guò)AnimatedFactoryProvider創(chuàng)建了一個(gè)AnimatedFactory,里面代碼如下:
通過(guò)反射來(lái)創(chuàng)建的,既然出現(xiàn)了空指針那就是這里創(chuàng)建的問(wèn)題咯,那我們來(lái)看看這個(gè)兩個(gè)類,發(fā)現(xiàn)根本找不到,所以問(wèn)題就是這里咯。同時(shí)發(fā)現(xiàn)在項(xiàng)目中存在兩個(gè)Fresco的包0.8.0和0.11.0,于是定位到可能是依賴沖突的原因。查看了0.8.0里面創(chuàng)建AnimatedFactory的代碼發(fā)現(xiàn)與0.11.0中的代碼還是有點(diǎn)區(qū)別的,確認(rèn)是依賴包沖突的問(wèn)題了。
通過(guò)在Android studio Terminal輸入如下命令gradlew -q app:dependencies查看當(dāng)前使用的依賴版本,發(fā)現(xiàn)雖然配置的是使用0.8.0但是實(shí)際使用的卻是0.11.0。所以解決辦法1就是恢復(fù)到0.8.0版本,至于11版本的為什么沒(méi)有那兩個(gè)類我們稍后再來(lái)看。
+--- com.facebook.fresco:fresco:0.8.0 -> 0.11.0
| +--- com.facebook.fresco:drawee:0.11.0
| | +--- com.android.support:support-v4:23.2.1 (*)
| | \--- com.facebook.fresco:fbcore:0.11.0
| +--- com.facebook.fresco:fbcore:0.11.0
| \--- com.facebook.fresco:imagepipeline:0.11.0
| +--- com.android.support:support-v4:23.2.1 (*)
| +--- com.facebook.fresco:fbcore:0.11.0
| +--- com.parse.bolts:bolts-tasks:1.4.0
| +--- com.nineoldandroids:library:2.4.0
| \--- com.facebook.fresco:imagepipeline-base:0.11.0
| +--- com.android.support:support-v4:23.2.1 (*)
| +--- com.facebook.fresco:fbcore:0.11.0
| +--- com.parse.bolts:bolts-tasks:1.4.0
| \--- com.nineoldandroids:library:2.4.0
+--- com.facebook.react:react-native:0.29.2
| +--- com.google.code.findbugs:jsr305:3.0.0
| +--- org.webkit:android-jsc:r174650
| +--- com.facebook.fresco:imagepipeline-okhttp3:0.11.0
| | +--- com.facebook.fresco:fbcore:0.11.0
| | +--- com.squareup.okhttp3:okhttp:3.0.1 -> 3.2.0
| | | \--- com.squareup.okio:okio:1.6.0 -> 1.8.0
| | \--- com.facebook.fresco:imagepipeline:0.11.0 (*)
| +--- com.squareup.okio:okio:1.8.0
| +--- com.fasterxml.jackson.core:jackson-core:2.2.3
| +--- com.squareup.okhttp3:okhttp:3.2.0 (*)
| +--- com.facebook.fresco:fresco:0.11.0 (*)
| +--- com.squareup.okhttp3:okhttp-ws:3.2.0
| | \--- com.squareup.okhttp3:okhttp:3.2.0 (*)
| +--- com.android.support:recyclerview-v7:23.0.1 (*)
| +--- com.squareup.okhttp3:okhttp-urlconnection:3.2.0
| | \--- com.squareup.okhttp3:okhttp:3.2.0 (*)
| \--- com.android.support:appcompat-v7:23.0.1 (*)
從上面的依賴關(guān)系可以看出我們自己依賴了fresco的0.8,同時(shí)依賴了react,但是react中依賴了fresco 0.11導(dǎo)致我們實(shí)際依賴的是搞版本的fresco,所以如果要使用低版本的,按理說(shuō)只需要把react中的fresco排除,然后我們?cè)趯?dǎo)入自己需要的版本就行了,但是我按照這樣的思路在build.gradle進(jìn)行了如下配置,結(jié)果還是使用11版本的,最終沒(méi)有找到原因,你們也可以試一下,如果有發(fā)現(xiàn)什么問(wèn)題的一定要告訴我。
compile 'com.facebook.fresco:fresco:0.8.0'
compile ('com.facebook.react:react-native:0.29.2') {
exclude module:'com.facebook.fresco:fresco'
}
接下來(lái)我們就來(lái)看看為什么使用11版本的就少了那兩個(gè)類呢?查閱各種資料,最終在一篇博客中發(fā)現(xiàn)了如下的內(nèi)容:
一定要導(dǎo)入下邊這個(gè) compile 'com.facebook.fresco:animated-gif:0.12.0',否則gif圖壓根不動(dòng)
于是瞬間懂了,原來(lái)高版本的fresco把gif相關(guān)的內(nèi)容獨(dú)立到了另一個(gè)依賴?yán)锩妫@樣如果不需要用到gif的項(xiàng)目就不需要導(dǎo)入,由于我們升級(jí)到了高版本,所以默認(rèn)是沒(méi)有g(shù)if相關(guān)的類的,所以就缺少了那兩個(gè)類,導(dǎo)致gif不能顯示,于是導(dǎo)入后,完美解決問(wèn)題。
3.總結(jié)
解決問(wèn)題的方法有兩種:
1.退回到低版本的fresco
2.使用高版本的,需要另外導(dǎo)入com.facebook.fresco:animated-gif這個(gè)依賴