Movie を使うとアニメーション Gif を表示できるのですが、まぁバグがあるとかなんとかは自分でぐぐってください。
よくあるサンプルは assets や raw にある Gif を読むやつばっかりです。
InputStream is = context.getResources().openRawResource(R.drawable.piggy);
Movie movie = Movie.decodeStream(is);
http://androidosbeginning.blogspot.jp/2010/09/gif-animation-in-android.html
とか
InputStream is = context.getAssets().open("piggy.gif");
Movie movie = Movie.decodeStream(is);
とか
SD にある Gif ファイルから読もうとして
try {
InputStream is = new FileInputStream(filePath);
Movie movie = Movie.decodeStream(is);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
とすると Native 側で IOException が起こって表示されません。
LogCat で
09-19 15:38:37.828: W/System.err(7523): java.io.IOException
09-19 15:38:37.828: W/System.err(7523): at java.io.InputStream.reset(InputStream.java:218)
09-19 15:38:37.828: W/System.err(7523): at android.graphics.Movie.decodeStream(Native Method)
と出ていて、どうも InputStream の reset() で起こっていると。
InputStream のコードをみると、サブクラスが Override しないかぎり IOException が投げられるようになっています。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/libcore/luni/src/main/java/java/io/InputStream.java#217
217 public synchronized void reset() throws IOException {
218 throw new IOException();
219 }
FileInputStream は Override してませんでした。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/libcore/luni/src/main/java/java/io/FileInputStream.java
AssetManager の open() で返ってくる InputStream は AssetInputStream です。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/content/res/AssetManager.java#288
288 public final InputStream open(String fileName) throws IOException {
289 return open(fileName, ACCESS_STREAMING);
290 }
...
309 public final InputStream open(String fileName, int accessMode)
310 throws IOException {
311 synchronized (this) {
312 if (!mOpen) {
313 throw new RuntimeException("Assetmanager has been closed");
314 }
315 int asset = openAsset(fileName, accessMode);
316 if (asset != 0) {
317 AssetInputStream res = new AssetInputStream(asset);
318 incRefsLocked(res.hashCode());
319 return res;
320 }
321 }
322 throw new FileNotFoundException("Asset file: " + fileName);
323 }
この AssetInputStream は reset() を Override しています。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/content/res/AssetManager.java#538
538 public final class AssetInputStream extends InputStream {
...
569 public final void reset() throws IOException {
570 seekAsset(mAsset, mMarkPos, -1);
571 }
...
597 }
InputStream のサブクラスで reset() を継承しているクラスとして
BufferedInputStream があります。
このクラスを使って、次のようにすると IOException が発生せずに読むことができます。
try {
InputStream stream = new BufferedInputStream(
new FileInputStream(filePath), 16 * 1024);
stream.mark(16 * 1024);
mMovie = Movie.decodeStream(stream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
ポイントは mark() メソッドを呼ぶ必要がある点です。
ちなみに Movie には deocdeFile() というメソッドもあるのですが、なかみは FileInputStream 使ってるだけなので、使えません。。。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/graphics/java/android/graphics/Movie.java#51
51 public static Movie decodeFile(String pathName) {
52 InputStream is;
53 try {
54 is = new FileInputStream(pathName);
55 }
56 catch (java.io.FileNotFoundException e) {
57 return null;
58 }
59 return decodeTempStream(is);
60 }
おまけ
Movie.cpp
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/jni/android/graphics/Movie.cpp#82
82 static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) {
83
84 NPE_CHECK_RETURN_ZERO(env, istream);
85
86 // what is the lifetime of the array? Can the skstream hold onto it?
87 jbyteArray byteArray = env->NewByteArray(16*1024);
88 SkStream* strm = CreateJavaInputStreamAdaptor(env, istream, byteArray);
89 if (NULL == strm) {
90 return 0;
91 }
92
93 SkMovie* moov = SkMovie::DecodeStream(strm);
94 strm->unref();
95 return create_jmovie(env, moov);
96 }
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp#CreateJavaInputStreamAdaptor
12 class JavaInputStreamAdaptor : public SkStream {
13 public:
14 JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar)
15 : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) {
16 SkASSERT(ar);
17 fCapacity = env->GetArrayLength(ar);
18 SkASSERT(fCapacity > 0);
19 fBytesRead = 0;
20 }
21
22 virtual bool rewind() {
23 JNIEnv* env = fEnv;
24
25 fBytesRead = 0;
26
27 env->CallVoidMethod(fJavaInputStream, gInputStream_resetMethodID);
28 if (env->ExceptionCheck()) {
29 env->ExceptionDescribe();
30 env->ExceptionClear();
31 SkDebugf("------- reset threw an exception\n");
32 return false;
33 }
34 return true;
35 }
...
145 SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
146 jbyteArray storage, int markSize) {
147 static bool gInited;
148
149 if (!gInited) {
150 jclass inputStream_Clazz = env->FindClass("java/io/InputStream");
151 RETURN_NULL_IF_NULL(inputStream_Clazz);
152
153 gInputStream_resetMethodID = env->GetMethodID(inputStream_Clazz,
154 "reset", "()V");
155 gInputStream_markMethodID = env->GetMethodID(inputStream_Clazz,
156 "mark", "(I)V");
157 gInputStream_availableMethodID = env->GetMethodID(inputStream_Clazz,
158 "available", "()I");
159 gInputStream_readMethodID = env->GetMethodID(inputStream_Clazz,
160 "read", "([BII)I");
161 gInputStream_skipMethodID = env->GetMethodID(inputStream_Clazz,
162 "skip", "(J)J");
163
164 RETURN_NULL_IF_NULL(gInputStream_resetMethodID);
165 RETURN_NULL_IF_NULL(gInputStream_markMethodID);
166 RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
167 RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
168 RETURN_NULL_IF_NULL(gInputStream_skipMethodID);
169
170 gInited = true;
171 }
172
173 if (markSize) {
174 env->CallVoidMethod(stream, gInputStream_markMethodID, markSize);
175 }
176
177 return new JavaInputStreamAdaptor(env, stream, storage);
178 }