Как объединить базу данных SQLite с фрагментом? - PullRequest
0 голосов
/ 25 марта 2019

Я хочу создать калькулятор молярной массы. Для этого мне нужен доступ к моей базе данных, где я хранил массы. Я создал фрагмент для этого. Если я нажму кнопку, приложение должно отобразить массу из базы данных.

Если я добавлю это в действие, оно будет работать.

Это мой DatabaseOpenHelper:

public class DatabaseOpenHelper extends SQLiteAssetHelper {

    private static final String DATABASE_NAME = "molmassen.db";
    private static final int DATABASE_VERSION = 1;

    public DatabaseOpenHelper(Context context){
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
}

Доступ к базе данных:

public class DatabaseAccess {

    private SQLiteOpenHelper openHelper;
    private SQLiteDatabase db;
    private static DatabaseAccess instance;
    Cursor c = null;

    public DatabaseAccess(Context context){
        this.openHelper = new DatabaseOpenHelper(context);

    }

    public static DatabaseAccess getInstance (Context context){
        if (instance == null){
            instance = new DatabaseAccess(context);

        }
        return instance;
    }

    public void open(){

        this.db=openHelper.getWritableDatabase();
        }

    public void close(){
        if(db!=null){
            this.db.close();
        }
    }

    public String getMolmasse(String kürzel){
        c=db.rawQuery("select Molmasse from Molmassen where Kürzel = '"+kürzel+"'",new String[]{});
        StringBuffer buffer = new StringBuffer();
        while (c.moveToNext()){
           String masse = c.getString(0);
           buffer.append(""+masse);
        }
        return buffer.toString();

    }
}

и фрагмент (я думаю, проблема в этом ...)

public class MolmasseFragment extends Fragment implements View.OnClickListener{
     public DatabaseAccess databaseAccess;

     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_molmasse, container, false);

        name = view.findViewById(R.id.name);
        Button query_button = (Button) view.findViewById(R.id.query_button);
        result = view.findViewById(R.id.result);

        databaseAccess = new DatabaseAccess(getActivity());


        button.setOnClickListener(this);
        query_button.setOnClickListener(this);


        return view;
    }

    public void onClick(View v) {

        switch (v.getId()) {

            case R.id.query_button:


                DatabaseAccess databaseAccess = DatabaseAccess.getInstance(getActivity());
                databaseAccess.open();

                String kürzel = name.getText().toString();
                String masse = databaseAccess.getMolmasse(kürzel);

                result.setText(masse);

                databaseAccess.close();


                break;

            default:
                break;

       }
    }
}

Я получаю эту ошибку

com.readystatesoftware.sqliteasset.SQLiteAssetHelper$SQLiteAssetException: Missing databases/molmassen.db file (or .zip, .gz archive) in assets, or target folder not writable

Я действительно надеюсь, что кто-нибудь может мне помочь:)

1 Ответ

0 голосов
/ 26 марта 2019

Я полагаю, что это проблема, связанная с тем, что SQLiteAssetHelper не справляется с введением баз данных SQLite с использованием WAL (запись в журнал с опережением записи) по умолчанию (режим журнала использовался по умолчанию до API 28).

Если нет, то, по сути, файл molmassen.file (названный именно так) не находится в папке assets / database .В любом случае приведенное ниже исправление будет работать, если файл базы данных называется molmassen.db И он находится в папке базы данных в папке ресурсов.

Поэтому я бы предложил изменить метод копирования, отличный от SQLiteAssetHelper, для копированиябазы данных.

Например, рассмотрим следующий альтернативный помощник по базам данных, а именно: DatabaseOpenHelperAlt

public class DatabaseOpenHelperAlt extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "molmassen.db";
    private static final int DATABASE_VERSION = 1;
    Context myContext;
    int buffer_size = 1024 * 4, blocks_copied = 0, bytes_copied = 0;

    public DatabaseOpenHelperAlt(Context context){
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        myContext = context;
        if (!checkDataBase()) {
            try {
                copyDataBase();
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException("Error copy database file " + DATABASE_NAME + " - see stack-trace above" );
            }
        }
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

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

    }

    private boolean checkDataBase() {
        /**
         * Does not open the database instead checks to see if the file exists
         * also creates the databases directory if it does not exists
         * (the real reason why the database is opened, which appears to result in issues)
         */

        File db = new File(myContext.getDatabasePath(DATABASE_NAME).getPath()); //Get the file name of the database
        Log.d("DBPATH","DB Path is " + db.getPath()); //TODO remove for Live App
        if (db.exists()) return true; // If it exists then return doing nothing

        // Get the parent (directory in which the database file would be)
        File dbdir = db.getParentFile();
        // If the directory does not exits then make the directory (and higher level directories)
        if (!dbdir.exists()) {
            db.getParentFile().mkdirs();
            dbdir.mkdirs();
        }
        return false;
    }

    private void copyDataBase() throws IOException {

        final String TAG = "COPYDATABASE";

        //Open your local db as the input stream
        Log.d(TAG,"Initiated Copy of the database file " + DATABASE_NAME + " from the assets folder."); //TODO remove for Live App
        //Note how the databases folder is hard coded (to suit the same location as used by SQLiteAssetHelper)
        InputStream myInput = myContext.getAssets().open("databases" + File.separator + DATABASE_NAME); // Open the Asset file
        String dbpath = myContext.getDatabasePath(DATABASE_NAME).getPath();
        Log.d(TAG,"Asset file " + DATABASE_NAME + " found so attmepting to copy to " + dbpath); //TODO remove for Live App

        // Path to the just created empty db
        //String outFileName = DB_PATH + DB_NAME;
        //Open the empty db as the output stream
        File outfile = new File(myContext.getDatabasePath(DATABASE_NAME).toString());
        Log.d("DBPATH","path is " + outfile.getPath()); //TODO remove for Live App
        //outfile.setWritable(true); // NOT NEEDED as permission already applies
        //OutputStream myoutputx2 = new FileOutputStream(outfile);
        /* Note done in checkDatabase method
        if (!outfile.getParentFile().exists()) {
            outfile.getParentFile().mkdirs();
        }
        */

        OutputStream myOutput = new FileOutputStream(outfile);
        //transfer bytes from the inputfile to the outputfile
        byte[] buffer = new byte[buffer_size];
        int length;
        while ((length = myInput.read(buffer))>0) {
            blocks_copied++;
            Log.d(TAG,"Ateempting copy of block " + String.valueOf(blocks_copied) + " which has " + String.valueOf(length) + " bytes."); //TODO remove for Live App
            myOutput.write(buffer, 0, length);
            bytes_copied += length;
        }
        Log.d(TAG,
                "Finished copying Database " + DATABASE_NAME +
                        " from the assets folder, to  " + dbpath +
                        String.valueOf(bytes_copied) + "were copied, in " +
                        String.valueOf(blocks_copied) + " blocks of size " +
                        String.valueOf(buffer_size) + "."
        ); //TODO remove for Live App
        //Close the streams
        myOutput.flush();
        myOutput.close();
        myInput.close();
        Log.d(TAG,"All Streams have been flushed and closed."); //TODO remove for Live App
    }
}

Использование вышеупомянутого вместе с измененным классом DatabaseAccess (для вызова альтернативного DatabaseHelper итакже метод для получения базовой базы данных SQLite для тестирования без необходимости иметь точно такую ​​же базу данных, как у вас) проверить комментарии : -

public class DatabaseAccess {

    private SQLiteOpenHelper openHelper;
    private SQLiteDatabase db;
    private static DatabaseAccess instance;
    Cursor c = null;

    public DatabaseAccess(Context context){
        //this.openHelper = new DatabaseOpenHelper(context); //<<<<<<<<<< OLD REPLACED
        this.openHelper = new DatabaseOpenHelperAlt(context); //<<<<<<<<<< NEW
        db = this.openHelper.getWritableDatabase(); //<<<<<<<<<< force open
    }

    //!!!!!!!!! ADDED for simple testing
    public SQLiteDatabase getDB() {
        return db;
    }

    public static DatabaseAccess getInstance (Context context){
        if (instance == null){
            instance = new DatabaseAccess(context);

        }
        return instance;
    }

    public void open(){

        this.db=openHelper.getWritableDatabase();
    }

    public void close(){
        if(db!=null){
            this.db.close();
        }
    }

    public String getMolmasse(String kürzel){
        c=db.rawQuery("select Molmasse from Molmassen where Kürzel = '"+kürzel+"'",new String[]{});
        StringBuffer buffer = new StringBuffer();
        while (c.moveToNext()){
            String masse = c.getString(0);
            buffer.append(""+masse);
        }
        return buffer.toString();
    }
}

Работающее приложение со следующим фрагментом (уменьшено для удобства): -

public class MolassesFragment extends Fragment {

    public DatabaseAccess databaseAccess;

    private MolassesViewModel mViewModel;

    public static MolassesFragment newInstance() {
        return new MolassesFragment();
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.molasses_fragment, container, false);
        databaseAccess = new DatabaseAccess(getContext());
        SQLiteDatabase db = databaseAccess.getDB();
        Cursor csr = db.query("sqlite_master",null,null,null,null,null,null);
        DatabaseUtils.dumpCursor(csr);
        csr.close();
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mViewModel = ViewModelProviders.of(this).get(MolassesViewModel.class);
        // TODO: Use the ViewModel
    }
}

Для первого запуска (запуска, который копирует базу данных), он работает, и журнал показывает: -

2019-03-26 12:14:15.419 7929-7929/aaa.so55342826preexistdbassethelperexample D/DBPATH: DB Path is /data/user/0/aaa.so55342826preexistdbassethelperexample/databases/molmassen.db
2019-03-26 12:14:15.419 7929-7929/aaa.so55342826preexistdbassethelperexample D/COPYDATABASE: Initiated Copy of the database file molmassen.db from the assets folder.
2019-03-26 12:14:15.419 7929-7929/aaa.so55342826preexistdbassethelperexample D/COPYDATABASE: Asset file molmassen.db found so attmepting to copy to /data/user/0/aaa.so55342826preexistdbassethelperexample/databases/molmassen.db
2019-03-26 12:14:15.419 7929-7929/aaa.so55342826preexistdbassethelperexample D/DBPATH: path is /data/user/0/aaa.so55342826preexistdbassethelperexample/databases/molmassen.db
2019-03-26 12:14:15.419 7929-7929/aaa.so55342826preexistdbassethelperexample D/COPYDATABASE: Ateempting copy of block 1 which has 4096 bytes.
2019-03-26 12:14:15.420 7929-7929/aaa.so55342826preexistdbassethelperexample D/COPYDATABASE: Ateempting copy of block 2 which has 4096 bytes.
.....
2019-03-26 12:14:15.422 7929-7929/aaa.so55342826preexistdbassethelperexample D/COPYDATABASE: Ateempting copy of block 27 which has 4096 bytes.
2019-03-26 12:14:15.422 7929-7929/aaa.so55342826preexistdbassethelperexample D/COPYDATABASE: Finished copying Database molmassen.db from the assets folder, to  /data/user/0/aaa.so55342826preexistdbassethelperexample/databases/molmassen.db110592were copied, in 27 blocks of size 4096.
2019-03-26 12:14:15.423 7929-7929/aaa.so55342826preexistdbassethelperexample D/COPYDATABASE: All Streams have been flushed and closed.
2019-03-26 12:14:15.452 7929-7929/aaa.so55342826preexistdbassethelperexample 

.......... остальная часть журнала опущена как слишком большая, но достаточно сказать, что ожидаемые таблицы были отображены.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...