0

How do I backup a SQLite Database to a SD Card? I have looked at many examples on here and none of them work for me. I have added the following lines to my manifest:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

and the following to a fragment with a textview to click on and perform the backup:

    // Find the Text View that displays the Backup Database Button //
    TextView backupDatabaseTextView = (TextView) rootview.findViewById(R.id.backup_database_text_view);

    // Set a click listener on the backup database text view //
    backupDatabaseTextView.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            exportDB();

        }
    });

and the following method:

private void exportDB() {
    try {
        File sd = Environment.getExternalStorageDirectory();
        File data = Environment.getDataDirectory();

        if (sd.canWrite()) {
            String currentDBPath = "//data//" + "com.budgettrackpro.android.budgettrackpro"
                    + "//databases//" + "budgettrackpro.db";
            String backupDBPath = "BudgetTrackPro";
            File currentDB = new File(data, currentDBPath);
            File backupDB = new File(sd, backupDBPath);

            FileChannel src = new FileInputStream(currentDB).getChannel();
            FileChannel dst = new FileOutputStream(backupDB).getChannel();
            dst.transferFrom(src, 0, src.size());
            src.close();
            dst.close();
            Toast.makeText(getActivity(), "Backup Successful!",
                    Toast.LENGTH_SHORT).show();

        }
    } catch (Exception e) {

        Toast.makeText(getActivity(), "Backup Failed!", Toast.LENGTH_SHORT)
                .show();

    }
}
Scott
  • 55
  • 5
  • Which android version you are running your app? – Abu Yousuf Aug 19 '18 at 08:44
  • Please explain in detail what "none of them work for me" means. Your code has a variety of bugs (hardcoded incorrect paths, no runtime permission support, not logging exceptions so you can see what went wrong, doing disk I/O on the main application thread, etc.). However, until you explain exactly what your problem is, it will be difficult for anyone to help you. – CommonsWare Aug 19 '18 at 10:52
  • Do the Android sqlite bindings support the [online backup](https://www.sqlite.org/backup.html) API? – Shawn Aug 19 '18 at 19:12
  • @ Abu, I am running Android version 27. @CommonsWare, I have tried the code from quite a few different questions here and they have not worked for me. The one you see here worked for others but not me. I don't get an error, just nothing happens. When I debug, it finds the sd card but then drops to the bottom of the try catch. – Scott Aug 19 '18 at 22:17
  • @ Shawn, I don't know. I read the link you posted and didn't see anything about Android. I am new to Android development though. – Scott Aug 19 '18 at 22:22
  • @Abu, sorry, I am running Android 8.1, API 27 – Scott Aug 19 '18 at 22:24

1 Answers1

0

When I debug, it finds the sd card but then drops to the bottom of the try catch

I do not know how to interpret that, as I do not know which lines in that code you think represent "finds the sd card". For example, your code has nothing to do with removable storage.

So, here are various issues, in descending order of likelihood of being the source of your current difficulty (though you should fix all of them):

No Runtime Permission Support

You are running on Android 8.1. Since Android 6.0, you need to request certain permissions at runtime, and WRITE_EXTERNAL_STORAGE is among those permissions.

Hardcoded Database Path

String currentDBPath = "//data//" + "com.budgettrackpro.android.budgettrackpro"
                + "//databases//" + "budgettrackpro.db";

Never hardcode paths. Replace that with:

File currentDB = getActivity().getDatabasePath("budgettrackpro.db");

(and get rid of the File currentDB = new File(data, currentDBPath); line, as you will no longer need it)

Not Logging the Exception

While you are developing an app, log all exceptions. Add this line above your Toast.makeText() line in your catch block:

Log.e(getClass().getSimpleName(), "Exception backing up database", e);

Then, use LogCat to examine the stack trace associated with any crashes.

Doing I/O on the Main Application Thread

At best, your UI will be frozen for the duration of the exportDB() call, and your users will think that your app is broken. At worst, you will crash, because either:

  • The backup is taking too long and tying up the main application thread, or
  • StrictMode complains about the disk I/O on the main application thread

Please move this work to a background thread.

Not Making the Backup Visible to the User

Even once you create the file, the user will not be able to see that it exists without rebooting or waiting a few hours.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thanks. At line "File sd = Environment.getExternalStorageDirectory();" in debug, the result is sd: /storage/emulated/0 – Scott Aug 20 '18 at 11:01
  • Thanks. At line "File sd = Environment.getExternalStorageDirectory();" in debug, the result is sd: /storage/emulated/0 and at "if (sd.canWrite()) {" the result is sd: /storage/emulated/0 in green. After that line it drops to the brace below the catch WRITE_EXTERNAL_STORAGE was already in my manifest file. I replaced the hardcoded path with File currentDB = getActivity().getDatabasePath("budgettrackpro.db"); as you stated. I added the log but since it didn't crash, I couldn't find it in Logcat. Nothing happened still. I rebooted the phone and still no backup. – Scott Aug 20 '18 at 11:06
  • @Scott: "WRITE_EXTERNAL_STORAGE was already in my manifest file" -- that is necessary but not sufficient. The concept of runtime permissions for Android 6.0+ are covered in modern books and courses on Android app development, plus [the documentation that I linked to in my answer](https://developer.android.com/training/permissions/requesting). – CommonsWare Aug 20 '18 at 11:14
  • Thank you very much! I read the documentation and viewed a few tutorials about requesting permissions and it worked after that. However, it copied the database to my internal storage... I will post the code in another comment. – Scott Aug 22 '18 at 11:12
  • Here is the code: private void exportDB() { try { File sd = Environment.getExternalStorageDirectory(); File data = Environment.getDataDirectory(); if (sd.canWrite()) { File currentDB = getActivity().getDatabasePath("budgettrackpro.db"); String backupDBPath = "BudgetTrackPro"; File backupDB = new File(sd, backupDBPath); FileChannel src = new FileInputStream(currentDB).getChannel(); – Scott Aug 22 '18 at 11:13
  • FileChannel dst = new FileOutputStream(backupDB).getChannel(); dst.transferFrom(src, 0, src.size()); src.close(); dst.close(); Toast.makeText(getActivity(), "Backup Successful!", Toast.LENGTH_SHORT).show(); } } catch (Exception e) { Log.e(getClass().getSimpleName(), "Exception backing up database", e); Toast.makeText(getActivity(), "Backup Failed!", Toast.LENGTH_SHORT) .show(); – Scott Aug 22 '18 at 11:15
  • @Scott: That writes the file to [external storage](https://commonsware.com/blog/2017/11/14/storage-situation-external-storage.html), not [internal storage](https://commonsware.com/blog/2017/11/13/storage-situation-internal-storage.html), from the standpoint of the Android SDK. – CommonsWare Aug 22 '18 at 11:16
  • Thanks, I understand now that I should have specified that I need to backup to my SD card, not just external storage. What do I need to change to direct it to the SD card specifically? – Scott Aug 23 '18 at 05:30
  • @Scott: The simplest solution is to use `getExternalFilesDirs()` on `Context`. If that method returns 2+ `File` objects, the second and subsequent ones are locations on [removable storage](https://commonsware.com/blog/2017/11/15/storage-situation-removable-storage.html) that your app has read/write access to, no permissions required. – CommonsWare Aug 23 '18 at 10:45
  • Thank you. I like your page as well and will subscribe soon :) I deleted File sd = Environment.getExternalStorageDirectory(); and used File[] sd = getActivity().getExternalFilesDirs("BudgetTrackPro Backup"); This provided me the two files but if(sd.canwrite()) no longer works. Can you provide an example of how to properly use getExternalFilesDirs? I can't find any good examples even in the Android Developers documentation – Scott Aug 24 '18 at 00:31
  • @Scott: `getActivity().getExternalFilesDirs("BudgetTrackPro Backup")` -- that is not a valid value to pass to `getExternalFilesDirs()`. Try `null`. See [the documentation for `getExternalFilesDirs()`](https://developer.android.com/reference/android/content/Context.html#getExternalFilesDirs(java.lang.String)). – CommonsWare Aug 24 '18 at 10:46
  • Thanks, but that is not the problem or at least the only problem. getExternalFilesDirs requires a File[] type, and the sd.canwrite() needs a File type, so it says it can't resolve the method canWrite(). I read that documentation earlier today among other stuff and nothing worked. I even tried Environment.MEDIA_MOUNTED without any luck – Scott Aug 24 '18 at 12:17
  • @Scott: "getExternalFilesDirs requires a File[] type" -- it returns a `File[]` type. As I wrote, if it returns 2+ `File` objects, the second and subsequent ones are on removable storage. [Here is part of a Wikibook on how to work with Java arrays](https://en.wikibooks.org/wiki/Java_Programming/Arrays), and there are many other Java books and courses available for you to consider. – CommonsWare Aug 24 '18 at 12:38
  • Thank you, I figured it out! I changed sd.canwrite() to sd[1].canwrite and it works now! – Scott Aug 25 '18 at 00:23