Android SQLite数据库在读取时锁定
我们正在构建的Xamarin.Android应用程序存在数据库锁定问题。作为应用程序功能的概述:Android SQLite数据库在读取时锁定,android,sqlite,xamarin,xamarin.android,sqlite-net-pcl,Android,Sqlite,Xamarin,Xamarin.android,Sqlite Net Pcl,我们正在构建的Xamarin.Android应用程序存在数据库锁定问题。作为应用程序功能的概述: 表A中的get插入项 用户按下按钮运行计划作业,以将数据从数据库同步到API。当成功响应返回时,我们将删除表中的该行 用户仍然可以将数据添加到表A中,作为其例程的一部分 我们在A表有锁 我们构建了一个测试应用程序来尝试和模拟这一点,解决方案是构建一个表B来复制表a中的数据,这样用户就可以继续处理表a。但是,当表B中的数据试图同步(删除就是为了模拟这一点)其数据时,我们会在Get()上获得表上的锁
- 表A中的get插入项
- 用户按下按钮运行计划作业,以将数据从数据库同步到API。当成功响应返回时,我们将删除表中的该行
- 用户仍然可以将数据添加到表A中,作为其例程的一部分
- 我们在A表有锁
使用系统;
使用SQLite;
使用System.Collections.Generic;
使用System.Linq;
名称空间锁模拟
{
公共类样本数据库B:IDatabase
{
公共样本数据库B()
{
尝试
{
字符串dbPath=System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
“样本B.db3”);
使用(SQLiteConnection db=newsqliteconnection(dbPath))
{
db.CreateTable();
db.Close();
}
}
捕获(例外情况除外)
{
Console.WriteLine(“已创建数据库”);
}
}
公共列表Get()
{
列表样本=新列表();
字符串dbPath=System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
“样本B.db3”);
使用(var db=new SQLiteConnection(dbPath,SQLiteOpenFlags.ReadOnly))
{
var s=db.Table();
//问题在这里>s.ToList()显示锁
样本=s.ToList();
db.Close();
}
返回样品;
}
公开作废副本(列出样本)
{
字符串dbPath=System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
“样本B.db3”);
使用(var db=new SQLiteConnection(dbPath,SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.FullMutex | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache))
{
db.InsertAll(样本);
db.Close();
}
}
公共作废保存(对象示例)
{
字符串dbPath=System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
“样本B.db3”);
使用(var db=new SQLiteConnection(dbPath,SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache))
{
db.插入(样本);
db.Close();
}
}
}
}
我的预定工作在这里:
使用系统;
使用System.Collections.Generic;
使用System.Threading.Tasks;
使用Android.App;
使用Android.App.Job;
名称空间锁模拟
{
[Service(Name=“com.testapp.LockSimulation.Job”,Permission=“android.Permission.BIND\u Job\u Service”)]
公共类同步作业:作业服务
{
SampleDatabaseB db=null;
公共覆盖bool OnStartJob(JobParameters jobParams)
{
db=新的SampleDatabaseB();
Task.Run(()=>
{
var loopCount=jobParams.Extras.GetInt(“loopCount”,10);
List samples=db.Get();
尝试
{
foreach(样本中的样本)
{
db.删除(样本);
}
如果(db.Get().Count==0)
{
JobFinished(jobParams,false);
}
其他的
{
JobFinished(jobParams,true);
}
}
catch(SQLite.SQLiteException-ex)
{
控制台写入线(ex);
}
});
返回true;
}
公共覆盖bool OnStopJob(JobParameters jobParams)
{
//在作业尝试完成之前,我们将检查示例数据库。
//如果数据库中有样本,我们需要告知流程
//重新运行作业。
//如果执行此操作时出现问题,请告诉流程重试。
//如果(db.Get().Count==0)
//{
//返回false;
//}
//否则
//{
//返回true;
//}
尝试
{
如果(db.Get().Count==0)
{
返回false;
}
其他的
{
返回true;
}
}
捕获(例外情况除外)
{
var属性=新字典
{
{“同步作业异常”,例如Message},
{“DB问题”,“DB可能正在对其进行处理。将重试。”}
};
Console.WriteLine(属性);
//崩溃。跟踪错误(例如,属性);
返回true;
}
}
}
}
我的按钮代码如下:
button.Click+=委托{
SampleDatabaseB SampleDatabaseB=新的SampleDatabaseB();
List samplesA=sampleDatabase.Get();
List samplesB=sampleDatabaseB.Get();
WriteLine(String.Format(“拷贝{0}之前的总样本数”,samplesA.Count));
WriteLine(String.Format(“复制{0}之前B中的总样本数”,samplesB.Count));
样本数据库B.副本(样本A);
sampleDatabase.DeleteAll();
WriteLine(String.Format(“复制{0}后B中的样本总数”,sampleDatabaseB.Get().Count));
WriteLine(String.Format(“After副本{0},sampleDatabase.Get().Count中的样本总数”);
var jobBuilder=this.createJobbuilderUserSingJobId(1)
using System;
using SQLite;
using System.Collections.Generic;
using System.Linq;
namespace LockSimulation
{
public class SampleDatabaseB : IDatabase
{
public SampleDatabaseB()
{
try
{
string dbPath = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
"sampleb.db3");
using (SQLiteConnection db = new SQLiteConnection(dbPath))
{
db.CreateTable<Sample>();
db.Close();
}
}
catch (Exception ex)
{
Console.WriteLine("DB already created");
}
}
public List<Sample> Get()
{
List<Sample> samples = new List<Sample>();
string dbPath = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
"sampleb.db3");
using (var db = new SQLiteConnection(dbPath,SQLiteOpenFlags.ReadOnly))
{
var s = db.Table<Sample>();
// ISSUE IS HERE> s.ToList() SHOWS THE LOCK
samples = s.ToList();
db.Close();
}
return samples;
}
public void Copy(List<Sample> samples)
{
string dbPath = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
"sampleb.db3");
using (var db = new SQLiteConnection(dbPath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.FullMutex | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache))
{
db.InsertAll(samples);
db.Close();
}
}
public void Save(object sample)
{
string dbPath = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
"sampleb.db3");
using (var db = new SQLiteConnection(dbPath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache))
{
db.Insert(sample);
db.Close();
}
}
}
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Android.App;
using Android.App.Job;
namespace LockSimulation
{
[Service(Name = "com.testapp.LockSimulation.Job", Permission = "android.permission.BIND_JOB_SERVICE")]
public class SyncJob : JobService
{
SampleDatabaseB db = null;
public override bool OnStartJob(JobParameters jobParams)
{
db = new SampleDatabaseB();
Task.Run( () =>
{
var loopCount = jobParams.Extras.GetInt("LoopCount", 10);
List<Sample> samples = db.Get();
try
{
foreach (Sample sample in samples)
{
db.Delete(sample);
}
if (db.Get().Count == 0)
{
JobFinished(jobParams, false);
}
else
{
JobFinished(jobParams, true);
}
}
catch (SQLite.SQLiteException ex)
{
Console.WriteLine(ex);
}
});
return true;
}
public override bool OnStopJob(JobParameters jobParams)
{
//Before the Job tries to finish, we will check the Samples DB.
//If there is Samples on the DB, we will need to tell the process
//to run the job again.
//If there is an issues in doing so, tell the process to try again.
//if (db.Get().Count == 0)
//{
// return false;
//}
//else
//{
// return true;
//}
try
{
if (db.Get().Count == 0)
{
return false;
}
else
{
return true;
}
}
catch (Exception ex)
{
var properties = new Dictionary<string, string>
{
{ "Syncing Job Exception", ex.Message},
{ "DB Issue", "DB could be having processing done on it. Will try again."}
};
Console.WriteLine(properties);
//Crashes.TrackError(ex, properties);
return true;
}
}
}
}
button.Click += delegate {
SampleDatabaseB sampleDatabaseB = new SampleDatabaseB();
List<Sample> samplesA = sampleDatabase.Get();
List<Sample> samplesB = sampleDatabaseB.Get();
Console.WriteLine(String.Format("Total Samples In A Before Copy {0}", samplesA.Count));
Console.WriteLine(String.Format("Total Samples In B Before Copy {0}", samplesB.Count));
sampleDatabaseB.Copy(samplesA);
sampleDatabase.DeleteAll();
Console.WriteLine(String.Format("Total Samples In B After Copy {0}", sampleDatabaseB.Get().Count));
Console.WriteLine(String.Format("Total Samples In A After Copy {0}", sampleDatabase.Get().Count));
var jobBuilder = this.CreateJobBuilderUsingJobId<SyncJob>(1)
.SetRequiredNetworkType(NetworkType.Any)
.SetBackoffCriteria(2000, BackoffPolicy.Linear)
.SetPersisted(true)
.Build();
var jobScheduler = (JobScheduler)GetSystemService(JobSchedulerService);
var scheduleResult = jobScheduler.Schedule(jobBuilder);
if (JobScheduler.ResultSuccess == scheduleResult)
{
Console.WriteLine("Samples Scheduled for Syncing Successfully");
}
else
{
Console.WriteLine("Samples Unsuccessfully Scheduled for Syncing.");
}
};