当前位置: 首页 > 知识库问答 >
问题:

房间:无法访问主线程上的数据库,因为它可能会长时间锁定用户界面

袁高明
2023-03-14

在主活动中,我有LiveData,其中包含成员和单击侦听器。如果我点击一个成员,那么他的ID将通过intent.putExtra。该ID稍后会传递给在此活动中打开的方法。有了这个活动,我想看看一个会员的详细情况。在我的MemberInfo活动中,我标出了我的问题所在的一行。它显示了这个错误:无法访问主线程上的数据库,因为它可能会锁定用户界面很长一段时间。

我的DAO由以下代码组成:

@Query("SELECT * FROM member_table WHERE MemberID=:id")
Member getMemberInfo(long id);

这是我的主要活动:

public class MemberMainActivity extends AppCompatActivity implements MemberListAdapter.MemberClickListener{

private MemberViewModel mMemberViewModel;
private List<Member> mMember;

void setMember(List<Member> members) {
    mMember = members;
}

public static final int NEW_MEMBER_ACTIVITY_REQUEST_CODE = 1;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_member);
    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent(MemberMainActivity.this, NewMemberActivity.class);
            startActivityForResult(intent, NEW_MEMBER_ACTIVITY_REQUEST_CODE);
        }
    });

    RecyclerView recyclerView = findViewById(R.id.recyclerviewcard_member);
    final MemberListAdapter adapter = new MemberListAdapter(this);
    recyclerView.setAdapter(adapter);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));

    mMemberViewModel = ViewModelProviders.of(this).get(MemberViewModel.class);

    mMemberViewModel.getAllMember().observe(this, new Observer<List<Member>>() {
        @Override
        public void onChanged(@Nullable final List<Member> members) {
            mMember = members;
            // Update the cached copy of the words in the adapter.
            adapter.setMember(members);
        }
    });

}

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == NEW_MEMBER_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {
        Member member = new Member(data.getStringExtra(NewMemberActivity.EXTRA_REPLY), data.getStringExtra(NewMemberActivity.EXTRA_REPLY2));
        mMemberViewModel.insert(member);
    } else {
        Toast.makeText(
                getApplicationContext(),
                R.string.empty_not_saved,
                Toast.LENGTH_LONG).show();
    }
}

public void onMemberClick(int position) {
    Member member = mMember.get(position);
    Intent intent = new Intent(getApplicationContext(),MemberInfo.class);
    intent.putExtra("MemberID", member.getId());
    MemberInfo.open(this, member.getId());

}

}

这是我的活动:

public class MemberInfo extends AppCompatActivity {

public static void open(Activity activity, long memberid) {
    Intent intent = new Intent(activity, MemberInfo.class);
    intent.putExtra("MemberID", memberid);
    activity.startActivity(intent);
}

private List<Member> mMember;
private MemberViewModel mMemberViewModel;


void setMember(List<Member> members){
    mMember = members;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_memberinfo);

    Log.i("okay", "memberinfo");
    Intent intent = getIntent();
    if (intent != null && intent.hasExtra("MemberID")) {
        long memberid = intent.getLongExtra("MemberID", -1);
        // TODO: get customer details based on customer id
        TextView firstname = findViewById(R.id.layout_memberfirstname);
        TextView surname = findViewById(R.id.layout_membersurname);
        TextView balance = findViewById(R.id.layout_memberbalance);
        -------------Member member = MemberRoomDatabase.getDatabase().memberDao().getMemberInfo(memberid);-------------
        firstname.setText(member.getFirstname());
        surname.setText(member.getSurname());

    }
    else {
        Toast.makeText(
                getApplicationContext(),
                R.string.empty_not_saved,
                Toast.LENGTH_LONG).show();
    }
}
}

我想可能是因为我缺少了一个AsyncTask方法。我试过这个,但也没用:

private static class insertMemberInfoAsyncTask extends AsyncTask<Member, Void, Void> {

    private MemberDao mAsyncTaskDao;

    insertMemberInfoAsyncTask(MemberDao dao) {
        mAsyncTaskDao = dao;
    }

    @Override
    protected Void doInBackground(Member... params) {
        Member member = params[0];
        mAsyncTaskDao.getMemberInfo(member.getId());
        return null;
    }
}

public Member getMemberInfo(long id) {
    mAllMember = mMemberDao.getAllMember();
    Member member = mMemberDao.getMemberInfo(id);
    new insertMemberInfoAsyncTask(mMemberDao).execute(member);
    return member;
}

我想我用的方法不对。有人能帮帮我吗?

共有3个答案

夏建弼
2023-03-14

对我来说,allowMainThreadQueries()有效。

这允许在主线程上支持数据库访问。

查看以下代码

@Database(entities = [Word::class ],version = 1)
abstract class VocabularyDatabase:RoomDatabase() {

    companion object {
        private lateinit var INSTANCE:VocabularyDatabase
        fun getInstance(context:Context):VocabularyDatabase= Room.databaseBuilder(
            context,
            VocabularyDatabase::class.java,
             "vocabulary"

        )

            .createFromAsset("vocabulary.db")
            .allowMainThreadQueries()
            .build()

    }

    abstract fun dao():WordDao
}
孙正业
2023-03-14

你可以使用Future和Callable。因此,您无需编写长异步任务,并且无需添加allowMainThreadQueries()或使用LiveData即可执行查询。

我道查询:-

@Query("SELECT * from user_data_table where SNO = 1")
UserData getDefaultData();

我的仓库方法:-

public UserData getDefaultData() throws ExecutionException, InterruptedException {

    Callable<UserData> callable = new Callable<UserData>() {
        @Override
        public UserData call() throws Exception {
            return userDao.getDefaultData();
        }
    };

    Future<UserData> future = Executors.newSingleThreadExecutor().submit(callable);

    return future.get();
}
夹谷鸿福
2023-03-14

一个选项是将查询更新为:

@Query("SELECT * FROM member_table WHERE MemberID=:id")
LiveData<Member> getMemberInfo(long id);

(或类似的,使用可流动)。这样就不需要手动创建自己的AsyncTask

成员类型周围返回LiveData包装会自动向房间发出信号,表明查询可以/应该异步执行。每https://developer.android.com/training/data-storage/room/accessing-data(我的重点是):

注意:除非在生成器上调用了allowMainThreadQueries(),否则Room不支持在主线程上访问数据库,因为它可能会长时间锁定UI。返回LiveData或Flowable实例的异步查询不受此规则约束,因为它们在需要时在后台线程上异步运行查询。

 类似资料:
  • 我的协同程序在主线程上运行,这是我在协同程序上下文中指定的: 但我还是犯了这个错误: 在我上面的协程调用到我的数据库。 这里是我的: 如果我的协同程序上下文在主线程上运行,为什么仍然会出现错误?

  • 我的Coroutine运行在主线程上,我在Coroutine上下文中指定了主线程: 以下是我的: 如果我的coroutine上下文运行在主线程上,为什么我仍然得到错误?

  • 我得到了一个著名的错误但根据我的理解,我没有访问主线程中的数据库,因为我正在执行由ThreadPoolExecutor执行的Runnable中的调用。我做错了什么? 在下面的方法中,我使用runnable从网络获取数据并将其存储在本地数据库中。 数据源.保存: 执行人定义为:

  • 我正在尝试使用RxJava在后台线程上使用我的RoomDatabase。 我的DAO类: 我的实体类(为简洁起见省略了getter和setter方法): 最后,这里是我的MeasurementDatabase类: 在我的片段中,我试图在单击菜单项后插入背景线程: 最终测量数据库appDb= 我收到一个错误,说: 我的RxJava代码中缺少了什么,没有将进程放在后台线程上?

  • 我正在尝试获取消息的地图,作者id键如下: 这是我尝试过的: MessageViewModel.getAll()方法返回: 然后我将其转换为一个可流动的流(Flowable::fromIterable),这样它可以一次发出一个项目,而不是整个列表,然后我使用“toMultiMap”进行映射 on成功方法从不被调用,我不知道这里出了什么问题。如果我不使用toMultiMap(并对代码进行相应的修改)

  • 我转换java代码到kotlin代码,我得到类型未解决的java类错误。 我的Java类是 已转换的Kotlin类 非常感谢。