环境描述:Android版本:6.0.1,DB由客户创建并存储在外部存储中,完成任务后将收集Sqlite DB的数据。
我们需要根据不同的服务逻辑读取和写入数据库。
问题:
Could not open the database in read/write mode.
java.lang.RuntimeException: Failure delivering result ResultInfo{who=@android:requestPermissions:, request=10, result=-1, data=Intent { act=android.content.pm.action.REQUEST_PERMISSIONS (has extras) }} to activity {com.example.xx.sqlitedemo/com.example.xx.sqlitedemo.MainActivity}:
android.database.sqlite.SQLiteException: not an error (code 0): Could not open the database in read/write mode.
代码 :
SQLiteDatabase.openOrCreateDatabase
请求权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
在调试模式下通过以下代码检查 db 文件是否存在:
new File(dbpath).exists();
数据库路径是:
"/storage/4B53-98D9/System/System.db"
动态权限已经获得,问题依然存在。
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.xx.sqlitedemo">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.java
package com.example.xx.sqlitedemo;
import android.Manifest;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.DatabaseErrorHandler;
import android.database.DefaultDatabaseErrorHandler;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteCursorDriver;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabaseCorruptException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQuery;
import android.os.Build;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import java.io.File;
public class MainActivity extends AppCompatActivity {
private static final int EXTERNAL_STORAGE_REQ_CODE = 10;
private static final int MOUNT_UNMOUNT_FILESYSTEMS = 20;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// requestMountPermission();
requestStoragePermission();
int o = 0;
}
private void requestMountPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)) {
Toast.makeText(this, "please give me the permission", Toast.LENGTH_SHORT).show();
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS},
MOUNT_UNMOUNT_FILESYSTEMS);
}
} else {
requestStoragePermission();
int i = 1;
}
}
public void requestStoragePermission() {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(this, "please give me the permission", Toast.LENGTH_SHORT).show();
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
EXTERNAL_STORAGE_REQ_CODE);
}
} else {
openSQLiteDataBase();
int i = 1;
}
}
private void openSQLiteDataBase() {
// SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase("/storage/4B53-98D9/System/System.db", new CustomCursorFactory(), new CustomDbErrorHandler());
DatabaseHelper helper = new DatabaseHelper(this,"System.db");
SQLiteDatabase database = helper.getReadableDatabase();
int ii = 0;
Cursor cursor = database.query(null,null,null,null,null,null,null,null);
database.close();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case EXTERNAL_STORAGE_REQ_CODE:
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openSQLiteDataBase();
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
break;
case MOUNT_UNMOUNT_FILESYSTEMS:
break;
}
}
class CustomCursorFactory implements SQLiteDatabase.CursorFactory {
@Override
public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, String editTable, SQLiteQuery query) {
if (Build.VERSION.SDK_INT < 11) {
return new SQLiteCursor(db, masterQuery, editTable, query);
} else {
return new SQLiteCursor(masterQuery, editTable, query);
}
}
}
class CustomDbErrorHandler implements DatabaseErrorHandler {
DefaultDatabaseErrorHandler __d = new DefaultDatabaseErrorHandler();
boolean __once = false;
@Override
public void onCorruption(SQLiteDatabase dbObj) {
if (__once) {
throw new SQLiteDatabaseCorruptException("db corrupted and cannot be recovered");
}
__once = true;
__d.onCorruption(dbObj);
}
}
class DatabaseContext extends ContextWrapper {
private static final String DEBUG_CONTEXT = "DatabaseContext";
public DatabaseContext(Context base) {
super(base);
}
@Override
public File getDatabasePath(String name) {
File sdcard = Environment.getExternalStorageDirectory();
String path = this.getFilesDir().getAbsolutePath();
String dbfile = "/storage/4B53-98D9/System/System.db"; //sdcard.getAbsolutePath() + File.separator+ "databases" + File.separator + name;
if (!dbfile.endsWith(".db")) {
dbfile += ".db" ;
}
File result = new File(dbfile);
if (!result.getParentFile().exists()) {
result.getParentFile().mkdirs();
}
return result;
}
/* this version is called for android devices >= api-11. thank to @damccull for fixing this. */
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
return openOrCreateDatabase(name,mode, factory);
}
/* this version is called for android devices < api-11 */
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);
// SQLiteDatabase result = super.openOrCreateDatabase(name, mode, factory);
Log.w(DEBUG_CONTEXT, "openOrCreateDatabase(" + name + ",,) = " + result.getPath());
return result;
}
}
public class DatabaseHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
DatabaseHelper(final Context context, String databaseName) {
super(new DatabaseContext(context), databaseName, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}
更新于 2017-03-05: @greenapps 提供的方式很危险:
Have a look at getExternalFilesDirs(). Look at the second item returned.
That would then look like "/storage/4B53-98D9/Android/data/<package>/files/System.db".
卸载应用程序时,数据同时被删除。
/storage/4B53-98D9/Android/data/<package> was deleted.
谁能回答我的问题?
提前致谢!