1

I am attempting to copy a pre-loaded DB from my assets folder to somewhere I can read from it, however I am getting an IOException when my code tries to call the InputStream to get the DB from the assets folder.

I have been following the tutorials on Reign Design as well as this excellent answer from SO: https://stackoverflow.com/a/9109728/3699923. My question is extremely similar to Error copying SQLite DB from Asset folder, however he was getting a file not found error, and mine is a more generic "error copying file".

I have also tried deleting the app from my device and testing again to ensure that it wasn't related to the new file being already on device.

My DBHelper file (this is an excerpt):

public class AppDataDBHelper extends SQLiteOpenHelper {

private static String TAG = "AppDataDBHelper"; // Tag just for the LogCat window
private static String DB_NAME ="appdata.db";// Database name
private static String DB_PATH = "";
private static String DB_TABLE = "Bonus_Data";
private static int DB_VERSION = 1;

private SQLiteDatabase mDataBase;
private final Context mContext;

public AppDataDBHelper(Context context) {
    super(context, DB_NAME, null,DB_VERSION);
    this.mContext = context;
    DB_PATH = context.getAssets() + "/databases/";
    // DB_PATH = context.getApplicationInfo().dataDir + "/databases/";
    Log.e(TAG,DB_PATH + DB_NAME);
}

public void createDataBase() throws IOException
{
    //If the database does not exist, copy it from the assets.

    Log.e(TAG,"Entered createDatabase");
    boolean mDataBaseExist = checkDataBase();
    if(!mDataBaseExist)
    {
        this.getReadableDatabase();
        this.close();
        try
        {
            //Copy the database from assets
            Log.e(TAG,"createDatabase passing to copyDatabase");
            copyDataBase();
            Log.e(TAG, "createDatabase database created");
        }
        catch (IOException mIOException)
        {
            throw new Error("ErrorCopyingDataBase");
        }
    }
}

//Check that the database exists here: /data/data/your package/databases/Da Name
private boolean checkDataBase()
{
    Log.e(TAG,"entered checkDatabase");
    File dbFile = new File(DB_PATH + DB_NAME);
    //Log.v("dbFile", dbFile + "   "+ dbFile.exists());
    return dbFile.exists();
}

//Copy the database from assets
private void copyDataBase() throws IOException
{
    Log.e(TAG,"entered copyDatabase");
    close();
    Log.e(TAG,"copyDatabase close");
    InputStream mInput = mContext.getAssets().open(DB_NAME);
    Log.e(TAG,"copyDatabase inputStream");
    String outFileName = DB_PATH + DB_NAME;
    Log.e(TAG,DB_PATH + " | " + DB_NAME);
    OutputStream mOutput = new FileOutputStream(outFileName);
    Log.e(TAG,"copyDatabase outputStream");

    FileHelper.copyFile(mInput, mOutput);
    getWritableDatabase().close();
}

The call from my relevant activity file:

// Handle the appData DB.
    SQLiteDatabase appDB = null;
    appDBHelper = new AppDataDBHelper(this);

    try {
        Log.e(TAG,"Calling createDataBase");
        appDBHelper.createDataBase();

    } catch (IOException ioe) {
        throw new Error("Unable to create database");
    }

    try {
        Log.e(TAG,"Calling openDatabase");
        appDBHelper.openDataBase();
    } catch(SQLException sqle){
        throw sqle;
    }

And the error I'm seeing in logcat:

2019-02-02 22:40:29.103 603-603/net.tommyc.android.tourofhonor E/splashScreen: Going to Bonus List
2019-02-02 22:40:29.170 603-603/net.tommyc.android.tourofhonor D/ScrollView: initGoToTop
2019-02-02 22:40:29.195 603-603/net.tommyc.android.tourofhonor E/AppDataDBHelper: android.content.res.AssetManager@9353a47/databases/appdata.db
2019-02-02 22:40:29.195 603-603/net.tommyc.android.tourofhonor E/bonusListing: Calling createDataBase
2019-02-02 22:40:29.195 603-603/net.tommyc.android.tourofhonor E/AppDataDBHelper: Entered createDatabase
2019-02-02 22:40:29.195 603-603/net.tommyc.android.tourofhonor E/AppDataDBHelper: entered checkDatabase
2019-02-02 22:40:29.224 603-603/net.tommyc.android.tourofhonor E/AppDataDBHelper: createDatabase passing to copyDatabase
2019-02-02 22:40:29.224 603-603/net.tommyc.android.tourofhonor E/AppDataDBHelper: entered copyDatabase
2019-02-02 22:40:29.224 603-603/net.tommyc.android.tourofhonor E/AppDataDBHelper: copyDatabase close
2019-02-02 22:40:29.224 603-603/net.tommyc.android.tourofhonor D/AndroidRuntime: Shutting down VM
2019-02-02 22:40:29.225 603-603/net.tommyc.android.tourofhonor E/AndroidRuntime: FATAL EXCEPTION: main
Process: net.tommyc.android.tourofhonor, PID: 603
java.lang.Error: ErrorCopyingDataBase
    at net.tommyc.android.tourofhonor.AppDataDBHelper.createDataBase(AppDataDBHelper.java:57)
    at net.tommyc.android.tourofhonor.bonusListing.onCreate(bonusListing.java:53)
    at android.app.Activity.performCreate(Activity.java:7183)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2908)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3030)
    at android.app.ActivityThread.-wrap11(Unknown Source:0)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
    at android.os.Handler.dispatchMessage(Handler.java:105)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6938)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
Ujjwal Jung Thapa
  • 604
  • 2
  • 8
  • 31
DJFriar
  • 340
  • 7
  • 21

1 Answers1

2

[update: tested ok codes] I have updated the code from copying database to reading a group of names from database table.

For accessing external sqlite database stored in assets folder, this is working for me so please check it out.

data inserted into external sqlite database but not saving in android studio

For your case, I recommend following changes:

1) my table in sqlite dbbrowser:

CREATE TABLE `Bonus_Data` (
`id`    INTEGER NOT NULL,
`name`  TEXT NOT NULL,
PRIMARY KEY(`id`)
);

2) in your AppDataDBHelper class:

public class AppDataDBHelper extends SQLiteOpenHelper{
private static String TAG = "AppDataDBHelper"; // Tag just for the LogCat window
private static String DB_NAME = "appdata.db";// Database name
private static String DB_TABLE = "Bonus_Data";
private static int DB_VERSION = 1;

private SQLiteDatabase mDataBase;
private final Context mContext;

public AppDataDBHelper(Context context) {
    super(context, DB_NAME, null, DB_VERSION);
    this.mContext = context;
}

@Override
public void onCreate(SQLiteDatabase db) {

}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

}

public void createDataBase() throws IOException {
    //If the database does not exist, copy it from the assets.

    Log.e(TAG, "Entered createDatabase");
    boolean mDataBaseExist = checkDataBase();
    if (!mDataBaseExist) {
        this.getReadableDatabase();
        this.close();
        try {
            //Copy the database from assets
            Log.e(TAG, "createDatabase passing to copyDatabase");
            copyDataBase();
            Log.e(TAG, "createDatabase database created");
        } catch (IOException mIOException) {
            throw new Error("ErrorCopyingDataBase");
        }
    }
}

//Check that the database exists here: /data/data/your package/databases/Da Name
private boolean checkDataBase() {
    Log.e(TAG, "entered checkDatabase");
    File dbFile = new File(mContext.getDatabasePath(DB_NAME).getPath());
    if (dbFile.exists()) return true;
    File dbdir = dbFile.getParentFile();
    if (!dbdir.exists()) {
        dbdir.mkdirs();
    }
    return false;
}

//Copy the database from assets
private void copyDataBase() throws IOException {
    InputStream mInput = null;
    OutputStream mOutput = null;
    Log.e(TAG, "entered copyDatabase");
    Log.e(TAG, "copyDatabase close");
    String outFileName = mContext.getDatabasePath(DB_NAME).getPath();

    try {
        mInput = mContext.getAssets().open(DB_NAME);
        mOutput = new FileOutputStream(outFileName);

        byte[] buffer = new byte[1024];
        int length;
        while ((length = mInput.read(buffer)) > 0) {
            mOutput.write(buffer, 0, length);
        }
        mOutput.flush();
        mOutput.close();
        mInput.close();
    } catch (IOException ie) {
        throw new Error("Copydatabase() error ");
    }
}

public ArrayList<String> readNames() {
    boolean distinct = true;

    ArrayList<String> mname = new ArrayList<>();

    String[] columns = new String[]{"name"};
    //Cursor cursor = ourDatabase.query(DATABASE_TABLE, columns, null, null, null, null, null);
    //This is used to draw distinct data values from database table
    Cursor cursor = mDataBase.query(distinct, DB_TABLE, columns, null, null, null, null, null, null);
    int iName = cursor.getColumnIndex("name");

    for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
        mname.add(cursor.getString(iName));
    }

    cursor.close();
    return mname;
}
//this openRead is needed everytime you access this helper class 
public void openRead() {
    mDataBase = getReadableDatabase(); //mDatabase should always be initialized or else app crashes while accessing TABLE
}
}

2) finally in the onCreate() method of your activity create database as:

public class TestActivity extends AppCompatActivity {

private static final String TAG = "TestActivity";
private ArrayList<String> name = new ArrayList<>();
private TextView textView_name;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_test);

    AppDataDBHelper appDBHelper = new AppDataDBHelper(this);
    try {
        Log.e(TAG, "Calling createDataBase");
        appDBHelper.createDataBase();
    } catch (IOException ioe) {
        throw new Error("Unable to CREATE DATABASE");
    } finally {
        appDBHelper.close();
    }

    textView_name = findViewById(R.id.textView_name);

    check();
    String message = "";
    for (int i=0; i<name.size(); i++) {
        message = message + name.get(i) + "\n";
    }
    textView_name.setText(message);
}

private void check() {
    try {
        AppDataDBHelper appDataDBHelper = new AppDataDBHelper(this);
        appDataDBHelper.openRead();   //without this app crashes
        name = appDataDBHelper.readNames();
        appDataDBHelper.close();
    } catch (Exception e) {
        throw new Error("error reading");
    }
}
}
Ujjwal Jung Thapa
  • 604
  • 2
  • 8
  • 31
  • That got rid of my file copy error, though I'm still getting a `E/SQLiteLog: (1) no such table: Bonus_Data` when it tries to read the DB. Do you know if that error means it did successfully get the DB copied, or if that could stem from a file not found type of issue as well? – DJFriar Feb 03 '19 at 08:41
  • clear the data of your app in settings then go to >> device file explorer in android studio >> go to data >> data >> your package name. at first it will be empty. Open your app and again refresh device explorer and see there. If your database is there it has been copied. – Ujjwal Jung Thapa Feb 03 '19 at 08:52
  • So it is making a DB file there, but the size is way too small, so I think its an empty DB, hence why I'm getting the "table not found" error. The logcat shows `E/AppDataDBHelper: Entered createDatabase`, then `E/AppDataDBHelper: entered checkDatabase`, then it goes straight into trying to run the initial `select * from Bonus_Data` and that is where it fails. It looks like it enters `checkDatabase` but doesn't seem to be hitting either if statement in there (I added additional log statements to check for this). – DJFriar Feb 03 '19 at 09:08
  • try twitching your manifest file to read and write external storage permissions – Ujjwal Jung Thapa Feb 03 '19 at 09:16
  • Also try creating new database in your sqlite db browser and add it to your assets folder and check it. I tried that code and it seems like working for me since it copied the database. – Ujjwal Jung Thapa Feb 03 '19 at 09:19
  • The app has write permissions (I save images from the camera to the public area so I know its working). I double-checked the DB, its 885K. The file that is being created in the data\data\ path is 12K. – DJFriar Feb 03 '19 at 09:56
  • i have updated my code answer above. It is final and working for me. Checked and working. I hope it will be helpful for you. Good luck! – Ujjwal Jung Thapa Feb 03 '19 at 10:35
  • That was the issue @MikeT. Moving it fixed it. – DJFriar Feb 03 '19 at 11:02