****每日一題****: Glide
面試率: ★★★☆☆
面試技巧與建議
圖庫(kù)在Android實(shí)際項(xiàng)目中基本上都會(huì)使用到,這種概率跟網(wǎng)絡(luò)庫(kù)幾乎一樣的,因此出去面試前準(zhǔn)備好一套圖庫(kù)面試技巧是不可避免的.
面試建議
在最近的面試中很多應(yīng)聘者都存在如下幾個(gè)問(wèn)題:
基本都使用/了解過(guò)
你去面試一家公司,別人問(wèn)這個(gè)問(wèn)題你說(shuō)使用過(guò),但是面試官一天面試7-8個(gè)人,其他應(yīng)聘者也會(huì)使用,那么你們區(qū)別在哪里?不知道其底層網(wǎng)絡(luò)協(xié)議
一個(gè)網(wǎng)路底層使用socket還是http這個(gè)是最基本的了解.不知道為什么使用它
圖庫(kù)有很多種如Fresco,Picasso,imgLoader等,然而為什么使用Glide呢,你是否知道他的特點(diǎn)?不知道如何封裝
使用過(guò)那如何使用?做了什么封裝?如何自定義Glide?在什么地方用過(guò)
項(xiàng)目中哪個(gè)模塊使用過(guò)這個(gè)庫(kù)呢,是全局使用,還是局部使用?
對(duì)于上述問(wèn)題可以了解并答出,才能證明你有這方面相關(guān)開(kāi)發(fā)經(jīng)驗(yàn),否則只能說(shuō)你只是看過(guò)Glide.
面試技巧
一般開(kāi)發(fā)中迭代速度比較快的情況下,不會(huì)有很多時(shí)間去深入的探討開(kāi)源庫(kù)中的全部源碼,但是如果跟業(yè)務(wù)有關(guān)聯(lián)的話,我們才會(huì)去做研究,而研究的也是全部源碼中的某塊功能或者模塊.
- Glide Module自定義緩存
圖片框架中很多自定義的實(shí)現(xiàn),而緩存也是框架中最為常見(jiàn)的行為之一。因此在開(kāi)發(fā)使用中有些項(xiàng)目需求不凡試下自定義Glide的緩存.
下面是一篇自定義Glide的緩的文章:
Glide Module
面試題
下面是我從源碼中提取出來(lái)的一些問(wèn)題,簡(jiǎn)單而實(shí)用.
你為什么使用Glide?
Glide特點(diǎn)
- 使用簡(jiǎn)單
- 可配置度高呆盖,自適應(yīng)程度高
- 支持常見(jiàn)圖片格式
Jpg png gif webp
- 支持多種數(shù)據(jù)源
網(wǎng)絡(luò)拖云、本地、資源应又、Assets 等
- 高效緩存策略
支持Memory和Disk圖片緩存 默認(rèn)Bitmap格式采用RGB_565內(nèi)存使用至少減少一半
- 生命周期集成
根據(jù)Activity/Fragment生命周期自動(dòng)管理請(qǐng)求
- 高效處理Bitmap
使用Bitmap Pool使Bitmap復(fù)用宙项,主動(dòng)調(diào)用recycle回收需要回收的Bitmap,減小系統(tǒng)回收壓力
Glide是如何提高加載圖片的性能株扛?
總共有兩點(diǎn):
通過(guò)控制圖片大小.
.override(200,200)可以設(shè)置加載圖片大小尤筐,但是實(shí)際大小不一定是200x200,通過(guò)源碼分析下:
在BitmapRequestBuilder中的private Downsampler downsampler = Downsampler.AT_LEAST
默認(rèn)就是設(shè)置了尺寸優(yōu)化,超過(guò)最大比例的就會(huì)對(duì)圖片進(jìn)行等比例縮放,如何縮放見(jiàn)下面;在Downsampler中的decode方法中汇荐,獲取的Bitmap大小變成1/sampleSize,倍數(shù)通過(guò)getSampleSize計(jì)算所得盆繁,
inSampleSize 是 BitmapFactory.Options的屬性掀淘,應(yīng)該大家都知道。然后再看看怎么生成.override(200,200)油昂,如果沒(méi)有設(shè)置Glide默認(rèn)是FitCenter革娄,查看FitCenter可以看到圖片截取方式。
舉個(gè)例子:
加載的圖片大小為1080x540冕碟,如果使用了.override(200,200)默認(rèn)緩存一張200x100的圖片,也就是默認(rèn)存儲(chǔ)結(jié)果RESULT.
通過(guò)設(shè)置Bitmap Format的圖片類型
為了降低內(nèi)存消耗拦惋,Glide默認(rèn)配置的Bitmap Format 為 RGB_565,修改GlideBuilder's setDecodeFormat設(shè)置.
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
Glide為何要使用額外的無(wú)界面的Fragment/activity鸣哀?
根據(jù)傳入Context的類型有不同的實(shí)現(xiàn)架忌,這里以FragmentActivity為例(現(xiàn)在常用的MD樣式Activity類AppCompatActivity是FragmentActivity的子類)。方法get(FragmentActivity activity)調(diào)用了方法supportFragmentGet(activity, fm)我衬,后者返回的對(duì)象類型是SupportRequestManagerFragment 叹放。SupportRequestManagerFragment 是一個(gè)無(wú)界面的Fragment類,起到把請(qǐng)求和Activity生命周期同步的作用挠羔。
Glide.with() 不僅僅只是Context還可以是Activity,Fragment等井仰,傳入后自動(dòng)適配,Glide加載圖片是會(huì)隨著Activity,Fragment 的生命周期破加,具體可以參考LifecycleListener俱恶,所以推薦使用Activity,Fragment.
Glide的緩存策略是怎么做的?
通過(guò)該方法設(shè)置策略.diskCacheStrategy(DiskCacheStrategy.ALL)
DiskCacheStrategy 分別有以下幾種選擇,ALL緩存原圖和截取后的圖范舀,NONE 不緩存合是,SOURCE 只緩存原圖,RESULT緩存截取后的圖.
因此如果圖片需要分享或需要原圖的建議緩存ALL锭环,否則只緩存RESULT.
Glide的核心GlideModule
的作用是什么?
- GlideModule是對(duì)glide全局配置相關(guān)的類.
- 如可以設(shè)置緩存策略.
-
更多的配置如下
img-w400
- 可以通過(guò)實(shí)現(xiàn)GlideModule接口來(lái)自定義一個(gè)Glide圖庫(kù),可以通過(guò)他改變Glide的行為和基礎(chǔ)配置.
在自定義GlideModule時(shí)需要注意什么?
要全局的去聲明這個(gè)類聪全,讓 Glide 知道它應(yīng)該在哪里被加載和使用。Glide 會(huì)掃描 AndroidManifest.xml 為 Glide module 的 meta 聲明辅辩。具體可以查看源碼在Glide.get(Context),通過(guò)ManifestParser對(duì)象獲取GlideModule集合.
因此难礼,你必須在 AndroidManifest.xml 的 < application> 標(biāo)簽內(nèi)去聲明這個(gè)剛剛創(chuàng)建的 Glide module。
- 創(chuàng)建GlideModel
public class MyGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}
- 在AndroidManifest.xml的meta-data配置GlideModule
<meta-data android:name="com.branch.glidedemo.MyGlideModule"
android:value="GlideModule"/>
- 解決GlideModel沖突有可能加入的library中也同樣配置了GlideModule,如果配置了多個(gè)會(huì)出現(xiàn)沖突玫锋,無(wú)法編譯運(yùn)行蛾茉,解決方式可在AndroidManifest.xml移除
<meta-data android:name=”com.mypackage.MyGlideModule” tools:node=”remove” />
實(shí)際開(kāi)發(fā)中的一些問(wèn)題
為什么 有的圖片第一次加載的時(shí)候只顯示占位圖,第二次才顯示正常的圖片呢撩鹿?
如果你剛好使用了這個(gè)圓形Imageview庫(kù)或者其他的一些自定義的圓形Imageview谦炬,而你又剛好設(shè)置了占位的話,那么节沦,你就會(huì)遇到第一個(gè)問(wèn)題吧寺。如何解決呢窜管?方案一: 不設(shè)置占位;方案二:使用Glide的Transformation API自定義圓形Bitmap的轉(zhuǎn)換稚机。這里是一個(gè)已有的例子幕帆;方案三:使用下面的代碼加載圖片:
Glide.with(mContext)
.load(url)
.placeholder(R.drawable.loading_spinner)
.into(new SimpleTarget<Bitmap>(width, height) {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
// setImageBitmap(bitmap) on CircleImageView
}
});
該方法在listview上復(fù)用有問(wèn)題的bug,如果在listview中加載CircleImageView,請(qǐng)不要使用該方法赖条。
方案四:不使用Glide的默認(rèn)動(dòng)畫(huà):
Glide.with(mContext)
.load(url)
.dontAnimate()
.placeholder(R.drawable.loading_spinner)
.into(circleImageview);
為什么 我總會(huì)得到類似You cannot start a load for a destroyed activity
這樣的異常呢失乾?
請(qǐng)記住一句話:不要再非主線程里面使用Glide加載圖片,如果真的使用了纬乍,請(qǐng)把context參數(shù)換成getApplicationContext碱茁。
提示:這個(gè)問(wèn)題主要是context對(duì)應(yīng)的生命周期引起的.當(dāng) Glide 檢測(cè)到 Activity 被銷毀時(shí),會(huì)自動(dòng)取消等待中的請(qǐng)求.如果你傳遞的是一個(gè)
getApplicationContext,Glide就不能對(duì)其進(jìn)行優(yōu)化,所以適當(dāng)選擇仿贬。
為什么 我不能給加載的圖片setTag()呢纽竣?
首先Glide的全局tag只是為了保證你可以正常的使用view.setTag方法,和錯(cuò)位沒(méi)關(guān)系的茧泪。因?yàn)镚lide內(nèi)部就是通過(guò)view.setTag來(lái)保證不錯(cuò)位的.
進(jìn)一步說(shuō)明:
當(dāng)圖片從ListView 中移出屏幕時(shí)蜓氨,Glide 也會(huì)取消其對(duì)應(yīng)的請(qǐng)求。
由于大多數(shù)開(kāi)發(fā)者在 adapter 中重用 View队伟,Glide 會(huì)給在請(qǐng)求數(shù)據(jù)時(shí)給對(duì)應(yīng)的ImageView 附加一個(gè) tag穴吹,然后再載入其他圖片時(shí)檢查這個(gè) tag,如果存在的話取消第一個(gè)請(qǐng)求,進(jìn)而形成的優(yōu)化嗜侮。
使用setTag(int,object)方法設(shè)置tag,具體用法如下:Java代碼是醬紫的:
Glide.with(context).load(urls.get(i).getUrl()).fitCenter().into(imageViewHolder.image);
imageViewHolder.image.setTag(R.id.image_tag, i);
imageViewHolder.image.setOnClickListener(new View.OnClickListener() {
@Override
int position = (int) v.getTag(R.id.image_tag);
Toast.makeText(context, urls.get(position).getWho(), Toast.LENGTH_SHORT).show();
}
});
同時(shí)在values文件夾下新建ids.xml港令,添加
<item name="image_tag" type="id"/>
方案二:從Glide的3.6.0之后,新添加了全局設(shè)置的方法锈颗。具體方法如下:先實(shí)現(xiàn)GlideMoudle接口顷霹,全局設(shè)置ViewTaget的tagId:
public class MyGlideMoudle implements GlideModule{
@Override
public void applyOptions(Context context, GlideBuilder builder) {
ViewTarget.setTagId(R.id.glide_tag_id);
}
@Override
public void registerComponents(Context context, Glide glide) {
}
}
同樣,也需要在ids.xml下添加id
<item name="glide_tag_id" type="id"/>
最后在AndroidManifest.xml文件里面添加
<meta-data
android:name="com.yourpackagename.MyGlideMoudle"
android:value="GlideModule" />
方案三:寫(xiě)一個(gè)繼承自ImageViewTaget的類击吱,復(fù)寫(xiě)它的get/setRequest方法
Glide.with(context).load(urls.get(i).getUrl()).fitCenter().into(new ImageViewTarget<GlideDrawable>(imageViewHolder.image) {
@Override
protected void setResource(GlideDrawable resource) {
imageViewHolder.image.setImageDrawable(resource);
}
@Override
public void setRequest(Request request) {
imageViewHolder.image.setTag(i);
imageViewHolder.image.setTag(R.id.glide_tag_id,request);
}
@Override
public Request getRequest() {
return (Request) imageViewHolder.image.getTag(R.id.glide_tag_id);
}
});
imageViewHolder.image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = (int) v.getTag();
Toast.makeText(context, urls.get(position).getWho(), Toast.LENGTH_SHORT).show();
}
});
Glide常見(jiàn)錯(cuò)誤
java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity
解決辦法 在使用Glide的那段代碼加是否在主線程判斷
if(Util.isOnMainThread()) {
Glide.with(MyActivity.this).load(strURL).into(imageView);
}
在onDestory加
@Override
protected void onDestroy() {
super.onDestroy();
if(Util.isOnMainThread()) {
Glide.with(this).pauseRequest();
}
并且所有的this 都要寫(xiě)成getApplicationContext ,這個(gè)主要針對(duì)于在子線程使用Glide.
Glide的已經(jīng)寫(xiě)的不錯(cuò)了****,****有沒(méi)對(duì)他做些進(jìn)階的優(yōu)化/使用?
Glide.with(context).resumeRequests()和 Glide.with(context).pauseRequests()
當(dāng)列表在滑動(dòng)的時(shí)候淋淀,調(diào)用pauseRequests()取消請(qǐng)求,滑動(dòng)停止時(shí)姨拥,調(diào)用resumeRequests()恢復(fù)請(qǐng)求绅喉。這樣是不是會(huì)好些呢渠鸽?Glide.clear()
當(dāng)你想清除掉所有的圖片加載請(qǐng)求時(shí)叫乌,這個(gè)方法可以幫助到你。ListPreloader
如果你想讓列表預(yù)加載的話徽缚,不妨試一下ListPreloader這個(gè)類憨奸。
可以提升圖片加載速度。首先是在圖片展示前預(yù)讀取數(shù)據(jù)凿试,
它提供了一個(gè) ListPreloader排宰,通過(guò)預(yù)加載 item 的數(shù)量初始化似芝。接著通過(guò)
setOnScrollListener(OnScrollListener) 把 ListPreloader 設(shè)置給 ListView。如果你想在 ListView 之外預(yù)載圖片板甘,只要調(diào)用上面 DrawableRequestBuilder 對(duì)象的 downloadOnly() 方法就好党瓮,像這樣 builder.downloadOnly();