2015-03-31 36 views
1

我想允許我的Android應用程序的用戶導出SQLite數據庫文件來創建它們的內容。我當前的解決方案將文件複製到私有存儲(/data/data/com.package.name/files/Content.db),然後爲該文件創建一個URI並打開共享對話框。這是行得通的,例如,允許我使用Dropbox導出數據庫文件。下面是我使用的代碼,從https://stackoverflow.com/a/2661882部分調整 -從Android應用程序共享SQLite數據庫,無中間副本

private void exportContent() { 
    copyContentToPrivateStorage(); 

    Intent intent = new Intent(Intent.ACTION_SEND); 
    intent.setType("application/octet-stream"); 

    Uri uri = new FileProvider().getDatabaseURI(this); 

    intent.putExtra(Intent.EXTRA_STREAM, uri); 
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 

    startActivity(Intent.createChooser(intent, "Backup via:")); 
} 

private void copyContentToPrivateStorage() { 
    // From https://stackoverflow.com/a/2661882 
    try { 
     File data = Environment.getDataDirectory(); 
     File sd = getFilesDir(); 

     if (sd.canWrite()) { 
      String currentDBPath = "//data//com.package.name//databases//Content.db"; 
      String backupDBPath = "Content.db"; 
      File currentDB = new File(data, currentDBPath); 
      File backupDB = new File(sd, backupDBPath); 

      if (currentDB.exists()) { 
       FileChannel src = new FileInputStream(currentDB).getChannel(); 
       FileChannel dst = new FileOutputStream(backupDB).getChannel(); 
       dst.transferFrom(src, 0, src.size()); 
       src.close(); 
       dst.close(); 
      } 
     } 
    } catch (Exception e) { 
     Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show(); 
    } 
} 

public class FileProvider extends android.support.v4.content.FileProvider { 

    public Uri getDatabaseURI(Context c) { 
     File exportFile = new File(c.getFilesDir(), "Content.db"); 
     Uri uri = getUriForFile(c, "com.package.name.fileprovider", exportFile); 

     c.grantUriPermission("*", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 

     return uri; 
    } 
} 

好像我應該能夠直接從現有的數據庫路徑中創建一個URI,而不是做一箇中間副本。有沒有辦法做到這一點?

我可以繼續進行中間複製,但我認爲將數據庫的第二個副本保留在數據目錄中的時間超過必要的時間是個不好的做法。有沒有一種方法可以在選擇的應用程序使用URI共享文件後將其清除並刪除?

+0

在你的應用程序之間共享數據,是你的期望? – 2015-03-31 18:44:16

+0

這聽起來像一個非常糟糕的主意。您應該使用Contentproviders。 – wvdz 2015-03-31 18:45:04

+0

如果我只是想讓另一個應用程序訪問某些數據,ContentProviders會很棒。在這種情況下,我想發送實際的SQLite數據庫文件。這主要是爲了將數據備份和導出到另一臺設備。我最終也可能會選擇雲解決方案,但我也希望允許用戶將自己的數據庫文件副本備份到其個人存儲上。 – Patrick 2015-04-01 18:30:19

回答

1

我解決了這個在我自己的。根據Neil的要求,我在這裏記錄它。

這是我從我的活動啓動導出/備份:

public class MyActivity { 

    private void exportUserContent() { 
     Intent intent = new Intent(Intent.ACTION_SEND); 
     intent.setType("application/octet-stream"); 

     Uri uri = new FileProvider().getDatabaseURI(this); 

     intent.putExtra(Intent.EXTRA_STREAM, uri); 

     startActivity(Intent.createChooser(intent, "Backup via:")); 
    } 

} 

的FileProvider:

public class FileProvider extends android.support.v4.content.FileProvider { 

    public Uri getDatabaseURI(Context c) { 
     // https://developer.android.com/reference/android/support/v4/content/FileProvider.html 

     File data = Environment.getDataDirectory(); 
     String dbName = "UserContent.db"; 
     String currentDBPath = "//data//com.url.myapp//databases//" + dbName; 

     File exportFile = new File(data, currentDBPath); 

     return getFileUri(c, exportFile); 
    } 

    public Uri getFileUri(Context c, File f){ 
     return getUriForFile(c, "com.url.myapp.fileprovider", f); 
    } 

} 

內的AndroidManifest.xml:

<manifest ...> 
    <application ...> 
     <provider 
      android:name="android.support.v4.content.FileProvider" 
      android:authorities="com.url.myapp.fileprovider" 
      android:exported="false" 
      android:grantUriPermissions="true"> 
      <meta-data 
       android:name="android.support.FILE_PROVIDER_PATHS" 
       android:resource="@xml/filepaths" /> 
     </provider> 

裏面\程序\ src \ main \ res \ xml \ filepaths.xml (我認爲第一項是相關的,但是我將包括整個文件的完整性):

<paths> 
    <files-path 
     path="../databases/" 
     name="mydatabases"/> 

    <files-path 
     path="" 
     name="migrations"/> 

    <external-path 
     path="" 
     name="external"/> 
</paths> 
0

以下是我解決了這個帶有自定義ContentProvider

import android.content.ContentProvider; 
import android.content.ContentValues; 
import android.database.Cursor; 
import android.database.MatrixCursor; 
import android.net.Uri; 
import android.os.ParcelFileDescriptor; 
import android.provider.OpenableColumns; 

import java.io.File; 
import java.io.FileNotFoundException; 

/** 
* ContentProvider to share SQLite database 
* 
* Credit: https://android.googlesource.com/platform/frameworks/support/+/android-support-lib-19.1.0/v4/java/android/support/v4/content/FileProvider.java 
*/ 
public class MyProvider extends ContentProvider { 

    private final File file = new File("/data/data/com.example.provider/databases", "mydatabase.db"); 

    @Override 
    public boolean onCreate() { 
     return true; 
    } 

    @Override 
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 
     if (projection == null) { 
      projection = new String[] { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE }; 
     } 

     String[] cols = new String[projection.length]; 
     Object[] values = new Object[projection.length]; 
     int i = 0; 
     for (String col : projection) { 
      if (OpenableColumns.DISPLAY_NAME.equals(col)) { 
       cols[i] = OpenableColumns.DISPLAY_NAME; 
       values[i++] = file.getName(); 
      } else if (OpenableColumns.SIZE.equals(col)) { 
       cols[i] = OpenableColumns.SIZE; 
       values[i++] = file.length(); 
      } 
     } 
     cols = copyOf(cols, i); 
     values = copyOf(values, i); 
     final MatrixCursor cursor = new MatrixCursor(cols, 1); 
     cursor.addRow(values); 
     return cursor; 
    } 

    @Override 
    public String getType(Uri uri) { 
     return "application/octet-stream"; 
    } 

    @Override 
    public Uri insert(Uri uri, ContentValues values) { 
     throw new UnsupportedOperationException("No external inserts"); 
    } 

    @Override 
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 
     throw new UnsupportedOperationException("No external updates"); 
    } 

    @Override 
    public int delete(Uri uri, String selection, String[] selectionArgs) { 
     return 0; 
    } 

    @Override 
    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 
     return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); 
    } 

    private static String[] copyOf(String[] original, int newLength) { 
     final String[] result = new String[newLength]; 
     System.arraycopy(original, 0, result, 0, newLength); 
     return result; 
    } 

    private static Object[] copyOf(Object[] original, int newLength) { 
     final Object[] result = new Object[newLength]; 
     System.arraycopy(original, 0, result, 0, newLength); 
     return result; 
    } 
} 

然後在清單:

<provider 
    android:name="com.example.appname.MyProvider" 
    android:authorities="com.example.provider"> 
</provider> 
相關問題