2015-03-31 37 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> 
相关问题