この問題を解決するのに数日を費やしましたが、まだ解決策が見つかりません。私はAndroidの初心者ですので、コードが面倒かもしれません!メディアのサムネイルを効率的に読み込みます。
画像や動画のサムネイルを表示するRecyclerView(グリッドレイアウト)があります。特定のフォルダにメディアファイルを読み込みます。しかし、私がこの活動を開始すると、それは非常に多くの記憶を取ります!
サムネイルを読み込むために、2つのスレッドを作成しました。
スレッド1)MediaLoadThreadは、SDCardのメディアファイルを照会します。カーソルをループし、サムネイルデコードのタスクを別のスレッドにキューイングします。
スレッド2)ThumbnailLoaderThreadそれぞれのサムネイルをデコードします。コンテンツレゾルバ、メディアタイプ(イメージまたはビデオ)、およびメディアIDを受け取ります。基本的な.getThumbnail()メソッドを使用します。サムネイルを取得すると、呼び出し元スレッド(MediaLoadThread)に応答コールバックがトリガーされます。
3)MediaLoadThread(スレッド1)がコールバックを受信すると、別のコールバックがトリガーされ、アクティビティは指定された位置のアダプタ項目を更新します。アダプターによってUIが更新され、最後にサムネイルImageViewがプレースホルダーから実際のサムネイルに変更されます。
:::ここに私のコードです:::
1)MediaLoadThread.java
@Override
public void run() {
mMediaDataArr.clear();
mLoaderThread.start(); // Prepping the thread 2
mLoaderThread.prepareHandler();
// .... SD Card query stuff .....
if (mediaCursor != null && mediaCursor.moveToFirst()) {
do {
mMediaDataArr.add(new MediaData(videoCursor.getInt(columnIndexId),
mediaCursor.getLong(columnIndexDate), //ID
mediaCursor.getInt(columnIndexType), //MEDIA TYPE
null); //THUMBNAIL BITMAP (NULL UNTIL THE ACTUAL THUMBNAIL IS DECODED)
} while (mediaCursor.moveToNext());
mediaCursor.close();
mResponseHandler.post(new Runnable() {
@Override
public void run() {
// This callback lets the activity activate the adapter and recyclerview so that the user can interact with recyclerview before the app finishes decoding thumbnails.
mCallback.onVideoLoaded(mMediaDataArr);
}
});
//Passing tasks to thread 2
for (int i = 0; i < mMediaDataArr.size(); i++) {
mLoaderThread.queueTask(
mMediaDataArr.get(i).getmMediaType(),
i, mMediaDataArr.get(i).getmMediaId());
}
}
}
}
// This is triggered by thread 2 when it finishes decoding
@Override
public void onThumbnailLoaded(final int position, final Bitmap thumbnail) {
mResponseHandler.post(new Runnable() {
@Override
public void run() {
mCallback.onThumbnailPrepared(position, thumbnail);
}
});
}
2)ThumbnailLoaderThread.java
public void queueTask(int mediaType, int position, int videoId) {
mWorkerHandler.obtainMessage(mediaType, position, videoId).sendToTarget();
}
public void prepareHandler() {
mWorkerHandler = new Handler(getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
int type = msg.what;
final int position = msg.arg1;
int videoId = msg.arg2;
try {
if (type == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) {
Bitmap newThumb = MediaStore.Images.Thumbnails
.getThumbnail(mCr, videoId,
MediaStore.Images.Thumbnails.MINI_KIND, null);
postResult(position, newThumb);
} else if (type == MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO) {
Bitmap newThumb = MediaStore.Video.Thumbnails
.getThumbnail(mCr, videoId,
MediaStore.Video.Thumbnails.MINI_KIND, null);
postResult(position, newThumb);
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
});
}
private void postResult(final int position, final Bitmap newThumb) {
mResponseHandler.post(new Runnable() {
@Override
public void run() {
mCallback.onThumbnailLoaded(position, newThumb);
}
});
}
3) LibraryActivity.java
@Override
public void onThumbnailPrepared(int position, Bitmap thumbnail) {
if (thumbnail != null && position < mData.size()) {
MediaData updatedData = mData.get(position);
updatedData.setmThumbnail(thumbnail);
mData.set(position, updatedData);
mVideoAdapter.notifyItemChanged(position);
}
}
の流れはこのようなものです。
1)活性がスレッド1
2を開始する)スレッドファイルを照会1開始し、カーソルをループメディアIDを通過スレッド2を開始します。
3)スレッド2は、指定されたメディアIDでサムネイルをデコードします。
4)デコードが完了すると、スレッド2はスレッド1のコールバックを結果ビットマップでトリガします。
5)スレッド1はビットマップを受信し、コールバックを介してビットマップをアクティビティに配信します。
6)アクティビティはサムネイルを受け取り、指定されたビットマップでRecyclerViewデータを更新します。
これは問題なく動作しますが、システムがこのタスクに約50MBのメモリを割り当てると、サムネイルが100枚しか読み込まれていないことを考えると、かなり重いと思います。
:::私が試した何:::
1)私は、個々のサムネイルのURIを抽出し、それが結合したときrecyclerviewアダプタが指定されたURIで画像をロードするようにしましょう。それは正常に動作し、多くのメモリを消費しませんでしたが、アイテムがバインドされているときに画像を読み込むため、私は少し遅れて画面をスクロールするたびにサムネイルをリロードします。
2)私は、アダプターに直接サムネイルパスでサムネイルを読み込ませるようにしました。しかし、ユーザーが/.thumbnailsフォルダをクリーンアップしても機能しません。
3)スレッドがサムネイルをデコードするときにBitmapFactory.Optionsのサンプルサイズを4に設定しました。しかし、まだ重くて時には遅いときに...
4)MediaDataオブジェクトでは、サムネイルビットマップをメンバー変数として保持します。だから、アダプタをImageViewにロードした直後にnullにしました。まだ重いですし、オブジェクトのサムネイルをnullにしたので、スクロールしてもプレースホルダが表示されます。
私は本当に手掛かりがありません。どんな助けもありがとう!
ピカソを使ってみましたが、実際の画像が読み込まれました。サムネイルではありません。イメージファイル自体をロードするのは重すぎるのではないですか? :(あなたのレイアウトサイズ怒鳴るに応じて画像のサイズを変更することができ –
はコード Picasso.with(コンテキスト) .LOAD(URL) .resize(50、50) .centerCrop() .into(ImageViewの) – bhavikkumar
ですありがとう、次にどこにその行を追加する必要がありますか?アダプタのbindViewHolderメソッドに追加すると、スクロールするたびにイメージのサイズが変更されます。 –