gpt4 book ai didi

android - 极少数情况下出现 "android.database.sqlite.SQLiteException: no such table"错误

转载 作者:可可西里 更新时间:2023-11-01 19:07:33 25 4
gpt4 key购买 nike

我目前在应用商店中有一款应用可用,但有一种错误报告我似乎无法完全弄清楚。我的应用程序使用内部 sqlite 数据库,但在某些设备上(当然不是大多数)有时会出现以下错误:

android.database.sqlite.SQLiteException: no such table: image_data (code 1): , while compiling: SELECT Min(stamp) FROM image_data WHERE category = 'Astronomy' AND stamp >= 1357426800 and coalesce(title_nl, '') = ''

我确信这个表存在并且我确信这个查询是正确的。我知道这个错误只发生在应用程序的小部件和 AlarmManager 触发的 BroadcastReceiver 中(应用程序偶尔会尝试下载新条目,因为它是当天应用程序的图片)。

我觉得和我访问数据库时所处的Context有关系。我有一个名为 AppContextHelper 的类,它扩展了 Application 并有一个静态成员,我在其中存储上下文。访问数据库时始终使用该上下文。

我的问题:在某些情况下,当获取小部件中的数据库或上述由 AlarmManager 触发的 BroadcastReceiver 时,上下文是否无效,在这种情况下,我应该使用提供的上下文来支持“应用程序”语境?如果是这样,为什么该上下文无效或更好,提供的是哪个上下文?

提前致谢!

根据要求,导致问题的代码再次仅在某些设备上,并且仅在小部件或 AlarmManager 类中。我将在 AlarmManager 类中发布导致错误的代码(即行数最少的代码)

  1. 初始化闹钟的代码:

        Intent myIntent = new Intent(AppContextHelper.getContext(), ApodDownloader.class);
    mPendingIntent = PendingIntent.getBroadcast(AppContextHelper.getContext(), 0, myIntent, 0);

    AlarmManager alarmManager = (AlarmManager)AppContextHelper.getContext().getSystemService(Context.ALARM_SERVICE);
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());

    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis() + 10000, AlarmManager.INTERVAL_HOUR, mPendingIntent);
  2. AppContextHelper.java

    public class AppContextHelper extends Application {

    private static Context mContext;

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

    public static Context getContext(){
    return mContext;
    }
    }
  3. (部分)ApodDownloader.java(这包含抛出异常的行)

    @Override
    public void onReceive(Context arg0, Intent arg1) {
    (new AsyncTaskThreadPool<Integer, Void, Boolean>() {

    @Override
    protected Boolean doInBackground(Integer... params) {
    Helpers.logMessage("Checking new entries.");


    SQLiteDatabase db = FrescoDatabase.acquireReadableDatabase(AppContextHelper.getContext());
    try {
    >>> THIS LINE THROWS THE EXCEPTION <<<
    maxStamp = Helpers.executeScalarLong(db, "SELECT Min(stamp) FROM image_data WHERE category = 'Astronomy' AND stamp >= 1357426800 and coalesce(title_nl, '') = ''");

    [...]
    } finally {
    FrescoDatabase.releaseReadableDatabase();
    }

    [...] more code

    }
    [...] onPostExecute
    }).executeOnExecutor(AsyncTaskThreadPool.THREAD_POOL_EXECUTOR, 0);
  4. FrescoDatabase.java数据库由应用程序在启动时自动生成,此代码正在运行,也在触发异常的设备上运行。我怎么强调数据库都存在于有问题的设备上,因为除了 AlarmManager 的小部件和 BroadcastReceiver 之外,应用程序运行完美,所以请不要告诉我数据库未正确初始化:)

    public class FrescoDatabase extends SQLiteOpenHelper {

    public static final String[] OBSOLETE_DATABASE_FILE_NAMES = new String[] { "Fresco.v1.sqlite", "Fresco.v2.sqlite", "Fresco.v3.sqlite", "Fresco.v4.sqlite", "Fresco.v5.sqlite" };
    public static final String DATABASE_FILE_NAME = "Fresco.v6.sqlite";
    public static final int DATABASE_FILE_SIZE = 15302656;
    private static final int DATABASE_VERSION = 7;

    private static final Lock writeLock = new ReentrantLock();
    private static SQLiteDatabase currentDB = null;

    public static final SQLiteDatabase acquireWritableDatabase(Context c) {
    writeLock.lock();
    currentDB = new FrescoDatabase(c).getWritableDatabase();
    return currentDB;
    }

    public static final void releaseWritableDatabase() {
    currentDB.close();
    currentDB = null;
    writeLock.unlock();
    }

    public static final SQLiteDatabase acquireReadableDatabase(Context c) {
    writeLock.lock();
    currentDB = new FrescoDatabase(c).getReadableDatabase();
    return currentDB;
    }

    public static final void releaseReadableDatabase() {
    currentDB.close();
    currentDB = null;
    writeLock.unlock();
    }

    private FrescoDatabase(Context context) {
    super(context, InitializeFrescoDatabaseTask.getDatabaseFileName(context), null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    // database is automatically generated, this should not be called
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
    }
  5. (部分)Helpers.java

        public class Helpers {
    [...]
    public static long executeScalarLong(SQLiteDatabase db, String query) {
    return executeScalarLong(db, query, new String[] { });
    }
    public static long executeScalarLong(SQLiteDatabase db, String query, String... parameters) {
    (line 85, see stack trace down below) Cursor cursor = db.rawQuery(query, parameters);
    try {
    cursor.moveToNext();
    long val = cursor.getLong(0);
    return val;
    }
    catch (Exception e) {
    ;
    }
    finally {
    cursor.close();
    }
    return -1;
    }
    }

异常日志(按要求):

            java.lang.RuntimeException: An error occured while executing doInBackground()
at nl.tagpulse.utils.AsyncTaskThreadPool$3.done(AsyncTaskThreadPool.java:329)
at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)
Caused by: android.database.sqlite.SQLiteException: no such table: image_data (code 1): , while compiling: SELECT Min(stamp) FROM image_data WHERE category = 'Astronomy' AND stamp >= 1357426800 and coalesce(title_nl, '') = ''
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1012)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:623)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:44)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1314)
at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1253)
at nl.tagpulse.fresco.other.Helpers.executeScalarLong(Helpers.java:85)
at nl.tagpulse.fresco.other.Helpers.executeScalarLong(Helpers.java:82)
at nl.tagpulse.fresco.business.FrescoDatabase.retrieveNewEntries(FrescoDatabase.java:64)
at nl.tagpulse.fresco.business.ApodDownloader$1.doInBackground(ApodDownloader.java:192)
at nl.tagpulse.fresco.business.ApodDownloader$1.doInBackground(ApodDownloader.java:1)
at nl.tagpulse.utils.AsyncTaskThreadPool$2.call(AsyncTaskThreadPool.java:317)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
... 4 more

我在 Helpers.java block 中添加了第 85 行标记。

希望这对您有所帮助!

最佳答案

这是发生了什么:

ApodDownloader 是一个 BroadcastReceiver,在它的 onReceive() 中你启动了某种不允许的 AsyncTask。文档指出:

A BroadcastReceiver object is only valid for the duration of the call to onReceive(Context, Intent). Once your code returns from this function, the system considers the object to be finished and no longer active.

This has important repercussions to what you can do in an onReceive(Context, Intent) implementation: anything that requires asynchronous operation is not available, because you will need to return from the function to handle the asynchronous operation, but at that point the BroadcastReceiver is no longer active and thus the system is free to kill its process before the asynchronous operation completes.

http://developer.android.com/reference/android/content/BroadcastReceiver.html#ReceiverLifecycle

当进程被终止时,应用程序上下文不再可用。您仍然可以访问 AppContextHelper.getContext() 但这将是一个新加载的类,因为旧的类在消除进程时被杀死。换句话说,返回的上下文将为空。只有在极少数情况下才会发生这种情况,即 Android 确实终止了一个进程,这在我的经验中并不常见。

如果您的 AppContextHelper 提供的 Context 尚未初始化,那么您的 InitializeFrescoDatabaseTask.getDatabaseFileName(context) 调用将返回一个空字符串,它自然会打开错误的数据库。因为您在 FrescoDatabase 类的 onCreate() 中未执行任何操作,所以数据库未正确初始化并且找不到表。但即使您创建了表,它也可能是错误的数据库,尽管崩溃已经消失,但它不会显示任何数据。

为防止这种情况发生,您需要在运行 onReceive() 的同一个线程中完成所有工作,或者如果这花费的时间太长,则启动服务来完成繁重的工作。

在分析您的代码时我注意到的另一件事是,您在创建警报时使用应用程序上下文来创建 Intent (new Intent(AppContextHelper.getContext(), ApodDownloader.class);)。执行此操作时,您需要设置 FLAG_ACTIVITY_NEW_TASK,否则您可能会收到“android.util.AndroidRuntimeException:从 Activity 上下文外部调用 startActivity() 需要 FLAG_ACTIVITY_NEW_TASK 标志。这真的是您想要的吗?”这可能会使应用程序崩溃,也可能不会。

关于android - 极少数情况下出现 "android.database.sqlite.SQLiteException: no such table"错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15943534/

25 4 0