【问题标题】:SQLiteOpenHelper - Cannot access disposed objectSQLiteOpenHelper - 无法访问已处置的对象
【发布时间】:2019-04-22 14:27:35
【问题描述】:

我有一个 SQLiteOpenHelper 类,它是作为单例编写的。我应该注意,我不是在 Java 中执行此操作,而是使用 Xamarin.Android C# 编写此代码。

这是该课程的一个 sn-p:

public class DatabaseHelper : SQLiteOpenHelper
{
    private static readonly string TAG = typeof(DatabaseHelper).FullName;

    private static readonly string _databaseName = "istockdb";

    private static readonly int _databaseVersion = 32;

    private static DatabaseHelper _instance;

    private Context _context;

    private DatabaseHelper(Context context) : base(context, _databaseName, null, _databaseVersion)
    {
        _context = context;
    }

    [MethodImpl(MethodImplOptions.Synchronized)]
    public static DatabaseHelper Instance(Context context)
    {
        // *** Use the application context, which will ensure that ***
        // *** the Activity's context is not accidentally leaked ***
        return _instance ?? (_instance = new DatabaseHelper(context.ApplicationContext));
    }

}

所以我有我的DatabaseHelper,它是一个单例,在活动和服务中像这样使用:

服务:

[Service(Name=Text.MobileBackgroundHbService, Enabled = true, Exported = true), IntentFilter(new []{Intents.SyncHeartbeats})]
public class BGHeartbeatService : BaseIntentService
{
    public BGHeartbeatService()
    {
        this._database = DatabaseHelper.Instance(Application.Context);
    }

    protected override void OnHandleIntent(Intent intent)
    {
        if (this._database == null)
            this._database = DatabaseHelper.Instance(Application.Context);

        if (intent.Action.Equals(Intents.SyncHeartbeats)) SyncHeartbeatRecords();

        var broadcastIntent = new Intent(Intents.MobileRefresh);
        SendBroadcast(broadcastIntent);
    }

}

Activity,实际上是一个BaseActivity,所有Activity都继承自:

[Activity(Label = "BaseActivity")]
public abstract class BaseActivity : AppCompatActivity
{
    /// <summary>
    /// Reference to the current context.
    /// </summary>
    protected Context _context { get; set; }

    /// <summary>
    /// "Tag" used for Log functionallity.
    /// </summary>
    protected string _tag { get; set; }

    /// <summary>
    /// Reference To <see cref="RemoteSyncServiceConnection"/>
    /// </summary>
    protected RemoteSyncServiceConnection _service_connection;

    /// <summary>
    /// Reference To The Current SessionState.
    /// </summary>
    protected SessionState _session_state;

    /// <summary>
    /// Reference To <see cref="SyncReceiver"/>
    /// </summary>
    protected SyncReceiver _sync_receiver;

    /// <summary>
    /// Base FloatingActionButton.
    /// </summary>
    protected FloatingActionButton _base_fab;

    /// <summary>
    /// Is the Fab Menu Shown / Hid.
    /// </summary>
    protected static bool _is_fab_open;

    protected IConnection _printer_connection;

    protected string _printer_address;

    protected bool _service_required;

    protected bool _receiver_required;

    protected MediaPlayer _media_player;

    protected DatabaseHelper _database;

    /// <summary>
    /// <see cref="IntentFilter"/> for the <see cref="SyncReceiver"/>
    /// </summary>
    protected readonly string[] _intent_filters =
    {
        Intents.AlarmCompleteOrders,
        Intents.AlarmHeartbeats,
        Intents.AlarmPkas,
        Intents.AlarmTrackingScans,
        Intents.MobileRefresh
    };

    #region Lifecycle Methods

    /// <summary>
    /// Application Lifecycle Method.
    /// </summary>
    /// <param name="savedInstanceState"></param>
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        // *** Initialize Xamarin.Essentials ***
        Xamarin.Essentials.Platform.Init(this, savedInstanceState);

        // *** Initialize the DatabaseHelper ***
        if(this._database == null)
            this._database = DatabaseHelper.Instance(this.ApplicationContext);
    }

}

DatabaseHelper 实例被频繁处置,导致服务或活动尝试访问已处置的 _database 对象。

这是如何处理的,为什么?

我认为在DatabaseHelper 中将_instance 设为静态以及将构造函数设为私有并强制使用DatabaseHelper.Instance 方法将保留不会被处理的DatabaseHelper 的单个实例在活动和服务之间?

我是不是误会了?

编辑来自 try/catch 块的 logcat 输出,显示抛出的异常。 SaveHeartbeat 方法存在于基础活动中。:

protected void SaveHeartbeat(DateTime time, string sourceActivity, [CallerMemberName] string sourceEvent = "")
        {
            try
            {
                var heartbeat = new SmartWarehouse.Shared.Models.Heartbeat(sourceActivity,
                                                                            sourceEvent,
                                                                            this._session_state.CurrentSession.ROWID.ToString());

                this._database.InsertHeartbeat(heartbeat);
            }
            catch (Exception e)
            {
                // TODO: Document Exception
                Util.Tools.Bark(e);
            }
        }

编辑 2 DatabaseHelper.InsertHeartbeat():

/// <summary>
/// Inserts a Heartbeat record into local DB.
/// </summary>
/// <param name="heartbeat"></param>
/// <returns></returns>
public long InsertHeartbeat(Heartbeat heartbeat)
{
    if (heartbeat == null) return -2L;
    using (var db = this.WritableDatabase)
    {
        var id = -3L;
        db.BeginTransactionNonExclusive();
        try
        {
            var cv = GetContentValues(heartbeat);
            id = db.Insert(DatabaseSchema.Heartbeat.TableName, null, cv);
            db.SetTransactionSuccessful();
        }
        catch (Exception e)
        {
            // TODO: Document Exception
            Util.Tools.Bark(e);
        }
        finally
        {
            db.EndTransaction();
        }
        return id;
    }
}

好吧,我的理论是,当我在using() 语句中访问db 对象时,它正在处理DatabaseHelper 对象使用的数据库。还注意到我没有使用我应该使用的db.InsertOrThrow() 方法。我要对我的DatabaseHelper 类进行一些重新处理,看看是否能解决问题。

【问题讨论】:

  • 您能否提供任何证据表明正在处置 DatabaseHelper 实例?
  • 一秒钟我会发布一些 logcat 输出
  • 真的不知道这里发生了什么。不过,我要指出的是,它似乎是正在处理的 SQLiteDatabase,而不是 DatabaseHelper。如果那个 DatabaseHelper 被处理掉了,我的世界就没有意义了。
  • 该死!我认为您实际上在金钱上是对的,我将发布一个编辑以澄清。但是我需要重新设计我的辅助方法,以不处理我打赌这会导致问题的数据库。
  • @G.BlakeMeike 感谢您的帮助!很确定我正在处理 SQLiteDatabase 对象,因为我使用 using() 语句在辅助方法中访问它的方式。如果这解决了问题,将更新!

标签: c# android xamarin.android sqliteopenhelper


【解决方案1】:

事实证明,我的 DatbaseHelper 的单例实例没有被处理掉。

实际上,我正在从辅助方法中处理 SQLiteDatabase 正在使用的 SQLiteDatabase 对象。

要真正解决问题,我所要做的就是改变:

/// <summary>
/// Inserts a Heartbeat record into local DB.
/// </summary>
/// <param name="heartbeat"></param>
/// <returns></returns>
public long InsertHeartbeat(Heartbeat heartbeat)
{
    if (heartbeat == null) return -2L;
    // This using() statement is causing the disposal
    using (var db = this.WritableDatabase)
    {
        var id = -3L;
        db.BeginTransactionNonExclusive();
        try
        {
            var cv = GetContentValues(heartbeat);
            id = db.Insert(DatabaseSchema.Heartbeat.TableName, null, cv);
            db.SetTransactionSuccessful();
        }
        catch (Exception e)
        {
            // TODO: Document Exception
            Util.Tools.Bark(e);
        }
        finally
        {
            db.EndTransaction();
        }
        return id;
    }
}

到:

/// <summary>
/// Inserts a Heartbeat record into local DB.
/// </summary>
/// <param name="heartbeat"></param>
/// <returns></returns>
public long InsertHeartbeat(Heartbeat heartbeat)
{
    if (heartbeat == null) return -2L;

    // This is no longer initialized in a using() statement
    var db = this.WritableDatabase;

        var id = -3L;
        db.BeginTransactionNonExclusive();
        try
        {
            var cv = GetContentValues(heartbeat);
            id = db.Insert(DatabaseSchema.Heartbeat.TableName, null, cv);
            db.SetTransactionSuccessful();
        }
        catch (Exception e)
        {
            // TODO: Document Exception
            Util.Tools.Bark(e);
        }
        finally
        {
            db.EndTransaction();
        }
        return id;

}

总结:

通过在我的辅助方法内部的using() 语句中初始化我的SQLiteDatabase db 对象,我正在处理我的DatabaseHelper 需要的SQLiteDatabase

【讨论】:

    猜你喜欢
    • 2011-10-23
    • 2011-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    相关资源
    最近更新 更多