最近公司項(xiàng)目需要加載大量gif圖片,我們項(xiàng)目用的圖片加載庫(kù)是glide,眾所周知glide自帶加載gif功能,但是真實(shí)使用到項(xiàng)目中 glide加載gif會(huì)占用大量?jī)?nèi)存導(dǎo)致應(yīng)用卡頓,嚴(yán)重的會(huì)奔潰。查看glide源碼發(fā)現(xiàn)glide加載gif圖片,使用java解碼,所以導(dǎo)致內(nèi)存增高。想到原來(lái)項(xiàng)目中用過(guò)的一個(gè)加載本地gif的三方庫(kù)( android-gif-drawable ),人家優(yōu)化的就很好沒(méi)有那么卡,就開(kāi)始研究人家的代碼,研究后發(fā)現(xiàn)人家這個(gè)庫(kù)性能好的原因是他用giflib來(lái)解碼gif,但是他這個(gè)庫(kù)只能加載本地圖片,而我們項(xiàng)目需要加載網(wǎng)絡(luò)圖片,所以就想把glide和giflib做一個(gè)結(jié)合,使用glide下載圖片,bitmap緩存的功能,解碼器替換成giflib,經(jīng)過(guò)一天研究終于成功了,寫文章記錄一下,也希望其他新手朋友有同樣需求的有現(xiàn)成的參考O(∩_∩)O。
使用giflib需要一點(diǎn)ndk開(kāi)發(fā)的經(jīng)驗(yàn),最起碼能看懂cmake語(yǔ)法,因?yàn)楝F(xiàn)在android開(kāi)發(fā)ndk都是使用cmalelist。
ndk部分
首先需要下載 framesequence 及 giflib(以上網(wǎng)站需要翻墻,請(qǐng)自備梯子)
giflib目錄如下
framesequence 項(xiàng)目jin目錄如下
按以下步驟操作
1. 使用AndroidStudio創(chuàng)建NDK項(xiàng)目。
2. 把framesequence下的jin文件復(fù)制到自己項(xiàng)目的cpp文件夾下(只復(fù)制我圖片中的.cpp .h文件 多余部分請(qǐng)不要復(fù)制),把giflib文件夾復(fù)制到cpp目錄下。
3. 在cpp文件夾下新增util文件夾,創(chuàng)建log,math的頭文件。(文末會(huì)給demo鏈接,里面有這個(gè)兩個(gè)文件)
4. 重新寫CMackList.txt如下
cmake_minimum_required(VERSION 3.4.1)
file(GLOB_RECURSE GIF_LIB ${CMAKE_SOURCE_DIR}/giflib/*.*)
file(GLOB_RECURSE FRAME_SEQUENCE ${CMAKE_SOURCE_DIR}/*.cpp*)
add_library(mygif
SHARED
${FRAME_SEQUENCE}
${GIF_LIB})
list(APPEND LIBS
jnigraphics
android
GLESv2
log
)
set(LIBS)
list(APPEND LIBS
jnigraphics
android
GLESv2
log
)
target_link_libraries(mygif ${LIBS})
最終項(xiàng)目的cpp文件是這樣的
這時(shí)候點(diǎn)擊Build -> Refresh LInked C++ Projects
等待項(xiàng)目編譯好后,運(yùn)行項(xiàng)目看是否能跑起來(lái),如果跑起來(lái)證明so已生成,關(guān)于ndk的部分就結(jié)束了,剩下只有java代碼了。(●ˇ?ˇ●)
java部分
1. 把framesequence項(xiàng)目中的android文件夾復(fù)制到你自己項(xiàng)目的java文件夾下,如圖
2. 項(xiàng)目導(dǎo)入glide(4.+版本),創(chuàng)建一個(gè)類 繼承 AppGlideModule,使用過(guò)glide都知道這個(gè)類的用處,可以生成GlideApp,設(shè)置緩存大小,緩存路徑等功能。我們繼承這個(gè)類主要的目的是替換glide的gif加載
@GlideModule
public class GifGlideModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context,
@NonNull Glide glide, @NonNull Registry registry) {
super.registerComponents(context, glide, registry);
registry.append(Registry.BUCKET_GIF, InputStream.class,
FrameSequenceDrawable.class, new GifDecoder(glide.getBitmapPool()));
}
}
這時(shí)發(fā)現(xiàn)GifDecoder報(bào)錯(cuò),這個(gè)文件是需要我們自己編寫的,代碼如下
public class GifDecoder implements ResourceDecoder<InputStream, FrameSequenceDrawable> {
private BitmapPool bitmapPool;
public GifDecoder(BitmapPool bitmapPool) {
this.bitmapPool = bitmapPool;
}
@Override
public boolean handles(@NonNull InputStream source, @NonNull Options options) throws IOException {
return true;
}
@Nullable
@Override
public Resource<FrameSequenceDrawable> decode(@NonNull InputStream source, int width, final int height, @NonNull Options options) throws IOException {
FrameSequence frameSequence = FrameSequence.decodeStream(source);
FrameSequenceDrawable frameSequenceDrawable = new FrameSequenceDrawable(frameSequence, new FrameSequenceDrawable.BitmapProvider() {
@Override
public Bitmap acquireBitmap(int minWidth, int minHeight) {
return bitmapPool.get(minWidth, minHeight, Bitmap.Config.ARGB_8888);
}
@Override
public void releaseBitmap(Bitmap bitmap) {
bitmapPool.put(bitmap);
}
});
return new GifResource(frameSequenceDrawable);
}
}
GifResource文件也是自己編寫的,代碼如下
public class GifResource extends DrawableResource<FrameSequenceDrawable> {
public GifResource(FrameSequenceDrawable drawable) {
super(drawable);
}
@NonNull
@Override
public Class<FrameSequenceDrawable> getResourceClass() {
return FrameSequenceDrawable.class;
}
@Override
public int getSize() {
return 0;
}
@Override
public void recycle() {
drawable.stop();
drawable.destroy();
}
}
到這里 所有的代碼都寫完,重新編譯項(xiàng)目,讓glide生成GlideApp
在代碼中使用
String gif = "gif格式的圖片url";
GlideApp.with(this).as(FrameSequenceDrawable.class).load(gif).into(imageView);