2013-07-19 37 views
0

我有以下問題:重啓破壞()寫入銀河S4的外部存儲與FileChannel.map文件

我們的Android應用程序使用性LevelDB將文件寫入到設備的外部存儲。 LevelDB內部使用mmap來編寫。到目前爲止,我們的問題只發生在三星Galaxy S4的上。文件可以毫無問題地寫入和讀取存儲。但是在設備重新啓動後,該文件已損壞。

有沒有人體驗過類似的東西?

我寫了一個小的演示應用程序來檢查mmap是否是問題,實際上它似乎是。演示應用程序會顯示隨應用程序附帶的圖像和圖像下方的按鈕。
如果按鈕被按壓

  • 該圖像是使用FileChannel.map()(等同於mmap
  • 圖像被從外部存儲器讀取,並且按鈕下方顯示寫入到外部存儲設備。

將按鈕按下一次並將圖像寫入外部存儲器後,該應用程序會顯示圖像的兩個副本。即使在重新啓動應用程序後,這也可以工作然而,Galaxy S4重新啓動後,外部存儲器上的文件已損壞,只顯示第一張圖像。

注意:當使用FileOutputStream寫入文件並且僅在Galaxy S4上發生此問題時,不會發生此問題。

如果有人知道如何使用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"); 
     } 
    } 
} 

回答

0

我的一個同事找到了解決S4問題的解決方案。他意識到這個設備上的外部存儲實際上是模擬的。在內部,它使用內部存儲。所以我們也可以直接使用內部存儲。

幸運的 - 從API級別11開始 - 有一個SDK調用來找出存儲是否被效仿:isExternalStorageEmulated()

我認爲這是因爲它的目標不是僅僅在單個設備中的清潔解決方案。每當模擬外部存儲器時,只需使用內部存儲器即可。

但是,如果數據是通過mmap(2)寫入的,我們仍然認爲存在Galaxy S4特定的模擬外部存儲問題。