Room:无法访问主线程上的数据库,因为它可能会长时间锁定UI

Lor*_*ash 7 android

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

我的DAO包含以下代码:

@Query("SELECT * FROM member_table WHERE MemberID=:id")
Member getMemberInfo(long id);
Run Code Online (Sandbox Code Playgroud)

这是我的主要活动:

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

}

}
Run Code Online (Sandbox Code Playgroud)

这是我的活动:

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();
    }
}
}
Run Code Online (Sandbox Code Playgroud)

我想也许是因为我错过了一个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;
}
Run Code Online (Sandbox Code Playgroud)

我想我用的方法不对.有谁能够帮我?

Cab*_*zas 7

就我而言,如果您在使用协程时添加 Dispatcher.IO,它就会起作用:

viewModelScope.launch(Dispatchers.IO) {

    //your database call
}
Run Code Online (Sandbox Code Playgroud)


stk*_*ent 6

一种选择是将您的查询更新为:

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

(或类似的,使用Flowable).这样就无需手动创建自己的AsyncTask.

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

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


beg*_*ner 5

您可以使用Future和Callable。因此,您无需编写冗长的asynctask即可执行查询而无需添加allowMainThreadQueries()或使用LiveData。

我的道查询:-

@Query("SELECT * from user_data_table where SNO = 1")
UserData getDefaultData();
Run Code Online (Sandbox Code Playgroud)

我的存储库方法:-

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();
}
Run Code Online (Sandbox Code Playgroud)


小智 5

对我来说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
}
Run Code Online (Sandbox Code Playgroud)