问题来源
android 可以在 gallery 里面显示内部存储的图片,支持 jpeg,png,gif,bmp 等,甚至文件类型和图片后缀名不一致,只要是图片文件就能显示,然后 git 只会显示第一帧图像,然而 android 其实是可以显示 gif 动画的,在浏览器里打开 gif 动画,就能够正常加载显示。
Gallery 查看图片
gallery 实现了 Gallery/src/com/android/camera/ViewImage.java
ImageViewTouchBase newView = mSlideShowImageViews[mSlideShowImageCurrent]; newView.setVisibility(View.VISIBLE); newView.setImageRotateBitmapResetBase(bitmap, true); newView.bringToFront();
通过自己实现的 ImageViewTouchBase 装载图片并显示
ImageViewTouchBase 继承自 framework 的 ImageView
package com.android.camera; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Handler; import android.util.AttributeSet; import android.view.KeyEvent; import android.widget.ImageView; abstract class ImageViewTouchBase extends ImageView {
framework 处理图片
ImageView 是 android 图片框架实现通用图片处理显示类:
可以显示 resource 里面的图片,
public void setImageResource(int resId) { if (mUri != null || mResource != resId) { updateDrawable(null); mResource = resId; mUri = null; final int oldWidth = mDrawableWidth; final int oldHeight = mDrawableHeight; resolveUri(); if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { requestLayout(); } invalidate(); } }
我们在 gallery 里面打开的图片调用是通过 uri 传过来的,
public void setImageURI(Uri uri) { if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) { updateDrawable(null); mResource = 0; mUri = uri; final int oldWidth = mDrawableWidth; final int oldHeight = mDrawableHeight; resolveUri(); if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { requestLayout(); } invalidate(); } }
android 会解析 uri 得到目标文件路径
} else if (mUri != null) { String scheme = mUri.getScheme(); if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { try { // Load drawable through Resources, to get the source density information ContentResolver.OpenResourceIdResult r = mContext.getContentResolver().getResourceId(mUri); d = r.r.getDrawable(r.id); } catch (Exception e) { Log.w("ImageView", "Unable to open content: " + mUri, e); } } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) || ContentResolver.SCHEME_FILE.equals(scheme)) { InputStream stream = null; try { stream = mContext.getContentResolver().openInputStream(mUri); d = Drawable.createFromStream(stream, null); } catch (Exception e) { Log.w("ImageView", "Unable to open content: " + mUri, e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { Log.w("ImageView", "Unable to close content: " + mUri, e); } } } } else { d = Drawable.createFromPath(mUri.toString()); }
我们点击 gallery 的图片会产生包含 content 的 uri:
I/ActivityManager( 1304): START u0 {dat=content://media/external/images/media/21 cmp=com.android.gallery/com.android.camera.ViewImage (has extras)} from pid 1706
然后通过 Drawable 从 inputstream 里面解析创建 Drawable 对象
public static Drawable createFromStream(InputStream is, String srcName) { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable"); try { return createFromResourceStream(null, null, is, srcName, null); } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } }
后面的流程就是交给 skia 解析然后获得一个 bitmap 并返回了。 skia 的 decode 可以自动识别 steam 里面的数据流创建相应的 decoder。
问题答案
framework 是通用的图片显示处理,最后只返回了一个 bitmap ,所以 gif 只能处理第一帧图像了,但是 browser 是从新实现了图像显示接口,有专门针对 gif 的显示处理,所以可以显示动画。