flutter:sqflite 数据库通过在 Android 上使用事务而被锁定
flutter: sqflite database getting locked though using transaction on Android
我有原生 Android 应用程序,它通过 EventChannel 每 50 毫秒将 Map() 数据发送到嵌入式 flutter 模块,并且 flutter 模块将接收到的数据添加到数据库(flutter 模块没有 UI)。
在androidactivity中,按一个按钮,一分钟内每5ms向模块发送一次数据。
Android Activity
public class MainActivity extends FlutterActivity implements View.OnClickListener {
private static final String ENGINE_ID = "engine_id";
private static final String CHANNEL_EVENT = "com.example.eventchannel";
private FlutterEngine flutterEngine;
private EventChannel eventChannel;
private EventChannel.EventSink eventSink;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
flutterEngine = new FlutterEngine(this);
flutterEngine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());
FlutterEngineCache.getInstance().put(ENGINE_ID, flutterEngine);
eventChannel = new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL_EVENT);
eventChannel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
eventSink = events;
}
@Override
public void onCancel(Object arguments) {
eventSink = null;
}
});
button = findViewById(R.id.btn_send);
button.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_send){
Handler handler = new Handler(Looper.getMainLooper());
handler.post(() -> {
int i = 0;
MapData mapData = new MapData();
while (i < 1200){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (eventSink != null){
eventSink.success(mapData.createMap());
}
i++;
}
});
}
}
}
public class MapData {
public Map<String, Object> createMap(){
return new HashMap<String, Object>(){
{
put("name", "aaaaaa");
put("age", "aaaaaa");
put("gender", "aaaaaa");
put("birthday", "aaaaaa");
put("phone", "aaaaaa");
put("address", "aaaaaa");
}
};
}
}
然后flutter模块将收到的数据插入数据库。
pubspec.yaml
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
sqflite: ^2.0.2
path_provider: ^2.0.9
synchronized: ^3.0.0+2
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
DatabaseHelper databaseHelper = DatabaseHelper.instance;
FlutterEventChannel.instance.configureChannel(databaseHelper);
}
Database Helper Class
class DatabaseHelper {
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
static Database? _database;
final _dbLock = Lock();
Future<Database> get database async {
if (_database != null) {
return _database!;
} else {
await _dbLock.synchronized(() async {
_database ??= await _initDatabase();
});
return _database!;
}
}
_initDatabase() async {
Directory documentDirectory = await getApplicationDocumentsDirectory();
String path = join(documentDirectory.path, 'sample');
return await openDatabase(path, version: 1, onCreate: _onCreate);
}
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE ${DbDefines.tripTable} (
$'_id' INTEGER PRIMARY KEY,
$'name' TEXT NOT NULL,
$'age' TEXT,
$'gender' TEXT,
$'birthday' TEXT,
$'phone' TEXT,
$'address' TEXT
)
''');
}
Future<void> insert(String table, Map<String, dynamic> row) async {
Database db = await instance.database;
await db.transaction((txn) async {
await txn.insert(table, row);
});
}
}
EventChannel Class
class FlutterEventChannel {
late DatabaseHelper databaseHelper;
static const channelName = 'com.example.eventchannel';
late EventChannel _eventChannel;
late StreamSubscription _streamSubscription;
static final FlutterEventChannel instance = FlutterEventChannel._init();
FlutterEventChannel._init();
void configureChannel(DatabaseHelper databaseHelper) {
this.databaseHelper = databaseHelper;
_eventChannel = const EventChannel(channelName);
_streamSubscription = _eventChannel.receiveBroadcastStream().listen(
(event) {
databaseHelper.insert(
'sample', Map<String, dynamic>.from(event));
},
onError: (error) {}, cancelOnError: true);
}
}
然后按一次按钮发送成功,但是第二次中间出现错误
I/flutter: Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
现在我很困惑,因为我找不到任何解决方案...
任何帮助将不胜感激!
我按如下方式修复了 java 代码并且成功了。
Handler handler = new Handler(Looper.getMainLooper());
Runnable runnable = new Runnable() {
@Override
public void run() {
if (eventSink != null){
eventSink.success(new MapData().createMap());
handler.postDelayed(this, 50);
}
}
};
handler.post(runnable);
我有原生 Android 应用程序,它通过 EventChannel 每 50 毫秒将 Map() 数据发送到嵌入式 flutter 模块,并且 flutter 模块将接收到的数据添加到数据库(flutter 模块没有 UI)。
在androidactivity中,按一个按钮,一分钟内每5ms向模块发送一次数据。
Android Activity
public class MainActivity extends FlutterActivity implements View.OnClickListener {
private static final String ENGINE_ID = "engine_id";
private static final String CHANNEL_EVENT = "com.example.eventchannel";
private FlutterEngine flutterEngine;
private EventChannel eventChannel;
private EventChannel.EventSink eventSink;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
flutterEngine = new FlutterEngine(this);
flutterEngine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());
FlutterEngineCache.getInstance().put(ENGINE_ID, flutterEngine);
eventChannel = new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL_EVENT);
eventChannel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
eventSink = events;
}
@Override
public void onCancel(Object arguments) {
eventSink = null;
}
});
button = findViewById(R.id.btn_send);
button.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_send){
Handler handler = new Handler(Looper.getMainLooper());
handler.post(() -> {
int i = 0;
MapData mapData = new MapData();
while (i < 1200){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (eventSink != null){
eventSink.success(mapData.createMap());
}
i++;
}
});
}
}
}
public class MapData {
public Map<String, Object> createMap(){
return new HashMap<String, Object>(){
{
put("name", "aaaaaa");
put("age", "aaaaaa");
put("gender", "aaaaaa");
put("birthday", "aaaaaa");
put("phone", "aaaaaa");
put("address", "aaaaaa");
}
};
}
}
然后flutter模块将收到的数据插入数据库。
pubspec.yaml
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
sqflite: ^2.0.2
path_provider: ^2.0.9
synchronized: ^3.0.0+2
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
DatabaseHelper databaseHelper = DatabaseHelper.instance;
FlutterEventChannel.instance.configureChannel(databaseHelper);
}
Database Helper Class
class DatabaseHelper {
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
static Database? _database;
final _dbLock = Lock();
Future<Database> get database async {
if (_database != null) {
return _database!;
} else {
await _dbLock.synchronized(() async {
_database ??= await _initDatabase();
});
return _database!;
}
}
_initDatabase() async {
Directory documentDirectory = await getApplicationDocumentsDirectory();
String path = join(documentDirectory.path, 'sample');
return await openDatabase(path, version: 1, onCreate: _onCreate);
}
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE ${DbDefines.tripTable} (
$'_id' INTEGER PRIMARY KEY,
$'name' TEXT NOT NULL,
$'age' TEXT,
$'gender' TEXT,
$'birthday' TEXT,
$'phone' TEXT,
$'address' TEXT
)
''');
}
Future<void> insert(String table, Map<String, dynamic> row) async {
Database db = await instance.database;
await db.transaction((txn) async {
await txn.insert(table, row);
});
}
}
EventChannel Class
class FlutterEventChannel {
late DatabaseHelper databaseHelper;
static const channelName = 'com.example.eventchannel';
late EventChannel _eventChannel;
late StreamSubscription _streamSubscription;
static final FlutterEventChannel instance = FlutterEventChannel._init();
FlutterEventChannel._init();
void configureChannel(DatabaseHelper databaseHelper) {
this.databaseHelper = databaseHelper;
_eventChannel = const EventChannel(channelName);
_streamSubscription = _eventChannel.receiveBroadcastStream().listen(
(event) {
databaseHelper.insert(
'sample', Map<String, dynamic>.from(event));
},
onError: (error) {}, cancelOnError: true);
}
}
然后按一次按钮发送成功,但是第二次中间出现错误
I/flutter: Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
现在我很困惑,因为我找不到任何解决方案... 任何帮助将不胜感激!
我按如下方式修复了 java 代码并且成功了。
Handler handler = new Handler(Looper.getMainLooper());
Runnable runnable = new Runnable() {
@Override
public void run() {
if (eventSink != null){
eventSink.success(new MapData().createMap());
handler.postDelayed(this, 50);
}
}
};
handler.post(runnable);