Android AsyncTask和SQLite DB实例

8
我有一个问题,不确定如何解决。我的app中有多个AsyncTask访问同一个SQLiteOpenHelper。我在onCreate()中初始化并打开帮助器,在onStop()中关闭它。我还会在onResume()中检查是否已初始化。

自从我发布了我的应用程序后,我收到了许多在doInBackground中出现Null Exception的错误报告,这是因为在调用doInBackground之前关闭了DB(onStop()),很公平。

我的问题是,我应该在哪里关闭DB连接?在Activity中使用单个DB helper实例并从多个线程(AsyncTasks)访问它是正确的吗?还是每个AsyncTask都应该使用单独的DB helper实例?

以下是我的Activity的简化框架:

public class MyActivity extends Activity{
    private DbHelper mDbHelper;
    private ArrayList<ExampleObject> objects;

    @Override
    public void onStop(){
        super.onStop();
        if(mDbHelper != null){
            mDbHelper.close();
            mDbHelper = null;
        }
    }

    @Override
    public void onResume(){
        super.onResume();
        if(mDbHelper == null){
            mDbHelper = new DbHelper(this);
            mDbHelper.open();
        }
    }

    @Override 
    public void onCreate(Bundle icicle) { 
        super.onCreate(icicle); 
        DbHelper mDbHelper = new DbHelper(this);
        mDbHelper.open();
    }

    private class DoSomething extends AsyncTask<String, Void, Void> {

        @Override
        protected Void doInBackground(String... arg0) {
            objects = mDbHelper.getMyExampleObjects();
            return null;
        }

        @Override
        protected void onPostExecute(final Void unused){
            //update UI with my objects
        }
    }

    private class DoSomethingElse extends AsyncTask<String, Void, Void> {

        @Override
        protected Void doInBackground(String... arg0) {
            objects = mDbHelper.getSortedObjects();
            return null;
        }

        @Override
        protected void onPostExecute(final Void unused){
            //update UI with my objects
        }
    }
}
3个回答

11

您不需要为每个活动(activity)管理数据库连接。您可以在android.app.Application的一个实例中完成这项工作,并使用此实例访问数据库。

像这样:

public class MyApplication extends Application {

    // Synchronized because it's possible to get a race condition here
    // if db is accessed from different threads. This synchronization can be optimized
    // thought I wander if it's necessary
    public synchronized static SQLiteDatabase db() {
        if(self().mDbOpenHelper == null) {
            self().mDbOpenHelper = new MyDbOpenHelper();
        }
        return self().mDbOpenHelper.getWritableDatabase();
    }

    public static Context context() {
        return self();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mSelf = this;
    }

    private static MyApplication self() {
        if (self == null) throw new IllegalStateException();
        return mSelf;
    }

    private MyDbOpenHelper mDbOpenHelper;

    private static MyApplication mSelf;
}

通过这种方式,您可以确保您的数据库始终可访问。

而且,拥有一个Db助手实例是一种良好的实践。线程同步已经默认为您处理。


另外还有一个问题...你会在哪里关闭数据库连接? - Marqs
我不关闭它。不知道这是否真的必要。如果您能找到更多关于此的细节,请分享,这很有趣。我认为我从未遇到过使用这种方法的任何问题。 - Dmitry Ryadnenko
谢谢您回复我。我会尝试更深入地调查这个问题。 - Marqs
如果您仍然感兴趣,我已经在“Application”的“onTerminate”方法中看到连接被关闭。 - Giulio Piancastelli
1
此方法适用于模拟进程环境。在生产Android设备上,进程会被简单地终止以删除它们;在这样做时不执行任何用户代码(包括此回调)。 - Dmitry Ryadnenko
如果有人想要这个示例的完整代码,请访问此处... http://ashwinrayaprolu.wordpress.com/2011/03/15/android-database-example-database-usage-asynctask-database-export/ - Rikin Patel

5

使用单一的DB Helper是好的选择。

问题在于当用户离开Activity时,DB会被关闭,但AsyncTask可能仍在运行。所以,在尝试访问它时,您应检查DB不为null,如果为null,则可能意味着您的Activity已被销毁,需要cancel该任务。


谢谢您的快速回复。我忘了提到我也收到了一些异常,显示“由于未完成的语句无法关闭”。这可能意味着我在doInBackground()中执行查询时尝试在onStop()中关闭DB连接。我在关闭DB之前取消了AsyncTask,但仍然会出现此错误。有任何想法为什么会发生这种情况吗? - Marqs
@Ovidiu 你通常把数据库初始化和清理代码放在哪里?(假设有一个DBHelper被多个视图访问) - Mister Smith
正如@boulder所说,将其保留在应用程序中可能是一个好的实践。 - Ovidiu Latcu

1

你提到在关闭数据库之前取消 AsyncTask。但是请记住,取消 AsyncTask 只是发出信号要取消任务,并且您需要检查 doInBackground() 中的 isCancelled() 并采取必要措施停止数据库操作。

在关闭数据库之前,您还需要检查 getStatus() 确保 AsyncTask 已停止。


所以基本上我应该在doInBackground中使用while(!isCancelled){},并在循环内执行所有计算吗? - Marqs
是的,在主线程中检查getStatus()以确保AsyncTask已经结束。 - Hemant Sharma
好的,但如果我在onStop()中检查它并且它还没有结束呢?那么我应该在哪里关闭DB连接呢? - Marqs
对于AsyncTask,建议在收到取消信号时尽早退出doInBackground。因此,在doInBackground的while循环中,每当您要执行DB语句时,请检查isCancelled()是否为真,并在那里中断。在主线程的onStop()中,应该使用sleep循环并等待AsyncTask完成后再关闭DB连接。 - Hemant Sharma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接