0

次の問題があります。

私たちの 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");
        }
    }
}
4

1 に答える 1

0

私の同僚の 1 人が、S4 の問題を回避する解決策を見つけました。彼は、このデバイスの外部ストレージが実際にエミュレートされていることに気付きました。内部的には、内部ストレージを使用します。したがって、内部ストレージを直接使用することもできます。

幸いなことに、API レベル 11 以降では、ストレージがエミュレートされているかどうかを確認するための SDK 呼び出しがあります。isExternalStorageEmulated()

単一のデバイスのみを対象としていないため、クリーンなソリューションだと思います。外部ストレージがエミュレートされるときはいつでも、内部ストレージを使用してください。

ただし、データが mmap(2) 経由で書き込まれる場合、エミュレートされた外部ストレージに Galaxy S4 固有の問題があると考えられます。

于 2013-07-23T11:43:17.670 に答える