0

Im creating a dictionary App using Android Studio. I'm trying to use an SQlite database.

I cant launch it on emulator because I have 2 problems.

  1. First, I don't really get how to create directory, I code it like this.."mydirectory" shows red, and I don't understand how to fix it

:-

 public DBHelper(Context context) {
 super(context, DATABASE_NAME, null, DATABASE_VERSION);
 mContext = context;
    DATABASE_LOCATION = "data/data/" + mContext.getPackageName() + 
    "/database/";
    DATABASE_FULL_PATH = DATABASE_LOCATION + DATABASE_NAME;
    if (!isExistingfDB()) {
        try {
           File dbLocation = new File (DATABASE_LOCATION);
           dbLocation = mydirectory();
            extractAssetsToDatabaseDirectory(DATABASE_NAME);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    mDB = SQLiteDatabase.openOrCreateDatabase(DATABASE_FULL_PATH, null);

}
 
  1. I try to query data from my SQLitedatabase, but it doesn't work, here the line "SELECT * FROM " doesn't show green as it should.

:-

public ArrayList<String> getWord(int dicType) {
    String tableName = getTableName(dicType);
    String q = "SELECT * FROM " + tableName;
    Cursor result = mDB.rawQuery(q, null);
    ArrayList<String> source = new ArrayList<>();
    while (result.moveToNext()) {
        source.add(result.getString(result.getColumnIndex(COL_KEY)));
    }
    return source;
}

And here is structure of SQLite database i just put it as image

Subsequent failure

thank you, i changed the code like in step 8, because it didnt look exactly like it shows, and there werent SQLite mistakes in Logcat, except this:

06-16 19:24:15.406 2925-2925/com.example.dimarozkin.diplomdict E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.dimarozkin.diplomdict, PID: 2925
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
    at com.example.dimarozkin.diplomdict.MainActivity.onPrepareOptionsMenu(MainActivity.java:194)
    at android.app.Activity.onPreparePanel(Activity.java:3406)
    at android.support.v4.app.FragmentActivity.onPrepareOptionsPanel(FragmentActivity.java:530)
    at android.support.v4.app.FragmentActivity.onPreparePanel(FragmentActivity.java:518)
    at android.support.v7.view.WindowCallbackWrapper.onPreparePanel(WindowCallbackWrapper.java:98)
    at android.support.v7.app.AppCompatDelegateImplBase$AppCompatWindowCallbackBase.onPreparePanel(AppCompatDelegateImplBase.java:359)
    at android.support.v7.view.WindowCallbackWrapper.onPreparePanel(WindowCallbackWrapper.java:98)
    at android.support.v7.app.ToolbarActionBar$ToolbarCallbackWrapper.onPreparePanel(ToolbarActionBar.java:521)
    at android.support.v7.app.ToolbarActionBar.populateOptionsMenu(ToolbarActionBar.java:455)
    at android.support.v7.app.ToolbarActionBar$1.run(ToolbarActionBar.java:55)
    at android.os.Handler.handleCallback(Handler.java:790)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6494)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

as i understand the error is in here

public boolean onPrepareOptionsMenu(Menu menu) {
    String activeFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container).getClass().getSimpleName();
    if (activeFragment.equals(BookmarkFragment.class.getSimpleName())) {
        menuSettings.setVisible(false);
        toolbar.findViewById(R.id.edit_search).setVisibility(View.GONE);
        toolbar.setTitle("Bookmark");
    } else {
        menuSettings.setVisible(true);
        toolbar.findViewById(R.id.edit_search).setVisibility(View.VISIBLE);
        toolbar.setTitle("");

    }
    return true;
}

but i still dont know how to solve it...

Community
  • 1
  • 1
KuCo
  • 1
  • 1
  • 2
    Hi KuCo, welcome to stackoverflow. Please copy+paste your code in as text, not as images. Thanks! – Erty Seidohl Jun 15 '18 at 21:23
  • hello Erty, i edited my question as you ask – KuCo Jun 15 '18 at 21:42
  • `myDirectory()` would be a method that returns a value of type File and should be elsewhere within you class code (perhaps include all the code for that class). The second issue is that the IDE may be warning you that the Cursor hasn't been closed. Add `result.close();` immediately before the line `return source;` – MikeT Jun 15 '18 at 22:05
  • ok the 1st problem i resolved just using this: "File dbLocation = new File(DATABASE_LOCATION); dbLocation.mkdirs();", the 2nd one i did as u advised and there weren't any mistakes during building, but after installing APK it says "YOU_APP keeps stopping" and i cant launch it...maybe the reason in my SQLite database? – KuCo Jun 15 '18 at 22:46
  • You need to edit your question to include the stack-trace (in Android Studio click on **Logcat**, probably at the bottom, find the error messages). You might find this helpful regarding locating the stack-trace [Analyze a stack trace](https://developer.android.com/studio/debug/stacktraces). You will virtually without doubt, also need to add most of the code from the Database Helper class. – MikeT Jun 16 '18 at 00:13

1 Answers1

0

As you are having difficulties the following is a hastily put together tutorial along with code.

  1. Create the database and tables in an SQLite tool, adding data as required and then Save it.

  2. Close the database and re-open it to check that the tables and data are as expected. If not make changes and then repeat 2 until you are sure that the saved database matches.

  3. Obtain the filename of the saved database and record it including the file extension.

  4. If you haven't yet created a Project for the App then do so and save the project.

  5. Outside of the IDE navigate to the projects app/src/main folder and create a folder named assets if it doesn't already exist.

  6. Copy the database file into the assets folder.

  7. Open the Project in Android Studio.

  8. Create a new Java Class named DatabaseHelper with SuperClass as SQLiteOpenHelper (will resolve to android.database.sqlite.SQLiteOpenHelper) and tick the Show Select Overrides Dialog checkbox. The click OK.

It should look like :-

public class DatabaseHelper extends SQLiteOpenHelper {
    public Databasehelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
}
  1. Add a line, as a class variable, after public class DatabaseHelper extends SQLiteOpenHelper { that is like :-

    public static final String DBNAME = "my_dic.db";
    
    • Noting that it is important that the value within the quotes is the exactly the same as the file name that was copied into the assets folder.

.

  1. Add the following class variables

:-

public static final int DBVERSION = 1;
public static final String TB_BOOKMARK = "Bookmark";
public static final String COL_BOOKMARK_KEY = "key";
public static final String COL_BOOKMARK_VALUE = "value";
public static final String COL_BOOKMARK_DATE = "date";
SQLiteDatabase mDB;
  • Noting that the values in quotes need to match the respetice table/column names that were defined in the database for TB_BOOKMARK, COL_BOOKMARK_KEY, COL_BOOKMARK_VALUE and COl_BOOKMARK_DATE.
    • DBVERSION will be the version number stored in the database's user_version field.
    • SQliteDatabase mDB is a declartion for a variable to hold the SQLiteDatabase when it has been opened. NOTE currently it's value is null until it has been set.

.

  1. Change the constructor for the Databasehelper class from :-

    public DatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); }

to :-

public DatabaseHelper(Context context) {
    super(context, DBNAME, null, DBVERSION);
}
  • This makes it so the an instance of the Databasehelper class can be created with just one parameter, the context. The other values have been defined or in the case of the factory none will be used, so null signifies this.

.

  1. Add a method, ifDBExists to the DatabaseHelper class to check to see if the database exists (you only want to copy it from the assets file once)

:-

private boolean ifDBExists(Context context) {
    String dbparent = context.getDatabasePath(DBNAME).getParent();
    File f = context.getDatabasePath(DBNAME);
    if (!f.exists()) {
        Log.d("NODB MKDIRS","Database file not found, making directories."); //<<<< remove before the App goes live.
        File d = new File(dbparent);
        d.mkdirs();
        //return false;
    }
    return f.exists();
}
  • In addition to checking that the database file exists (note it is assumed to be a valid database file),
  • Additionally, if the database doesn't exist then it may be that the database directory doesn't exist, this will create it if it doesn't exist.

.

  1. Add another method copyDBFromAssets to copy the asset file to the database

:-

private boolean copyDBFromAssets(Context context) {
    Log.d("CPYDBINFO","Starting attemtpt to cop database from the assets file.");
    String DBPATH = context.getDatabasePath(DBNAME).getPath();
    InputStream is;
    OutputStream os;
    int length = 8192;
    long bytes_read = 0;
    long bytes_written = 0;
    byte[] buffer = new byte[length];

    try {

        is = context.getAssets().open(DBNAME);
    } catch (IOException e) {
        Log.e("CPYDB FAIL - NO ASSET","Failed to open the Asset file " + DBNAME);
        e.printStackTrace();
        return false;
    }

    try {
        os = new FileOutputStream(DBPATH);
    } catch (IOException e) {
        Log.e("CPYDB FAIL - OPENDB","Failed to open the Database File at " + DBPATH);
        e.printStackTrace();
        return false;
    }
    Log.d("CPYDBINFO","Initiating copy from asset file" + DBNAME + " to " + DBPATH);
    while (length >= 8192) {
        try {
            length = is.read(buffer,0,length);
        } catch (IOException e) {
            Log.e("CPYDB FAIL - RD ASSET",
                    "Failed while reading in data from the Asset. " +
                            String.valueOf(bytes_read) +
                            " bytes read ssuccessfully."
            );
            e.printStackTrace();
            return false;
        }
        bytes_read = bytes_read + length;
        try {
            os.write(buffer,0,length);
        } catch (IOException e) {
            Log.e("CPYDB FAIL - WR ASSET","failed while writing Database File " +
                    DBPATH +
                    ". " +
            String.valueOf(bytes_written) +
                    " bytes written successfully.");
            e.printStackTrace();
            return false;

        }
        bytes_written = bytes_written + length;
    }
    Log.d("CPYDBINFO",
            "Read " + String.valueOf(bytes_read) + " bytes. " +
                    "Wrote " + String.valueOf(bytes_written) + " bytes."
    );
    try {
        os.flush();
        is.close();
        os.close();
    } catch (IOException e ) {
        Log.e("CPYDB FAIL - FINALISING","Failed Finalising Database Copy. " +
                String.valueOf(bytes_read) +
                " bytes read." +
                String.valueOf(bytes_written) +
                " bytes written."
        );
        e.printStackTrace();
        return false;
    }
    return true;
}
  • Note this is intentionally long-winded, so that any failure can be pin-pointed.

The complete DatabaseHelper class would now be :-

public class DatabaseHelper extends SQLiteOpenHelper {

    public static final String DBNAME = "my_dic.db"; // <<<< VERY IMPORTANT THAT THIS MATCHES DATABASE FILE NAME
    public static final int DBVERSION = 1;
    public static final String TB_BOOKMARK = "Bookmark";
    public static final String COL_BOOKMARK_KEY = "key";
    public static final String COL_BOOKMARK_VALUE = "value";
    public static final String COL_BOOKMARK_DATE = "date";
    SQLiteDatabase mDB;

    public DatabaseHelper(Context context) {
        super(context, DBNAME, null, DBVERSION);
        if (!ifDBExists(context)) {
            if (!copyDBFromAssets(context)) {
                throw new RuntimeException("Failed to Copy Database From Assets Folder");
            }
        }
        mDB = this.getWritableDatabase();
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
    }

    private boolean ifDBExists(Context context) {
        String dbparent = context.getDatabasePath(DBNAME).getParent();
        File f = context.getDatabasePath(DBNAME);
        if (!f.exists()) {
            Log.d("NODB MKDIRS","Database file not found, making directories.");
            File d = new File(dbparent);
            d.mkdirs();
            //return false;
        }
        return f.exists();
    }

    private boolean copyDBFromAssets(Context context) {
        Log.d("CPYDBINFO","Starting attemtpt to cop database from the assets file.");
        String DBPATH = context.getDatabasePath(DBNAME).getPath();
        InputStream is;
        OutputStream os;
        int length = 8192;
        long bytes_read = 0;
        long bytes_written = 0;
        byte[] buffer = new byte[length];

        try {

            is = context.getAssets().open(DBNAME);
        } catch (IOException e) {
            Log.e("CPYDB FAIL - NO ASSET","Failed to open the Asset file " + DBNAME);
            e.printStackTrace();
            return false;
        }

        try {
            os = new FileOutputStream(DBPATH);
        } catch (IOException e) {
            Log.e("CPYDB FAIL - OPENDB","Failed to open the Database File at " + DBPATH);
            e.printStackTrace();
            return false;
        }
        Log.d("CPYDBINFO","Initiating copy from asset file" + DBNAME + " to " + DBPATH);
        while (length >= 8192) {
            try {
                length = is.read(buffer,0,length);
            } catch (IOException e) {
                Log.e("CPYDB FAIL - RD ASSET",
                        "Failed while reading in data from the Asset. " +
                                String.valueOf(bytes_read) +
                                " bytes read ssuccessfully."
                );
                e.printStackTrace();
                return false;
            }
            bytes_read = bytes_read + length;
            try {
                os.write(buffer,0,length);
            } catch (IOException e) {
                Log.e("CPYDB FAIL - WR ASSET","failed while writing Database File " +
                        DBPATH +
                        ". " +
                String.valueOf(bytes_written) +
                        " bytes written successfully.");
                e.printStackTrace();
                return false;

            }
            bytes_written = bytes_written + length;
        }
        Log.d("CPYDBINFO",
                "Read " + String.valueOf(bytes_read) + " bytes. " +
                        "Wrote " + String.valueOf(bytes_written) + " bytes."
        );
        try {
            os.flush();
            is.close();
            os.close();
        } catch (IOException e ) {
            Log.e("CPYDB FAIL - FINALISING","Failed Finalising Database Copy. " +
                    String.valueOf(bytes_read) +
                    " bytes read." +
                    String.valueOf(bytes_written) +
                    " bytes written."
            );
            e.printStackTrace();
            return false;
        }
        return true;
    }
}

.

  1. Change the constructor to run the copyDBFromAssets method when/if the Database doesn't exist (using the ifDBExists method)

:-

public DatabaseHelper(Context context) {
    super(context, DBNAME, null, DBVERSION);
    if (!ifDBExists(context)) {
        if (!copyDBFromAssets(context)) {
            throw new RuntimeException("Failed to Copy Database From Assets Folder");
        }
    }
    mDB = this.getWritableDatabase();
}
  • Note if there was an issue copying the Database then the App will be stopped due to the RunTimeExcpetion issued.

.

  1. Last amend an activity's onCreate method (would normally be the main activity) to create an instance of the DatabaseHelper class. Then run the App (if the App has been run it would be best to delete the App's data before doing so, just in case a database, perhaps empty, has been created.)

The following code also includes a query that will tell you what tables exist in the database :-

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DatabaseHelper mDBHlpr = new DatabaseHelper(this);
        Cursor csr = mDBHlpr.getWritableDatabase().query(
                "sqlite_master",
                null,null,null,null,null,null
        );
        while (csr.moveToNext()) {
            Log.d("DB TABLES", csr.getString(csr.getColumnIndex("name")));
        }
        csr.close();
    }
}

Based upon the screen shot and a database file named my_dic.db. The output in the Log is :-

06-16 02:28:45.208 4467-4467/? D/NODB MKDIRS: Database file not found, making directories.
06-16 02:28:45.208 4467-4467/? D/CPYDBINFO: Starting attemtpt to cop database from the assets file.
    Initiating copy from asset filemy_dic.db to /data/data/com.mydictionaryapp.mydictionaryapp/databases/my_dic.db
    Read 12288 bytes. Wrote 12288 bytes.
06-16 02:28:45.224 4467-4467/? D/DB TABLES: Bookmark
    sqlite_autoindex_Bookmark_1
    android_metadata
  • This indicates that :-
    • The database didn't exist and the databases directory was created (i.e. data/data/<package name>/databases)
    • 12288 bytes were copied from the asset file to the database file (i.e. a successful copy was made).
    • The resultant database has three entries in the sqlite_master table, the BookMark table, a table called android_metadata (a table automatically create for android devices by the SDK which stores the locale) and an automatically generated index for the BookMark table.

Subsequent Issue

Basically the object doesn't have a method called getClass, rather you need to use the Fragment's inherited getClass method. So you need to enclose the returned fragment in parenthesis's.

So instead of :-

String activeFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container).getClass().getSimpleName();

You could use :-

String activeFragment = (getSupportFragmentManager().findFragmentById(R.id.fragment_container)).getClass().getSimpleName();

Alternately you could use :-

Fragment activeFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);

along with using :-

if (activeFragment instanceOf BookmarkFragment) { ...... rest of your code

instead of using if (activeFragment.equals(BookmarkFragment.class.getSimpleName())) { ......

MikeT
  • 51,415
  • 16
  • 49
  • 68
  • thank you, i changed the code like in step 8, because it didnt loo exactly like it shows, and there werent SQLite mistakes in Logcat, exept this: – KuCo Jun 16 '18 at 19:30
  • Except what mistake? – MikeT Jun 16 '18 at 19:34
  • That should really be another question. In the meantime only have the single line `return super.onPrepareOptionsMenu(menu);` in the `onPrepareOptionsMenu` method and then try (ideally you should delete the App's data to again check that the DB is being copied from the assets folder and then run the App a second time to check that the copied database is being used.) – MikeT Jun 16 '18 at 20:01
  • @KuCo see the edited answer re the last issue but please note that StackOverflow is really about answering single questions about specific/distinct/single problems rather than being a continuous list of answers that end up writing a large portion of the code. – MikeT Jun 16 '18 at 23:58