Fresco的封裝和使用說明以及獲取快取中的Bitmap物件

hahadelphi發表於2021-09-09

Fresco介紹

是facebook開源的圖片載入框架。

關於 Fresco Fresco 是一個強大的圖片載入元件。

Fresco 中設計有一個叫做 image pipeline 
的模組。它負責從網路,從本地檔案系統,本地資源載入圖片。為了最大限度節省空間和CPU時間,它含有3級快取設計(2級記憶體,1級檔案)。

Fresco 中設計有一個叫做 Drawees 模組,方便地顯示loading圖,當圖片不再顯示在螢幕上時,及時地釋放記憶體和空間佔用。

Fresco 支援 Android2.3(API level 9) 及其以上系統。

相比其他的圖片載入框架功能強大得多,如果還不瞭解同學可以點選上面的連結學習瞭解。

它的整個框架設計涉及到很多的設計模式,作為開發者如果不瞭解足夠多的設計模式對開源框架分析理解是在有點困難,所以我的部落格會經常更新一些有關設計模式的內容和大家一起學習。

封裝理由

Fresco功能強大。如果只是普通的使用很簡單。 
如果需要使用它真正強大的功能就很複雜,這是因為它的框架中包含了太多的功能類和設計模式,導致使用起來非常麻煩,相比其他的一行程式碼就可以解決圖片載入,Fresco就麻煩多了。所以我封裝了一個類方便我在專案中使用,現在開源出來給作為使用大家參考。

為什麼Fresco配置這麼複雜? 
因為對整個Fresco圖片載入框架 使用了MVC模式 
M層上有很多的圖片資源,比如佔點陣圖,loading圖,error圖 
C層決定呼叫M層上哪些圖 
V層對應檢視View層,最後繫結M/C層。所以在使用時需要配置M/C層需要很多程式碼。

使用說明

封裝之後呼叫圖片載入就相對簡單,簡單的使用是沒有問題的,只是在Adapter中使用存在控制元件重複配置的問題,會對記憶體有些影響。這個等我有時間再深究解決。 

在指定一個新的controller的時候,使用setOldController,這可節省不必要的記憶體分配。

使用示例程式碼

[程式碼]java程式碼:

1

2

3

new ImageLoadFresco.LoadImageFrescoBuilder(mContext,view,url)

                .setIsRadius(true)//圓角配置

                .build();

封裝程式碼

說明

針對這個配置屬性比較多的類,我採用,簡化使用時候的程式碼量。類裡面配置很多預設屬性。如果需要新增新的屬性也不會影響現有程式碼編譯。同時在使用的時候不涉及具體的Fresco程式碼,後期如果有需求替換圖片載入框架,也會方便。

程式碼

最新的程式碼點,這個封裝類我自己也在專案中使用,會根據需求新增新的功能。

[程式碼]java程式碼:

001

002

003

004

005

006

007

008

009

010

011

012

013

014

015

016

017

018

019

020

021

022

023

024

025

026

027

028

029

030

031

032

033

034

035

036

037

038

039

040

041

042

043

044

045

046

047

048

049

050

051

052

053

054

055

056

057

058

059

060

061

062

063

064

065

066

067

068

069

070

071

072

073

074

075

076

077

078

079

080

081

082

083

084

085

086

087

088

089

090

091

092

093

094

095

096

097

098

099

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

package licola.demo.com.huabandemo.HttpUtils;

 

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.PointF;

import android.graphics.drawable.Drawable;

import android.net.Uri;

import android.support.v4.content.ContextCompat;

 

import com.facebook.common.executors.CallerThreadExecutor;

import com.facebook.common.references.CloseableReference;

import com.facebook.datasource.DataSource;

import com.facebook.drawee.backends.pipeline.Fresco;

import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;

import com.facebook.drawee.controller.ControllerListener;

import com.facebook.drawee.drawable.AutoRotateDrawable;

import com.facebook.drawee.drawable.ScalingUtils;

import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;

import com.facebook.drawee.generic.RoundingParams;

import com.facebook.drawee.interfaces.DraweeController;

import com.facebook.drawee.view.SimpleDraweeView;

import com.facebook.imagepipeline.common.ResizeOptions;

import com.facebook.imagepipeline.core.ImagePipeline;

import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;

import com.facebook.imagepipeline.image.CloseableImage;

import com.facebook.imagepipeline.request.ImageRequest;

import com.facebook.imagepipeline.request.ImageRequestBuilder;

 

import licola.demo.com.huabandemo.Util.Logger;

 

public class ImageLoadFresco   {

    private static final String   TAG = "ImageLoadFresco";

 

    //必要引數

    private SimpleDraweeView mSimpleDraweeView;

    private Context mContext;

 

 

    

    private ImageLoadFresco(LoadImageFrescoBuilder   frescoBuilder) {

        this.mContext   = frescoBuilder.mContext;

        this.mSimpleDraweeView   = frescoBuilder.mSimpleDraweeView;

 

        //初始化M層 用於初始化圖片中包含的資料

        GenericDraweeHierarchyBuilder   builderM=new GenericDraweeHierarchyBuilder(mContext.getResources());

 

        //初始化C層 用於控制圖片的載入 是主要的實現控制類

        PipelineDraweeControllerBuilder   builderC = Fresco.newDraweeControllerBuilder();

 

        if (frescoBuilder.mUrlLow != null) {

            builderC.setLowResImageRequest(ImageRequest.fromUri(frescoBuilder.mUrlLow));

        }

 

        ImageRequest   request =   ImageRequestBuilder.newBuilderWithSource(Uri.parse(frescoBuilder.mUrl))

                .setResizeOptions(frescoBuilder.mResizeOptions)

                .build();

        builderC.setImageRequest(request);

 

        setViewPerformance(frescoBuilder,   builderM, builderC);

 

        if (frescoBuilder.mControllerListener != null)   {

            builderC.setControllerListener(frescoBuilder.mControllerListener);

        }

 

        DraweeController   draweeController=builderC.build();

 

        if (frescoBuilder.mBitmapDataSubscriber!=null){

            ImagePipeline   imagePipeline = Fresco.getImagePipeline();

 

            DataSource<closeablereference<closeableimage>>   dataSource =

                    imagePipeline.fetchDecodedImage(request,   mSimpleDraweeView.getContext());

            dataSource.subscribe(frescoBuilder.mBitmapDataSubscriber,CallerThreadExecutor.getInstance());

        }

 

        mSimpleDraweeView.setHierarchy(builderM.build());

        mSimpleDraweeView.setController(draweeController);

    }

 

    

    private void setViewPerformance(LoadImageFrescoBuilder frescoBuilder,   GenericDraweeHierarchyBuilder builderM, PipelineDraweeControllerBuilder   builderC) {

 

        //設定圖片的縮放形式

        builderM.setActualImageScaleType(frescoBuilder.mActualImageScaleType);

        if (frescoBuilder.mActualImageScaleType ==   ScalingUtils.ScaleType.FOCUS_CROP) {

            builderM.setActualImageFocusPoint(new PointF(0f, 0f));

        }

 

        if (frescoBuilder.mPlaceHolderImage != null)   {

            builderM.setPlaceholderImage(frescoBuilder.mPlaceHolderImage,   ScalingUtils.ScaleType.CENTER);

        }

 

        if (frescoBuilder.mProgressBarImage != null)   {

            Drawable   progressBarDrawable = new AutoRotateDrawable(frescoBuilder.mProgressBarImage, 2000);

            builderM.setProgressBarImage(progressBarDrawable);

            ////   TODO: 2016/3/18 0018 直接設定無效   是自定義Drawable setColor知識為了類裡面的取值

//              MyProgressBarDrawable progressBarDrawable=new MyProgressBarDrawable();

//              builderM.setProgressBarImage(progressBarDrawable);

        }

 

        //設定重試圖 同時就是設定支援載入影片時重試

        if (frescoBuilder.mRetryImage != null) {

            builderC.setTapToRetryEnabled(true);

            builderM.setRetryImage(frescoBuilder.mRetryImage);

        }

 

        if (frescoBuilder.mFailureImage != null) {

            builderM.setFailureImage(frescoBuilder.mFailureImage);

        }

 

        if (frescoBuilder.mBackgroundImage != null)   {

            builderM.setBackground(frescoBuilder.mBackgroundImage);

        }

 

        if (frescoBuilder.mIsCircle) {

 

            if (frescoBuilder.mIsBorder) {

                //預設白色包邊

                builderM.setRoundingParams(RoundingParams.asCircle().setBorder(0xFFFFFFFF,   2));

            }else {

                builderM.setRoundingParams(RoundingParams.asCircle());

            }

//              builderM.setRoundingParams(RoundingParams.asCircle());

        }

 

        //如果圓角取預設值10 或者是已經修改過的mRadius值

        if (frescoBuilder.mIsRadius) {

            builderM.setRoundingParams(RoundingParams.fromCornersRadius(frescoBuilder.mRadius));

        }

 

    }

 

    //構造器 作為類級內部類

    public static class LoadImageFrescoBuilder   {

        //必要引數

        private Context mContext;

        private SimpleDraweeView mSimpleDraweeView;

        private String mUrl;

 

        //非必要引數

        private String mUrlLow;//低分率圖地址

 

        private Drawable mPlaceHolderImage;//佔點陣圖

        private Drawable mProgressBarImage;//loading

        private Drawable mRetryImage;//重試圖

        private Drawable mFailureImage;//失敗圖

        private Drawable mBackgroundImage;//背景圖

 

        private ScalingUtils.ScaleType   mActualImageScaleType = ScalingUtils.ScaleType.CENTER_CROP;

        private boolean mIsCircle = false;//是否圓形圖片

        private boolean mIsRadius = false;//是否圓角

        private boolean mIsBorder = false;//是否有包邊

        private float mRadius = 10;//圓角度數   預設10

        private ResizeOptions mResizeOptions = new ResizeOptions(3000, 3000);//圖片的大小限制

 

        private ControllerListener mControllerListener;//圖片載入的回撥

 

        private BaseBitmapDataSubscriber   mBitmapDataSubscriber;

        

        public LoadImageFrescoBuilder(Context mContext,   SimpleDraweeView mSimpleDraweeView, String mUrl) {

            this.mContext   = mContext;

            this.mSimpleDraweeView   = mSimpleDraweeView;

            this.mUrl   = mUrl;

        }

 

        

        public ImageLoadFresco build() {

 

//              if (TextUtils.isEmpty(mUrl)) {

//                  throw new IllegalArgumentException("URL不能為空");

//              }

 

            //不能同時設定 圓形圓角

            if (mIsCircle && mIsRadius) {

                throw new IllegalArgumentException("圖片不能同時設定圓角和圓形");

            }

 

            return new ImageLoadFresco(this);

        }

 

        public LoadImageFrescoBuilder   setBitmapDataSubscriber(BaseBitmapDataSubscriber mBitmapDataSubscriber){

            this.mBitmapDataSubscriber   = mBitmapDataSubscriber;

            return this;

        }

 

        public LoadImageFrescoBuilder setUrlLow(String   urlLow) {

            this.mUrlLow   = urlLow;

            return this;

        }

 

        public LoadImageFrescoBuilder   setActualImageScaleType(ScalingUtils.ScaleType mActualImageScaleType) {

            this.mActualImageScaleType   = mActualImageScaleType;

            return this;

        }

 

        public LoadImageFrescoBuilder   setPlaceHolderImage(Drawable mPlaceHolderImage) {

            this.mPlaceHolderImage   = mPlaceHolderImage;

            return this;

        }

 

        public LoadImageFrescoBuilder setProgressBarImage(Drawable   mProgressBarImage) {

            this.mProgressBarImage   = mProgressBarImage;

            return this;

        }

 

        public LoadImageFrescoBuilder   setRetryImage(Drawable mRetryImage) {

            this.mRetryImage   = mRetryImage;

            return this;

        }

 

        public LoadImageFrescoBuilder   setFailureImage(Drawable mFailureImage) {

            this.mFailureImage   = mFailureImage;

            return this;

        }

 

        public LoadImageFrescoBuilder   setBackgroundImage(Drawable mBackgroundImage) {

            this.mBackgroundImage   = mBackgroundImage;

            return this;

        }

 

        public LoadImageFrescoBuilder   setBackgroupImageColor(int colorId) {

            Drawable   color = ContextCompat.getDrawable(mContext, colorId);

            this.mBackgroundImage   = color;

            return this;

        }

 

        public LoadImageFrescoBuilder setIsCircle(boolean mIsCircle) {

            this.mIsCircle   = mIsCircle;

            return this;

        }

 

        public LoadImageFrescoBuilder setIsCircle(boolean mIsCircle, boolean mIsBorder) {

            this.mIsBorder   = mIsBorder;

            this.mIsCircle   = mIsCircle;

            return this;

        }

 

        public LoadImageFrescoBuilder setIsRadius(boolean mIsRadius) {

            this.mIsRadius   = mIsRadius;

            return this;

        }

 

        public LoadImageFrescoBuilder setIsRadius(boolean mIsRadius, float mRadius) {

            this.mRadius   = mRadius;

            return setIsRadius(mIsRadius);

        }

 

        public LoadImageFrescoBuilder setRadius(float mRadius) {

            this.mRadius   = mRadius;

            return this;

        }

 

        public LoadImageFrescoBuilder   setResizeOptions(ResizeOptions mResizeOptions) {

            this.mResizeOptions   = mResizeOptions;

            return this;

        }

 

        public LoadImageFrescoBuilder   setControllerListener(ControllerListener mControllerListener) {

            this.mControllerListener   = mControllerListener;

            return this;

        }

 

    }

}</closeablereference<closeableimage></p>

得到Bitmap物件

在使用Fresco存在這樣一個問題,把圖片快取封裝得太好,導致很難直接得到快取中的Bitmap物件,當然也不是不能。 
根據:文中說明的程式碼。 
我也在上面的封裝程式碼中加入了獲取Bitmap物件的回撥功能。使用如下在回撥中得到物件。

[程式碼]java程式碼:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

new ImageLoadFresco.LoadImageFrescoBuilder(getApplicationContext(),   mImageUser, url)

                   .setBitmapDataSubscriber(new BaseBitmapDataSubscriber() {

                       @Override

                       protected void onNewResultImpl(Bitmap bitmap) {

                         if (bitmap == null) {

                               Logger.d("bitmap   is null");

                           }   else {

                               Logger.d("bitmap   is not null");

                               Drawable   backDrawable = new BitmapDrawable(getResources(), FastBlurUtil.doBlur(bitmap, 25, false));

                               if (Looper.getMainLooper() !=   Looper.myLooper()) {

                                   mAppBar.post(new Runnable() {

                                       @Override

                                       public void run() {

                                           mAppBar.setBackground(backDrawable);

                                       }

                                   });

                               }   else {

                                   mAppBar.setBackground(backDrawable);

                               }

                           }

                       }

 

                       @Override

                       protected void onFailureImpl(DataSource<closeablereference<closeableimage>>   dataSource) {

 

                       }

                   })

                   .build();</closeablereference<closeableimage>

提示:

千萬不要把bitmap複製給onNewResultImpl函式範圍之外的任何變數。訂閱者執行完操作之後,image pipeline 會回收這個bitmap,釋放記憶體。在這個函式範圍內再次使用這個Bitmap物件進行繪製將會導致IllegalStateException。

Bitmap物件物件回撥執行緒有可能是在UI主執行緒回撥,也有可能在子執行緒中回撥,如果需要更新UI,需要做判斷進行不同的邏輯處理。

我上面的程式碼,用bitmap物件構造Drawable物件使用是不會發生異常,已經足夠我的開發使用,以後想到什麼再更新。

總結

·         本文主要是介紹Fresco和封裝理由

·         封裝採用構造器模式,我之後會寫有關構造器模式的博文,幫助大家理解參考。

·         最後介紹Fresco的複雜使用,經過實際執行是滿足功能需求,提供大家在使用Fresco的需要獲取Bitmap時的參考。

原文連結:http://www.apkbus.com/blog-705730-60692.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2471/viewspace-2815242/,如需轉載,請註明出處,否則將追究法律責任。

相關文章