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