我可以编写存储在外部SDCard上的SQLite DB吗? SOS

时间:2017-03-09 11:04:19

标签: android sqlite android-sdcard android-external-storage android-6.0.1-marshmallow

Env desc:   Android版本:6.0.1,数据库由客户创建并存储在外部存储,任务完成后将收集Sqlite数据库。

我们需要根据不同的服务逻辑读取和写入数据库。

问题:

 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();

dbpath是:

"/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.

任何人都可以回答我的问题吗?

提前致谢!

2 个答案:

答案 0 :(得分:0)

要申请许可,请尝试以下代码..

//check for WRITE_EXTERNAL_STORAGE permission
        int hasAccessExternalStoragePermission = ContextCompat.checkSelfPermission(LoginActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE);

    if (hasAccessExternalStoragePermission != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(LoginActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_CODE_ASK_PERMISSIONS);
    }

并按照以下方式处理..

@Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE_ASK_PERMISSIONS:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.e(TAG, "Permission Granted....");
                    createFile();
                }else{
                     Log.e(TAG, "Permission Not Granted....");
                }
        }
    }

我想问题就出现了,因为你正在使用shouldShowRequestPermissionRationale这种方法。如果使用此方法here,请阅读用法。

答案 1 :(得分:0)

SD卡是只读的,因此您会收到无法在写入模式下打开的消息。

您可以尝试将db文件放在应用程序可以写入的SD卡上的特定于应用程序的目录中。

看看getExternalFilesDirs()。看看返回的第二个项目。

那就像"/storage/4B53-98D9/Android/data/<package>/files/System.db"