尽管使用房间数据库 TransactionExecutor,为什么我仍面临线程问题? [英] Why I am facing threading issues despite using room database TransactionExecutor?

查看:22
本文介绍了尽管使用房间数据库 TransactionExecutor,为什么我仍面临线程问题?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有 2 个片段.一个是显示分支详细信息,另一个是显示历史中查看的分支.我想在用户看到分支详细信息时添加条目.

I have 2 fragments. One is showing Branch Details, other is showing showing branches viewed in history. I want to add entry whenver user see branch details.

我遵循 Android 架构原则并使用 Room+LiveData+Repository+Viewmodel.

I am following Android Architecture principles and using Room+LiveData+Repository+Viewmodel.

这是 BranchDetailsFragment:

Here is BranchDetailsFragment:

public class BranchDetailsFragment extends Fragment implements View.OnClickListener {

    private final Application application;
    private final int branchIid;

    private FragBranchDetailsBinding binding;//view-binding

    public BranchDetailsFragment(Application application, int branchIid) {
        //saving branchId
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        //normal stuff
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        BranchDetailsViewModel branchDetailsViewModel = new BranchDetailsViewModel(application);
        
        //inserting entry in history table
        HistoryViewModel historyViewModel = new HistoryViewModel(application);
        History history = new History();
        history.setBranchId(branchIid);
        historyViewModel.insert(history);
        
        //fetching data from branches table
        branchDetailsViewModel.getBranchCustomizedById(branchIid).observe(getViewLifecycleOwner(), new Observer<BranchCustomized>() {
            @Override
            public void onChanged(BranchCustomized branchCustomized) {
                //get branch details and show them
            }
        });
    }
}

这是分支 POJO 房间实体:

Here is the Branch POJO Room Entity:

@Entity(tableName = "branches")
public class Branch {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "_id")
    private int id;

    @ColumnInfo(name = "bank_id")
    private int bankId;
    
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getBankId() {
        return bankId;
    }

    public void setBankId(int bankId) {
        this.bankId = bankId;
    }

}

这是历史 POJO 房间实体:

Here is History POJO Room Entity:

@Entity(tableName = "history", foreignKeys = @ForeignKey(entity = Branch.class, parentColumns = "_id", childColumns = "branch_id"))
public class History {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "_id")
    private int id;

    @ColumnInfo(name = "branch_id")
    private int branchId;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getBranchId() {
        return branchId;
    }

    public void setBranchId(int branchId) {
        this.branchId = branchId;
    }
}

这是historyViewModel

Here is historyViewModel

public class HistoryViewModel extends AndroidViewModel {
    final HistoryRepository repository;

    public HistoryViewModel(Application application) {
        super(application);
        repository = new HistoryRepository(application);
    }

    public LiveData<List<HistoryCustomized>> getAll(){
        return repository.getAll();
    }

    public LiveData<List<HistoryCustomized>>  searchHistoryByBankOrBranch(String token){
        return repository.searchHistoryByBankOrBranch(token);
    }

    public void insert(History history){
        repository.insert(history);
    }
}

这里是历史存储库

public class HistoryRepository {
    private final HistoryDao dao;
    AppDatabase db;


    public HistoryRepository(Application application) {
        db = AppDatabase.getInstance(application);
        dao = db.getHistoryDao();
    }

    public LiveData<List<HistoryCustomized>> getAll() {
        return dao.getAll();
    }

    public LiveData<List<HistoryCustomized>> searchHistoryByBankOrBranch(String token) {
        return dao.searchHistoryByBankOrBranch(token);
    }

    public void insert(History history){
        try {
            db.getTransactionExecutor().execute(new Runnable() {
                @Override
                public void run() {
                    dao.insert(history);
                }
            });
        }catch (NullPointerException e){
            e.printStackTrace();
        }
    }
}

这是HistoryDao

Here is HistoryDao

@Dao
public interface HistoryDao {
    @Query("select history.branch_id, bank, branch from history\n" +
            "join branches on branches._id=history.branch_id\n" +
            "join banks on banks._id=branches.bank_id")
    LiveData<List<HistoryCustomized>> getAll();

    @Query("select history.branch_id, bank, branch from history\n" +
            "join branches on branches._id=history.branch_id\n" +
            "join banks on banks._id=branches.bank_id\n" +
            "where bank like :token or branch like :token")
    LiveData<List<HistoryCustomized>> searchHistoryByBankOrBranch(String token);

    @Transaction
    @Insert
    void insert(History history);
}

我知道线程存在一些问题,因为每当我在模拟器 (BranchFragment) 中运行它时,它都会迅速崩溃,但是当我调试它时,它会显示正确的数据,尽管没有在历史记录表中插入条目.Room 有很多线程问题.

I know there is some problem with threading because whenever I run this in emulator (BranchFragment), it crash quickly, but when I debug it, it show proper data, though not insert entry in history table. Room has many threading issues.

这是应用程序数据库:

@Database(entities = {Branch.class, History.class},
        version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {

    public abstract BranchesDao getBranchesDao();
    public abstract HistoryDao getHistoryDao();

    public static AppDatabase getInstance(final Context context) {
        dbInstance = buildDatabaseInstance(context);
        return dbInstance;
    }

    private static AppDatabase buildDatabaseInstance(Context context) {
        return Room.databaseBuilder(context,
                AppDatabase.class,
                "branch.db")
                .createFromAsset("branch.db")
                .fallbackToDestructiveMigration()
                .build();
    }

    public static void cleanUp() {
        dbInstance = null;
    }

}

这是 HistoryDao_Impl(由 Room 库自动生成):

Here is HistoryDao_Impl(autogenerated by Room library):

@SuppressWarnings({"unchecked", "deprecation"})
public final class HistoryDao_Impl implements HistoryDao {
  private final RoomDatabase __db;

  private final EntityInsertionAdapter<History> __insertionAdapterOfHistory;

  public HistoryDao_Impl(RoomDatabase __db) {
    this.__db = __db;
    this.__insertionAdapterOfHistory = new EntityInsertionAdapter<History>(__db) {
      @Override
      public String createQuery() {
        return "INSERT OR ABORT INTO `history` (`_id`,`branch_id`) VALUES (nullif(?, 0),?)";
      }

      @Override
      public void bind(SupportSQLiteStatement stmt, History value) {
        stmt.bindLong(1, value.getId());
        stmt.bindLong(2, value.getBranchId());
      }
    };
  }

  @Override
  public void insert(final History history) {
    __db.assertNotSuspendingTransaction();
    __db.beginTransaction();
    try {
      __insertionAdapterOfHistory.insert(history);
      __db.setTransactionSuccessful();
    } finally {
      __db.endTransaction();
    }
  }
  
      @Override
      protected void finalize() {
        _statement.release();
      }
    });
  }
}

这是显示下划线 sql 的图像:

Here is image showing underline sql:

这是错误日志(但在我调试时不会发生):

Here are the error logs (but it not happen when I debug):

04-06 13:58:35.080 2471-2471/com.appsbharti.bharatbankdetails E/dalvikvm: Could not find class 'android.graphics.drawable.RippleDrawable', referenced from method com.google.android.material.button.MaterialButtonHelper.createBackground
04-06 13:58:35.080 2471-2471/com.appsbharti.bharatbankdetails E/dalvikvm: Could not find class 'android.graphics.drawable.RippleDrawable', referenced from method com.google.android.material.button.MaterialButtonHelper.setRippleColor
04-06 13:58:35.090 2471-2471/com.appsbharti.bharatbankdetails E/dalvikvm: Could not find class 'com.google.android.material.chip.Chip$2', referenced from method com.google.android.material.chip.Chip.initOutlineProvider
04-06 13:58:35.090 2471-2471/com.appsbharti.bharatbankdetails E/dalvikvm: Could not find class 'android.graphics.drawable.RippleDrawable', referenced from method com.google.android.material.chip.Chip.updateFrameworkRippleBackground
04-06 13:58:35.100 2471-2471/com.appsbharti.bharatbankdetails E/dalvikvm: Could not find class 'android.graphics.drawable.RippleDrawable', referenced from method com.google.android.material.chip.ChipDrawable.updateFrameworkCloseIconRipple
04-06 13:58:35.770 2471-2498/com.appsbharti.bharatbankdetails E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_2
    android.database.sqlite.SQLiteConstraintException: foreign key constraint failed (code 19)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:775)
        at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
        at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
        at androidx.sqlite.db.framework.FrameworkSQLiteStatement.executeInsert(FrameworkSQLiteStatement.java:51)
        at androidx.room.EntityInsertionAdapter.insert(EntityInsertionAdapter.java:64)
        at com.appsbharti.bharatbankdetails.Daos.HistoryDao_Impl.insert(HistoryDao_Impl.java:48)
        at com.appsbharti.bharatbankdetails.Repositories.HistoryRepository$1.run(HistoryRepository.java:41)
        at androidx.room.TransactionExecutor$1.run(TransactionExecutor.java:45)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
        at java.lang.Thread.run(Thread.java:856)
04-06 13:58:39.170 1395-1481/system_process E/ThrottleService: problem during onPollAlarm: java.lang.IllegalStateException: problem parsing stats: java.io.FileNotFoundException: /proc/net/xt_qtaguid/iface_stat_all: open failed: ENOENT (No such file or directory)

有时会抛出此错误:

--------- beginning of crash
2021-04-07 17:05:06.152 4247-4279/com.appsbharti.bharatbankdetails E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_2
    Process: com.appsbharti.bharatbankdetails, PID: 4247
    android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database (code 1032 SQLITE_READONLY_DBMOVED)
        at android.database.sqlite.SQLiteConnection.nativeExecuteForString(Native Method)
        at android.database.sqlite.SQLiteConnection.executeForString(SQLiteConnection.java:655)
        at android.database.sqlite.SQLiteConnection.setJournalMode(SQLiteConnection.java:336)
        at android.database.sqlite.SQLiteConnection.setWalModeFromConfiguration(SQLiteConnection.java:298)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:217)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:195)
        at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:503)
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:204)
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:196)
        at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:880)
        at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:865)
        at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:739)
        at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:729)
        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:355)
        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:298)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:92)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:53)
        at androidx.room.SQLiteCopyOpenHelper.getWritableDatabase(SQLiteCopyOpenHelper.java:90)
        at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
        at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
        at com.appsbharti.bharatbankdetails.Daos.HistoryDao_Impl.insert(HistoryDao_Impl.java:45)
        at com.appsbharti.bharatbankdetails.Repositories.HistoryRepository$1.run(HistoryRepository.java:37)
        at androidx.room.TransactionExecutor$1.run(TransactionExecutor.java:45)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)

当我抛出 can't fine locale en_US 错误时,我手动添加了 android_metadata 表.

I have added android_metadata table manually when I throws error of can't fine locale en_US.

CREATE TABLE "android_metadata" (
    "locale"    TEXT
);

并将 en_US 条目添加到区域设置列.

and added en_US entry to locale column.

推荐答案

好吧,所有这些错误背后都有一个简单的问题.我还没有在 synchronized 块中获取 AppDatabase.

OK there was a simple issue behind those all errors. I haven't fetched AppDatabase inside synchronized block.

在 AppDatabse 类中,我将 newInsance 方法从:

Inside AppDatabse class, I changed newInsance method from:

public static AppDatabase getInstance(final Context context) {
    dbInstance = buildDatabaseInstance(context);
    return dbInstance;
}

public static AppDatabase getInstance(final Context context) {
    if (dbInstance == null)
        synchronized (AppDatabase.class) {
            if (dbInstance == null)
                dbInstance = buildDatabaseInstance(context);
         }
//  dbInstance = buildDatabaseInstance(context);
    return dbInstance;
}

问题在于,Room 在获取查询结果时隐式调用了 getReadableDatabase() 而不是 SqliteOpenHelper 类的 getWritableDatabse().

Problem is that, Room implicitly call getReadableDatabase() not getWritableDatabse() of SqliteOpenHelperclass while fetching query results.

为了获取 BranchDetails,我在 BranchDetailsRepository 中创建了具有只读访问权限的 AppDatabase 实例.

To fetched BranchDetails, I created AppDatabase instance with read-only access inside BranchDetailsRepository.

我在 HistoryRepository 中获取了相同的只读实例(因为 AppDatbase 遵循单例模式).

I fetched same read-only instance inside HistoryRepository(as AppDatbase follows Singleton patter).

所以当我调用 insert(History history) 时,它会抛出错误.

So when I called insert(History history), it throws error.

没有外键问题.

我使用 SqliteStudio 创建的触发器也可以正常工作,没有任何问题,也没有问题.

Trigger which I created using SqliteStudio also working fine without any problem, no issue with that either.

这篇关于尽管使用房间数据库 TransactionExecutor,为什么我仍面临线程问题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆