有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何在其他线程上正确执行数据库调用?

我真的很困惑,我应该如何在Android应用程序中使用线程进行数据库交互。有太多的资源,我不知道从中选择。所以我希望就我的特殊情况得到更具体、更集中的建议,这样我就有了一个起点

这是我的数据库类结构,到目前为止效果很好:

public class DatabaseHelper extends SQLiteOpenHelper {
    private static volatile SQLiteDatabase mDatabase;
    private static DatabaseHelper mInstance = null;
    private static Context mContext;

    private static final String DB_NAME = "database.db";
    private static final int DB_VERSION = 1;

    private static final DB_CREATE_THINGY_TABLE = "CREATE TABLE blahblahblah...";
    //other various fields here, omitted

    public static synchronized DatabaseHelper getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new DatabaseHelper(context.getApplicationContext());
            try {
                mInstance.open();
            }
            catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return mInstance;
    }

    private DatabaseHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(DB_CREATE_THINGY_TABLE);
    }

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

    }

    @Override
    public void onConfigure(SQLiteDatabase db){
        super.onConfigure(db);
        db.setForeignKeyConstraintsEnabled(true);
    }

    public void open() throws SQLException {
        mDatabase = getWritableDatabase();
    }

    public void close() {
        mDatabase.close();
    }

    public long addNewThingy(String name) {
        ContentValues values = new ContentValues();
        values.put(DatabaseHelper.THINGY_COLUMN_NAME, name);
        return mDatabase.insertWithOnConflict(DatabaseHelper.THINGY_TABLE, null, values, SQLiteDatabase.CONFLICT_IGNORE);
    }

    public Thingy getThingyById(long id) {
        Cursor cursor = mDatabase.query(
                DatabaseHelper.THINGY_TABLE, // table
                new String[]{DatabaseHelper.THINGY_COLUMN_ID, DatabaseHelper.THINGY_COLUMN_NAME}, // column names
                DatabaseHelper.THINGY_COLUMN_ID + " = ?", // where clause
                new String[]{id + ""}, // where params
                null, // groupby
                null, // having
                null);  // orderby
        cursor.moveToFirst();
        Thingy thingy = null;
        if (!cursor.isAfterLast()) {
            String name = getStringFromColumnName(cursor, DatabaseHelper.THINGY_COLUMN_NAME);
            thingy = new Thingy(id, name);
            cursor.moveToNext();
        }
        cursor.close();
        return thingy;
    }

}

所以,任何时候我想要访问数据库,我都会mDatabaseHelper = DatabaseHelper.getInstance(context);,我很乐意去。我不会直接调用open()close()之类的东西。然而,我相信现在我所有的数据库调用都是在UI线程上进行的(无论是在我的onCreate或onCreateView方法中,还是在不调用任何新线程或任何东西的单独方法中)

我如何正确地使这个线程化,从而不在UI线程上执行数据库操作

我想我必须更改所有数据库调用才能基本做到这一点:

  1. 首先对数据库类进行任何必要的编辑,以确保它在多个线程试图同时执行操作的情况下正常工作。我已经试着让我的班级成为一个单身学生(不管怎样,我认为这是一个单身学生?)使用“volatile”和“synchronized”之类的关键词,但也许我遗漏了什么

  2. 在自己的线程中执行数据库操作

  3. 以某种方式触发相应函数/活动/片段中的附加代码,这些代码将在数据库操作完成后执行

  4. 使整个过程足够多功能,我可以在任何地方进行

我说得通吗?这样做对吗?有没有一个简单的例子可以告诉我,例如,如何正确地使用适当的线程从一个示例活动/片段/etc执行mThingy = mDatabaseHelper.getThingyById(id);mDatabaseHelper.addNewThingy(someName);之类的操作


共 (2) 个答案

  1. # 1 楼答案

    How would I correctly make this threaded so that I am not performing database operations on the UI thread?

    只需在UI线程外执行任何数据库操作。一种常见的技术涉及AsyncTask。例如:

    public class GetThingyTask extends AsyncTask<Long, Void, Thingy> {
    
        private Context context;
    
        public AddTask(Context context){
            this.context = context;
        }
    
        @Override
        protected Thingy doInBackground(Long... ids) {
    
            final long id = ids[0];
    
            Cursor cursor = DatabaseHelper.getInstance(context).query(
                DatabaseHelper.THINGY_TABLE,
                new String[]{
                    DatabaseHelper.THINGY_COLUMN_ID,
                    DatabaseHelper.THINGY_COLUMN_NAME
                },
                DatabaseHelper.THINGY_COLUMN_ID + "=?",
                new String[]{String.valueOf(id)},
                null, null, null);
    
            String name = null;
    
            if (cursor.moveToFirst() && (cursor.getCount() > 0)) {
                name = getStringFromColumnName(cursor, DatabaseHelper.THINGY_COLUMN_NAME);
            }
    
            cursor.close();
    
            return new Thingy(id, name);
    
        }
    
        @Override
        protected void onPostExecute(Thingy thingy) {
            //Broadcast the Thingy somehow. EventBus is a good choice.
        }
    
    }
    

    并使用它(例如,在Activity内):

    new GetThingyTask(this).execute(id);
    
  2. # 2 楼答案

    使用线程的简单解决方案

    public class DatabaseHelper extends SQLiteOpenHelper {
        //...
    
        public void addNewThingyAsync(final String name, final Callback<Long> cb) {
            new Thread(new Runnable(){
                @Override
                public void run(){
                    cb.callback(addNewThingy(name));
                }
            }).start();
        }
    
        private synchronized long addNewThingy(String name){
            //implementation...
        }
    
        public void getThingyByIdAsync(final long id, final Callback<Thingy> cb) {
            new Thread(new Runnable(){
                @Override
                public void run(){
                    cb.callback(getThingyById(id));
                }
            }).start();
        }
    
        private synchronized Thingy getThingyById(long id) {
            //implementation...
        }
    
        public interface Callback<T> {
            public void callback(T t);
        }
    }
    

    使用异步任务的解决方案

    同上,但有以下变化:

    public class DatabaseHelper extends SQLiteOpenHelper {
        //...
    
        public void addNewThingyAsync(final String name, final Callback<Long> cb) {
            new AsyncTask<Void, Void, Long>(){
                @Override
                protected Long doInBackground(Void... ignoredParams) {
                    return addNewThingy(name);
                }
    
                @Override
                protected void onPostExecute(Long result) {
                    cb.callback(result);
                }
            }.execute();
        }
    
        //...
    
        public void getThingyByIdAsync(final long id, final Callback<Thingy> cb) {
            new AsyncTask<Void, Void, Thingy>(){
                @Override
                protected Thingy doInBackground(Void... ignoredParams) {
                    return getThingyById(id);
                }
    
                @Override
                protected void onPostExecute(Thingy result) {
                    cb.callback(result);
                }
            }.execute();
        }
        //...
    }
    

    调用(两种方法都适用)

    long mId = ...; 
    mDatabaseHelper = DatabaseHelper.getInstance(context);
    mDatabaseHelper.getThingyByIdAsync(mId, new Callback<Thingy>{
        @Override
        public void callback(Thingy thingy){
            //do whatever you want to do with thingy
        }
    });