次の問題があります。
私たちの Android アプリは、LevelDBを使用してデバイスの外部ストレージにファイルを書き込みます。LevelDBは内部的mmap
に書き込みに使用します。これまでのところ、問題はSamsung Galaxy S4でのみ発生します。ファイルは問題なくストレージに読み書きされます。しかし、デバイスの再起動後、ファイルが破損しています。
誰かが似たようなことを経験しましたか?
問題があるかどうかを確認するために小さなデモアプリを作成しmmap
ましたが、実際には問題があるようです。デモ アプリには、アプリに同梱されている画像と、画像の下にあるボタンが表示されます。
ボタンが押された場合
FileChannel.map()
イメージは(と同等mmap
)を使用して外部ストレージに書き込まれます。- 画像は外部ストレージから読み込まれ、ボタンの下に表示されます。
ボタンが 1 回押され、画像が外部ストレージに書き込まれた後、アプリは画像の 2 つのコピーを表示します。これは、アプリを再起動した後でも機能します。ただし、Galaxy S4 の再起動後、外部ストレージのファイルが破損し、最初の画像のみが表示されます。
注: この問題は、ファイルが を使用して書き込まれた場合には発生せず、 Galaxy S4FileOutputStream
でのみ発生します。
LevelDBを使用してこの問題を回避する方法を誰かが知っていれば、それは素晴らしいことです。
問題を簡単に再現できるように、デモ アプリのコードを次に示します。
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAlignment="center"
android:layout_gravity="center_horizontal"
android:text="Original image as included in the app:" />
<ImageView
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_margin="10dp"
android:src="@drawable/test_image"
android:layout_gravity="center_horizontal" />
<Button
android:layout_width="300dp"
android:layout_height="44dp"
android:layout_gravity="center_horizontal"
android:text="Write image file to storage"
android:onClick="writeFile" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAlignment="center"
android:layout_gravity="center_horizontal"
android:text="Image as read from storage:" />
<ImageView
android:id="@+id/storageImage"
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal" />
</LinearLayout>
StartActivity.java
package net.skoobe.StorageWrite;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class StartActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
retrieveImage();
}
public void writeFile(View v) {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// we can read and write the media
try {
// convert resource to Bitmap
BitmapDrawable bm = ((BitmapDrawable)getResources().getDrawable(R.drawable.test_image));
Bitmap b = bm.getBitmap();
// store bitmap data in byte array
ByteArrayOutputStream bos = new ByteArrayOutputStream();
b.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos);
byte[] bitmapdata = bos.toByteArray();
bos.close();
File dir = getApplicationContext().getExternalCacheDir();
// write bytes to external storage
FileChannel readWriteChannel = new RandomAccessFile(dir.getPath() + "/test_image_s.png", "rw").getChannel();
ByteBuffer readWriteBuf = readWriteChannel.map(FileChannel.MapMode.READ_WRITE, 0, bitmapdata.length);
readWriteBuf.put(bitmapdata);
readWriteChannel.close();
} catch (Exception e) {
Log.e("StorageWrite", e.toString());
}
retrieveImage();
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
Log.e("StorageWrite", "storage not writable");
} else {
Log.e("StorageWrite", "storage neither writable nor readable");
}
}
private void retrieveImage() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// we can read and write the media
try {
// create Drawable from PNG file on external storage
File dir = getApplicationContext().getExternalCacheDir();
String pathName = dir.getPath() + "/test_image_s.png";
Drawable d = Drawable.createFromPath(pathName);
// display the image
((ImageView)findViewById(R.id.storageImage)).setImageDrawable(d);
} catch (Exception e) {
Log.e("StorageWrite", e.toString());
}
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
Log.e("StorageWrite", "storage not writable");
} else {
Log.e("StorageWrite", "storage neither writable nor readable");
}
}
}