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