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

时间:2018-06-24 19:14:28

标签: android

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

我的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;
}

我认为我使用的方法错误。有人可以帮我吗?

4 个答案:

答案 0 :(得分:6)

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

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

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

Member类型周围返回LiveData包装器会自动向Room发出信号,表示可以/应该异步执行查询。根据{{​​3}}(我的重点):

  

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

答案 1 :(得分:3)

您可以使用Future和Callable。因此,您无需编写冗长的asynctask即可执行查询而无需添加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();
}

答案 2 :(得分:2)

对我来说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
}

答案 3 :(得分:0)

在这里,使用Future和Callables可以替代。通过使用Future和Callable,您可以摆脱AsyncTask并将查询强制到主线程。

语法如下-

@Throws(ExecutionException::class, InterruptedException::class)
private fun canContinue(id: String): UserData{

    val callable = Callable { userDao.getDefaultData() }

    val future = Executors.newSingleThreadExecutor().submit(callable)

    return future!!.get()
}

而且,不要忘记对返回的数据进行空检查。因为它可能为空