每次的探索都源于好奇,每次的收获都是坚持的结果。
一、简介
本文章介绍三种主流的方法去播放gif图片,并介绍优劣势方便今后使用的时候更快更容易的找到自己需要使用的播放gif的方法,后续可能有对应原理的解析,此篇只用于介绍使用。
二、可收获
- 了解三种gif的播放方式,以及如何使用
- 知道三种gif播放的优劣势
- 了解如何从网络下载gif,知道一些rxjava和retrofit的知识。
三、android播放gif的方法
1)使用Movie播放Gif
- 基本原理:使用Movie类,将图片加载到Movie内,通过不断的绘制,不断的切换时间,达到播放Gif的目的。
- 基本操作:
– 首先自定义一个GifView用于使用Movie和接收Gif图片。(使用前先在对应位置放好Gif文件)
–代码如下
public class GifView extends View {
private Movie mMovie;
private long mMovieStart = 0;
public GifView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void startGif(String path) {
//此方法用于加载drawable内的gif图片
// mMovie = Movie.decodeStream(getResources().openRawResource(R.drawable.test));
byte[] array = new byte[0];
try {
//用于加载本地文件gif图片
array = streamToBytes(new FileInputStream(path));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
mMovie = Movie.decodeByteArray(array, 0, array.length);
invalidate();
}
//关键代码,原理已经说过
@Override
public void onDraw(Canvas canvas) {
long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) {
mMovieStart = now;
}
if (mMovie != null) {
int dur = mMovie.duration();
if (dur == 0) {
dur = 1000;
}
int relTime = (int) ((now - mMovieStart) % dur);
mMovie.setTime(relTime);
mMovie.draw(canvas, 0, 0);
invalidate();
}
}
private static byte[] streamToBytes(InputStream is) {
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
byte[] buffer = new byte[1024];
int len;
try {
while ((len = is.read(buffer)) >= 0) {
os.write(buffer, 0, len);
}
} catch (java.io.IOException e) {
}
return os.toByteArray();
}
}
- 结论:此方法我测试过,速度很慢,此方法也可以控制gif的播放,可以通过是否重绘进行控制,或者可以使用ValueAnimator ,根据对应的Gif时长生成对应长度的value,实时根据value设置time,用于控制播放暂停等操作,但根本原因加载太慢不推荐使用。
2)使用glide加载gif
- 基本原理:glide内部会识别你是用的是什么类型的图片,如果是gif图片,内部会转变为GifDrawable用于播放。
- 代码:
-首先配置build.gradle
-compile 'com.github.bumptech.glide:glide:3.7.0'
-配置仓库
buildscript {
repositories {
jcenter()
mavenCentral()
}
allprojects {
repositories {
jcenter()
mavenCentral()
}
}
-使用:
ImageView imageView = (ImageView) findViewById(R.id.test_img);
//load方法内可以传入资源id也可以传入下载链接
Glide.with(this).load(GifUtils.localPath).into(imageView);
- 结论:该方法首次加载也比较慢,但Glide内有缓存,无需考虑数据的来源(本地或者网络),不需要考虑数据的缓存。刚开始以为加载速度慢是因为下载的问题,后面加载本地的速度依然很慢,具体原因后面探索,初步怀疑是java解析的速度慢。想轻松一点的话可以使用此方法,如果追求速度,请看下面的。
3)使用android-gif-drawable
- 基本原理:内部转化为gifdrawable,类似于使用动画一样进行播放,内部使用c++的方法逐帧播放。内部原理还需探究。
-代码如下:
-非下载的使用方式:
GifImageView gifImageView= (GifImageView) findViewById(R.id.test_img);
gifImageView.setImageDrawable(new GifDrawable(new File(GifUtils.localPath)));
-下载的方式:
首先定义下载:
public interface ServiceApi {
//下载文件
@GET
Observable<ResponseBody> downloadPicFromNet(@Url String fileUrl);
}
其次定义下载和显示:
public class DownloadPicUtils {
public static void getPicFromNet(final GifImageView gifImageView) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.baidu.com/")//此处是无效基地址,但是不能写空字符串
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) //添加Rxjava
.addConverterFactory(GsonConverterFactory.create()) // 默认添加后返回gson格式,但是首先他会根据你返回的类型去判断默认的是否可以,默认的就是返回responseBody,如果满足不执行其他的,反之继续遍历转换器,找到符合的
.build();
ServiceApi serviceApi = retrofit.create(ServiceApi.class);
serviceApi.downloadPicFromNet(GifUtils.netPath)
.subscribeOn(Schedulers.newThread())//在新线程中实现该方法
.map(new Func1<ResponseBody, String>() {
@Override
public String call(ResponseBody arg0) {
return saveImgFromNet(arg0);
}
})
.observeOn(AndroidSchedulers.mainThread())//在Android主线程中展示
.subscribe(new Subscriber<String>() {
@Override
public void onStart() {
super.onStart();
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable arg0) {
Log.v("info", "error:" + arg0.toString());
}
@Override
public void onNext(String arg0) {
try {
gifImageView.setImageDrawable(new GifDrawable(new File(GifUtils.localPath)));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
private static String saveImgFromNet(ResponseBody arg0) {
try {
FileHelper.saveBytesToFile(arg0.bytes(), GifUtils.localPath);
} catch (IOException e) {
e.printStackTrace();
}
return GifUtils.localPath;
}
}
文件存储辅助类:
public class FileHelper {
public static byte[] getBytesFromStream(InputStream is) throws IOException {
int len;
int size = 1024;
byte[] buf;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
buf = new byte[size];
while ((len = is.read(buf, 0, size)) != -1) {
bos.write(buf, 0, len);
}
buf = bos.toByteArray();
return buf;
}
public static void saveBytesToFile(byte[] bytes, String path) throws IOException {
FileOutputStream fileOuputStream = null;
try {
fileOuputStream = new FileOutputStream(path);
fileOuputStream.write(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOuputStream != null) {
fileOuputStream.close();
}
}
}
}
使用方式:
GifImageView gifImageView= (GifImageView) findViewById(R.id.test_img);
DownloadPicUtils.getPicFromNet(gifImageView);
- 此方法加载gif速度很快,即使加上下载的速度仍比其他的两种快,就是内部没有缓存和网络模块,需要自己去实现,不过如果追求性能,这个是属于最推荐的方式