简体   繁体   English

SQLiteOpenHelper - 无法访问已处置的对象

[英]SQLiteOpenHelper - Cannot access disposed object

I have a my SQLiteOpenHelper class, which is written as a singleton. 我有一个我的SQLiteOpenHelper类,它被写成一个单例。 I should note that I am not doing this in Java, I am using Xamarin.Android C# to write this. 我应该注意到我在Java中没有这样做,我使用Xamarin.Android C#来编写这个。

Here's a snippet from that class: 这是该类的一个片段:

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));
    }

}

So I have my DatabaseHelper that is a singleton and is used like this within Activities and Services: 所以我的DatabaseHelper是一个单例,在活动和服务中使用如下:

Service: 服务:

[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, actually a BaseActivity which all Activities inherit from: Activity,实际上是所有活动继承自的BaseActivity

[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);
    }

}

The DatabaseHelper instance is being disposed of frequently causing either services, or activities to try and access the disposed _database object. 正在处理DatabaseHelper实例经常导致服务或活动尝试访问已处置的_database对象。

How is this being disposed of and why? 如何处理这个以及为什么?

I thought making the _instance static within the DatabaseHelper as well as making the constructor private and forcing the use of the DatabaseHelper.Instance method would keep a single instance of the DatabaseHelper that wouldn't be disposed of between activities and services? 我想使_instance的内静态DatabaseHelper以及使构造私有,并强制使用的DatabaseHelper.Instance方法将保持的单个实例DatabaseHelper将不布置的活动和服务之间?

Am I misunderstanding this? 我误解了吗?

EDIT logcat output from try/catch blocks showing the exception being thrown. 来自try / catch块的EDIT logcat输出显示抛出的异常。 The SaveHeartbeat method exists in the base activity.: 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);
            }
        }

异常Logcat

EDIT 2 DatabaseHelper.InsertHeartbeat(): 编辑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;
    }
}

Alright so my theory is that when I access the db object in the using() statement it is disposing of the database that the DatabaseHelper object uses. 好吧我的理论是,当我在using()语句中访问db对象时,它正在处理DatabaseHelper对象使用的DatabaseHelper Also noticed that I'm not using db.InsertOrThrow() method which I should be.. Gonna do some re-working on my DatabaseHelper class to see if that resolves the issue. 还注意到我没有使用我应该使用的db.InsertOrThrow()方法。我将对我的DatabaseHelper类进行一些重新处理以查看是否能解决问题。

It turns out that my singleton instance of the DatbaseHelper was not being disposed of. 事实证明,我的DatbaseHelper单例实例没有被处理掉。

Actually what was happening is I was disposing of the SQLiteDatabase object that was being used by the DatbaseHelper from within the helper methods. 实际上发生的事情是我在帮助器方法中处理了DatbaseHelper正在使用的SQLiteDatabase对象。

All I had to do to actually resolve the issue was change: 实际解决问题所需要做的就是改变:

/// <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;
    }
}

TO: 至:

/// <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;

}

SUMMARY: 摘要:

By initializing my SQLiteDatabase db object inside of using() statements inside of my helper methods I was disposing of the SQLiteDatabase that my DatabaseHelper needed. 通过在我的帮助器方法中的using()语句内初始化我的SQLiteDatabase db对象,我处理了我的DatabaseHelper所需的SQLiteDatabase

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM