【问题标题】:How to combine a SQLite database with a fragment?如何将 SQLite 数据库与片段结合起来?
【发布时间】:2019-08-15 23:47:00
【问题描述】:

我想创建一个摩尔质量计算器。为此,我需要访问我存储群众的数据库。 我为此创建了一个片段。如果我单击按钮,应用程序应显示数据库中的质量。

如果我把它放在一个活动中,它就可以工作。

这是我的 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

我真的希望有人可以帮助我:)

【问题讨论】:

    标签: android sqlite android-fragments


    【解决方案1】:

    我认为这是 SQLiteAssetHelper 无法应对使用 WAL(预写日志记录)作为默认值引入的 SQLite 数据库的问题(在 API 28 之前,日志模式一直是默认值)。

    如果不是,则基本上是 molmassen.file(确切命名)不在 assets/databases 文件夹中。在任何一种情况下,如果数据库文件名为 molmassen.db 并且它位于 assets 文件夹的数据库文件夹中,则以下修复将起作用。

    因此,我建议更改为使用非 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 以及获取底层 SQLiteDatabase 进行测试的方法,而无需与您的数据库完全相同)检查 cmets: -

    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 
    

    ........日志的其余部分因太大而被省略,但足以说明预期的表格已显示。

    【讨论】:

    • 非常感谢:),不过我自己修好了。我不知道是什么问题。我在另一台计算机上运行该应用程序 - 突然它工作了......在我自己的计算机上再次尝试后,它仍然可以工作。
    猜你喜欢
    • 1970-01-01
    • 2021-11-21
    • 2018-05-29
    • 2011-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多