i have following problem:
our android app uses leveldb write files device's external storage. leveldb internally uses mmap write. our problem far occurs on samsung galaxy s4. files written , read storage without problem. after restart of device, file corrupted.
did experience similar?
i wrote small demo app check if mmap problem , in fact seems be. demo app displays image ships app , button below image.
if button pressed
- the image written external storage using
filechannel.map()(as equivalentmmap) - the image read external storage , displayed below button.
after button pushed once , image written external storage, app shows 2 copies of image. works after restarting app. after reboot of galaxy s4 however, file on external storage corrupted , first image shown.
note: problem doesn't occur when file gets written using fileoutputstream , occurs on galaxy s4.
it great if knows how circumvent problem using leveldb.
make easier reproduce problem, here code of demo app:
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 included in 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 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 read 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)) { // can read , write media try { // convert resource 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 png*/, bos); byte[] bitmapdata = bos.tobytearray(); bos.close(); file dir = getapplicationcontext().getexternalcachedir(); // write bytes 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)) { // can read , write media try { // create drawable png file on external storage file dir = getapplicationcontext().getexternalcachedir(); string pathname = dir.getpath() + "/test_image_s.png"; drawable d = drawable.createfrompath(pathname); // display 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"); } } }
one of collegues found solution circumvent problem on s4. realized external storage on device emulated. internally, uses internal storage. can use internal storage directly.
fortunately - starting api level 11 - there sdk call find out whether or not storage emulated: isexternalstorageemulated()
i think it's clean solution not targeted single device only. whenever external storage emulated, use internal storage.
however still think there galaxy s4 specific problem emulated external storage if data written via mmap(2).
Comments
Post a Comment