【问题标题】:Flutter: Unable to execute database CRUD inside dart IsolateFlutter:无法在 dart Isolate 中执行数据库 CRUD
【发布时间】:2020-08-09 20:39:07
【问题描述】:

我正在尝试在单独的 dart 隔离/计算中从服务器同步数据,还需要使用 SQFlite 将下载的数据转储到数据库,但我收到错误,我无法弄清楚出了什么问题。

这里是代码


import "dart:isolate";
import "package:flutter/foundation.dart";
import "package:flutter/widgets.dart";
//import 'package:path_provider/path_provider.dart';
import "package:sqflite/sqflite.dart";

Future<Database> getDB() async{
  //var dir= await getExternalStorageDirectory();
  //var dbPath= "${dir.path}youtility.db";
  var dbPath= "/storage/emulated/0/Android/data/com.example.sampleapp/files/test.db";
  var database= await openDatabase(
    dbPath,
    version: 1,
    onCreate: (db, version) {
      return db.execute(
        "CREATE TABLE typeassist ("
          "taid   INTEGER PRIMARY KEY,"
          "tacode TEXT,"
          "taname TEXT"
        ");"
      );
    }
  );
  return database;
}

Future<int> upsert() async {
  print("upsert() -");
  Database db= await getDB();
  var taMapList= [
    { "taid": 1, "tacode": "ASSET", "taname": "ASSET" },
    { "taid": 2, "tacode": "SMARTPLACE", "taname": "SMARTPLACE" },
    { "taid": 3, "tacode": "CHECKPOINT", "taname": "CHECKPOINT" },
  ];
  for(var i= 0; i < taMapList.length; i++) {
    print("upsert() taMapList[$i] ${taMapList[i]}");
    await db.insert("typeassist", taMapList[i], conflictAlgorithm: ConflictAlgorithm.replace);
  }
  await db.close();
  print("upsert() db closed");
  db= null;
  print("upsert() +");
  return 0;
}

Future<int> fetch(String name) async {
  print("fetch(name $name) -");
  Database db= await getDB();
  var list= await db.rawQuery("select * from typeassist");
  for(var i= 0; i < list.length; i++) {
    print("fetch() [$i] ${list[i]}");
  }
  await db.close();
  print("fetch() db closed");
  db= null;
  print("fetch() +");
  return 0;
}

//  NORMAL
Future<int> dbNormal() async {
  print("dbNormal() -");
  await upsert();
  await fetch("normal");
  print("dbNormal() +");
  return 0;
}

//  COMPUTE
Future<int> dbCompute() async{
  print("dbCompute() -");
  await upsert();
  compute(fetch, "compute");
  print("dbCompute() +");
  return 0;
}

//  ISOLATE
Isolate isolate;
Future<int> start() async {
  print("start() -");
  ReceivePort receivePort= new ReceivePort();
  isolate= await Isolate.spawn(fetchData, receivePort.sendPort);
  receivePort.listen((data) {
    print("start() RECEIVE: $data");
  });
  print("start() +");
  return 0;
}

int stop() {
  print("stop() -");
  if(isolate != null) {
    isolate.kill(priority: Isolate.immediate);
    isolate= null;
  }
  print("stop() +");
  return 0;
}

int fetchData(SendPort sendPort) {
  print("fetchDB(sendPort $sendPort) -");
  var f= fetch("isolate");
  f.then((rc) {
    print("fetchDB() SEND: 0");
    sendPort.send(0);
  });
  print("fetchDB() +");
  return 0;
}

Future<int> dbIsolate() async {
  print("dbIsolate() -");
  start();
  print("dbIsolate() +");
  return 0;
}

Future<int> main() async {
  print("main() -");
  WidgetsFlutterBinding.ensureInitialized();

  print("main()  normal execution -");
  var n= dbNormal();
  n.then((rc){
    print("main() normal execution +");
  });

  await Future.delayed(new Duration(seconds: 1));
  /*print("main() compute execution -");
  var c= dbCompute();
  c.then((rc){
    print("main() compute execution +");
  });*/

  print("main() isolate execution -");
  var i= dbIsolate();
  i.then((rc){
    print("main() isolate execution +");
  });
  print("main() +");
  return 0;
}


这是错误日志



Performing hot restart...
Syncing files to device XT1706...
Restarted application in 1,801ms.
I/flutter (15820): main() -
I/flutter (15820): main()  normal execution -
I/flutter (15820): dbNormal() -
I/flutter (15820): upsert() -
I/flutter (15820): upsert() taMapList[0] {taid: 1, tacode: ASSET, taname: ASSET}
I/flutter (15820): upsert() taMapList[1] {taid: 2, tacode: SMARTPLACE, taname: SMARTPLACE}
I/flutter (15820): upsert() taMapList[2] {taid: 3, tacode: CHECKPOINT, taname: CHECKPOINT}
I/flutter (15820): upsert() db closed
I/flutter (15820): upsert() +
I/flutter (15820): fetch(name normal) -
I/flutter (15820): fetch() [0] {taid: 1, tacode: ASSET, taname: ASSET}
I/flutter (15820): fetch() [1] {taid: 2, tacode: SMARTPLACE, taname: SMARTPLACE}
I/flutter (15820): fetch() [2] {taid: 3, tacode: CHECKPOINT, taname: CHECKPOINT}
I/flutter (15820): fetch() db closed
I/flutter (15820): fetch() +
I/flutter (15820): dbNormal() +
I/flutter (15820): main() normal execution +
I/flutter (15820): main() isolate execution -
I/flutter (15820): dbIsolate() -
I/flutter (15820): start() -
I/flutter (15820): dbIsolate() +
I/flutter (15820): main() +
I/flutter (15820): main() isolate execution +
I/flutter (15820): fetchDB(sendPort SendPort) -
I/flutter (15820): start() +
I/flutter (15820): fetch(name isolate) -
I/flutter (15820): fetchDB() +
E/flutter (15820): [ERROR:flutter/runtime/dart_isolate.cc(915)] Unhandled exception:
E/flutter (15820): ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized.
E/flutter (15820): If you're running an application and need to access the binary messenger before `runApp()` has been called (for example, during plugin initialization), then you need to explicitly call the `WidgetsFlutterBinding.ensureInitialized()` first.
E/flutter (15820): If you're running a test, you can call the `TestWidgetsFlutterBinding.ensureInitialized()` as the first line in your test's `main()` method to initialize the binding.
E/flutter (15820): #0      defaultBinaryMessenger.<anonymous closure> (package:flutter/src/services/binary_messenger.dart:76:7)
E/flutter (15820): #1      defaultBinaryMessenger (package:flutter/src/services/binary_messenger.dart:89:4)
E/flutter (15820): #2      MethodChannel.binaryMessenger (package:flutter/src/services/platform_channel.dart:140:62)
E/flutter (15820): #3      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:314:35)
E/flutter (15820): #4      invokeMethod (package:sqflite/src/sqflite_impl.dart:17:13)
E/flutter (15820): #5      SqfliteDatabaseFactoryImpl.invokeMethod (package:sqflite/src/factory_impl.dart:82:7)
E/flutter (15820): #6      SqfliteDatabaseMixin.invokeMethod (package:sqflite_common/src/database_mixin.dart:287:15)
E/flutter (15820): #7      SqfliteDatabaseMixin.safeInvokeMethod.<anonymous closure> (package:sqflite_common/src/database_mixin.dart:208:43)
E/flutter (15820): #8      wrapDatabaseException (package:sqflite/src/exception_impl.dart:7:32)
E/flutter (15820): #9      SqfliteDatabaseFactoryImpl.wrapDatabaseException (package:sqflite/src/factory_impl.dart:78:7)
E/flutter (15820): #10     SqfliteDatabaseMixin.safeInvokeMethod (package:sqflite_common/src/database_mixin.dart:208:15)
E/flutter (15820): #11     SqfliteDatabaseMixin.openDatabase (package:sqflite_common/src/database_mixin.dart:542:15)
E/flutter (15820): #12     SqfliteDatabaseMixin.doOpen (package:sqflite_common/src/database_mixin.dart:635:28)
E/flutter (15820): #13     SqfliteDatabaseOpenHelper.openDatabase (package:sqflite_common/src/database.dart:46:22)
E/flutter (15820): #14     SqfliteDatabaseFactoryMixin.openDatabase.<anonymous closure> (package:sqflite_common/src/factory_mixin.dart:104:43)
E/flutter (15820): <asynchronous suspension>
E/flutter (15820): #15     ReentrantLock.synchronized.<anonymous closure>.<anonymous closure> (package:synchronized/src/reentrant_lock.dart:35:24)
E/flutter (15820): #16     _rootRun (dart:async/zone.dart:1126:13)
E/flutter (15820): #17     _CustomZone.run (dart:async/zone.dart:1023:19)
E/flutter (15820): #18     _runZoned (dart:async/zone.dart:1518:10)
E/flutter (15820): #19     runZoned (dart:async/zone.dart:1465:12)
E/flutter (15820): #20     ReentrantLock.synchronized.<anonymous closure> (package:synchronized/src/reentrant_lock.dart:34:24)
E/flutter (15820): #21     BasicLock.synchronized (package:synchronized/src/basic_lock.dart:32:26)
E/flutter (15820): #22     ReentrantLock.synchronized (package:synchronized/src/reentrant_lock.dart:30:17)
E/flutter (15820): #23     SqfliteDatabaseFactoryMixin.openDatabase (package:sqflite_common/src/factory_mixin.dart:71:17)
E/flutter (15820): #24     openDatabase (package:sqflite/sqflite.dart:152:26)
E/flutter (15820): #25     getDB (package:sampleapp/main.dart:11:23)
E/flutter (15820): #26     fetch (package:sampleapp/main.dart:48:22)
E/flutter (15820): #27     fetchData (package:sampleapp/main.dart:103:10)
E/flutter (15820): #28     _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:310:17)
E/flutter (15820): #29     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174:12)


我被这个问题困住了,寻找所有可能的解决方案,也尝试了很多方法,但我仍然不明白哪里出了问题。请帮忙

【问题讨论】:

    标签: flutter dart async-await sqflite dart-isolates


    【解决方案1】:

    只能在主隔离区进行访问。

    • sqflite 本机访问已在后台本机线程中进行
    • 事务机制不是交叉隔离安全的
    • sqflite_common_ffi 访问是在单独的隔离中进行的。

    这里有一些相关的讨论:

    【讨论】:

      【解决方案2】:

      让我试着帮助你。

      首先我创建一个DaoMaster 类表单管理所有表。

          import 'package:path/path.dart';
      import 'package:sqflite/sqflite.dart';
      import 'package:flutter/widgets.dart';
      
      class DaoMaster {
      
        static final DaoMaster dbProvider = DaoMaster();
      
        //region [ attributs ]
        static final int schemaVersion = 1;
        //endregion
      
        //region [ Configuration DataBase ]
        static Database _database;
        //endregion
      
        //region [ App DB ]
        Future<Database> get database async {
          if (_database != null) {
            return _database;
          }
          _database = await initDb();
          return _database;
        }
        initDb() async {
          String databasesPath = await getDatabasesPath();
          String path = join(databasesPath, 'test.db');
      
          var db = await openDatabase(path,
              version: schemaVersion,
              onCreate: _onCreate,
              onUpgrade: _onUpgrade,
              onDowngrade: _onDowngrade);
          return db;
        }
        //endregion
      
        //region [ _onCreate, _onUpgrade, _onDowngrade ]
        Future _onCreate(Database db, int version) async {
          //print("Creating tables for schema version  $schemaVersion");
          createAllTables(db, true);
        }
      
        Future _onUpgrade(Database db, int oldVersion, int newVersion) async {
          //print("Upgrating tables for schema oldVersion: $oldVersion | newVersion:$newVersion | SCHEMA_VERSION $schemaVersion");
          //DataBaseUpgrade.onUpgrade(db, oldVersion, newVersion);
        }
      
        Future _onDowngrade(Database db, int oldVersion, int newVersion) async {
          //println("onDowngrating tables for schema oldVersion: $oldVersion | newVersion:$newVersion | SCHEMA_VERSION $schemaVersion");
          //createAllTables(db, false);
        }
        //endregion
      
        //region [ createAllTables, dropAllTables ]
        Future createAllTables(Database db, bool ifNotExists){
          TypeAssistDao.createTable(db, ifNotExists);
          TypeAssistDao.defaultData(db);
          return null;
        }
      
        Future dropAllTables(Database db, bool ifExists){
          //TypeAssistDao.dropTable(db, ifExists);
          return null;
        }
        //endregion
      }
      

      之后我创建TypeAssistDao

      import 'package:flutter/cupertino.dart';
      import 'package:sqflite/sqflite.dart';
      
      import 'DaoMaster.dart';
      
      class TypeAssistDao{
      
        //region [ Database ]
        final dbProvider = DaoMaster.dbProvider;
        //endregion
      
        static final String tableName = "typeassist";
      
        //region [ createTable - dropTable ]
        static createTable(Database db, bool ifNotExists) {
          String constraint = ifNotExists? " IF NOT EXISTS ": "";
          db.execute(
              'CREATE TABLE $constraint  $tableName ('
                  ' taid INTEGER PRIMARY KEY '
                  ', tacode TEXT'
                  ', taname TEXT'
                  '); ');
          print("Creating table: $tableName");
        }
        static defaultData(Database db) {
          String sql = "INSERT INTO $tableName ";
          sql += " ( taid ,tacode ,taname  ) ";
          sql += " VALUES ";
          sql += " ( 1, 'ASSET', 'ASSET' )";
          sql += ",( 2, 'SMARTPLACE', 'SMARTPLACE' )"; 
          sql += ",( 3, 'CHECKPOINT', 'CHECKPOINT' );";
          db.execute(sql);
          print("Default Data Insert In: $tableName");
        }
      
        /// Drops the underlying database table. */
        static dropTable(Database db, bool ifExists) {
          String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + tableName;
          db.execute(sql);
        }
        //endregion
      }
      

      我想你在那之后会没事的。

      【讨论】:

        猜你喜欢
        • 2020-03-25
        • 2023-01-02
        • 1970-01-01
        • 2023-03-28
        • 2021-09-27
        • 2021-07-14
        • 2020-10-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多