React-Native
界面在啓動時會調用Fresco
的初始化,即如果Fresco
已經初始化過了,也會再重新執行初始化過程一次。
具體調用棧如下:
這會導致原先的Fresco
初始化的配置全部失效,比如緩存大小被改變圖片可能過早被清除,失去自定義的NetworkFetcher
導致圖片下載失敗(如果NetworkFetcher
涉及添加自定義頭信息的話)。
該問題在"react-native": "^0.40.0"
中才得以解決。解決辦法爲MainReactPackage
的構造函數中增加MainPackageConfig
參數,該參數可指定Fresco
的ImagePipelineConfig
。
應該是Fresco
的坑(因爲只要Fresco.initialize()
判斷避免重複初始化即),可卻是React-Native
來填(這也是機緣呀,因爲該bug是反饋在React-Native
的issue
上)。
填坑代碼:
MyApplication.java
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 | private ImagePipelineConfig imagePipelineConfig; public void onCreate() { // .... do something .... initFresco(); .... } public void initFresco() { DiskCacheConfig diskCacheConfig = DiskCacheConfig.newBuilder(this) .setBaseDirectoryPath(new File(MultiCard.getInstance(this).getRootDir())) .setBaseDirectoryName(MultiCard.FRESCO_IMAGE_CACHE) .setMaxCacheSize(50 * ByteConstants.MB) .setMaxCacheSizeOnLowDiskSpace(10 * ByteConstants.MB) .setMaxCacheSizeOnVeryLowDiskSpace(2 * ByteConstants.MB) .build(); imagePipelineConfig = ImagePipelineConfig.newBuilder(this) .setNetworkFetcher(new MyImageDownloaderFetcher()) .setMainDiskCacheConfig(diskCacheConfig).build(); Fresco.initialize(this, imagePipelineConfig); } public ImagePipelineConfig getImagePipelineConfig() { return imagePipelineConfig; } |
ReactNativeBaseActivity.java
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 | @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mReactRootView = new ReactRootView(this); // ↓↓↓ start custom config to fresco MainPackageConfig.Builder configBuilder = new MainPackageConfig.Builder(); configBuilder.setFrescoConfig(myApplication.getImagePipelineConfig()); mReactInstanceManager = ReactInstanceManager.builder() .setApplication(ElnApplication.getInstance()) .setBundleAssetName("index.android.bundle") .setJSMainModuleName("index.android") .addPackage(new MainReactPackage(configBuilder.build())) .addPackage(new ModulePackage()) .setUseDeveloperSupport(BuildConfig.DEBUG) .setInitialLifecycleState(LifecycleState.RESUMED) .build(); // ↑↑↑ end custom config to fresco Bundle bundle = getExtra(); if(bundle == null){ bundle = new Bundle(); } mReactRootView.startReactApplication(mReactInstanceManager, "MyBoduleName", bundle); setContentView(mReactRootView); } |
填坑的前前後後
公司的App
主體仍是原生開發的,但有部分界面使用了React-Native
實現。
事情起因,測試同學提了個bug
,說圖片加載隨機失敗。
好吧,隨機失敗,嘗試一下看能不能找出必定失敗的步驟吧。
跟進步驟爲:
Charles
抓包,查看圖片請求詳情。要在手機中安裝SSL
證書解決圖片的https
鏈接不可查看的現象。- 抓包發現原本應該
response code
是200
,現在卻返回302
,Location
是403 Forbidden
的網頁。
由此,是被後臺的防盜鏈識別爲非法請求拒絕訪問了。查看圖片鏈接的請求頭信息,發現User-Agent
和Referer
都沒有設置,才導致被判定爲非法。
App
使用的的圖片框架是Fresco
,爲了自定義圖片的請求頭信息(User-Agent
和Referer
)在Fresco
初始化時設置了自定義的NetworkFetcher
爲MyImageDownloaderFetcher
。
發現在抓包爲403
時,MyImageDownloaderFetcher
類中相關的日誌被沒有被打印出來。
這時開始懷疑Fresco
配置失效。
這時開始懷疑Fresco
在某個未知時刻被修改了網絡配置。
由於ImagePipelineConfig
只在Fresco.initialize()
纔會被調用。
這時開始懷疑Fresco
被重新初始化了。
使用Android Studio
的Find Usages
功能,發現Fresco.initialize()
除了在MyApplication
中由自己主動調用外,還在React-Native
中的FrescoModule
被調用了一次。
則在Debug
的編譯環境下,對Fresco.initialize()
設置斷點,發現App
啓動時正常執行一次,打開React-Native
界面時又執行一次。好了,原因就是這裏了,後面的執行把老子的網絡配置給沖掉了。
原因找到了,接下來是怎麼解決。
第一個思路看有沒有現成的接口可以自定義Configuration
,沒有!
第二個思路是那就使用Java Reflection
再把被篡改的Configuration
改回來吧!(死腦筋)
然後,lixiaowei
同學說上github/react-native/issue
找找,嗯,發現有人反饋了這個bug
,得升級React-Native
然後就可以按第一個思路來解決了。解決辦法就如上了。(死開發,別悶着改代碼,多聊天呀…)
Reference Link :
Android: Enable apps to provide a custom configuration to Fresco