【问题标题】:What am I doing wrong? Trying to retrieve data from SQLite within a AsyncTask, but it crashes我究竟做错了什么?尝试从 AsyncTask 中的 SQLite 检索数据,但它崩溃了
【发布时间】:2019-06-10 19:48:13
【问题描述】:

我目前正在使用 Android - Room Persistence Library,并尝试从 SQLite 数据库中检索非实时数据,然后需要根据 Firestore Cloud 中的数据对其进行处理和检查。所有这些都在 AsyncTask 中运行,但是当我尝试从 Room 检索数据时,出现以下错误:

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

我知道我不能从主线程调用数据库方法,但所有这些都是从 AsyncTask 调用的,它应该处理我上面所说的所有内容。我做错了什么?

我试图在我的 MainRepository 中创建一个 asyncTask,简而言之,它看起来像这样:

public List<Shift> getAllShiftsForJobExport(int jobId){
        try {
            return new GetShiftsForJobAsync(shiftDao).execute(jobId).get();
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        }
    }

这并不是一直有效,最终给出了一些奇怪的结果......

我试图从 Room 检索数据并与 Firestore 云进行比较的 AsyncTask 如下所示:

private static class DownloadDataFromCloud extends AsyncTask<Void, Void, Void> {
        private CloudViewModel viewModel;
        private FirebaseFirestore db;
        private String userId;

        DownloadDataFromCloud(FragmentActivity activity){
            viewModel = ViewModelProviders.of(activity).get(CloudViewModel.class);
        }

        private void downloadData(){
            List<Job> jobs = viewModel.getAllJobs();
            Map<String, Job> jobDocuments = new HashMap<>();
            for(Job job : jobs){
                if(job.getDocumentId() != null){
                    jobDocuments.put(job.getDocumentId(), job);
                }
            }
            CollectionReference jobsCollection = db.collection("jobs");
            jobsCollection.whereEqualTo("userId", userId)
                    .get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                @Override
                public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
                    for(QueryDocumentSnapshot documentSnapshot : queryDocumentSnapshots){
                        Job job = new Job();
                        job.convertCloudToObject(documentSnapshot);

                        if(jobDocuments.containsKey(job.getDocumentId())){
                            job.setId(jobDocuments.get(job.getDocumentId()).getId());
                            viewModel.updateJob(job);
                            downloadShiftData(job.getId(), job.getDocumentId());
                        }else {
                            viewModel.insertJobWithReturn(job, new AsyncResponse<Long>() {
                                @Override
                                public void processFinish(Long output) {
                                    long jobId = output;
                                    downloadShiftData((int) jobId, job.getDocumentId());
                                }
                            });
                        }
                    }
                }
            });
        }

        private void downloadShiftData(int jobId, String documentId){
            List<Shift> shifts = viewModel.getAllShiftForJob(jobId);
            Map<String, Shift> shiftDocuments = new HashMap<>();
            for(Shift shift : shifts){
                if(shift.getDocumentId() != null){
                    shiftDocuments.put(shift.getDocumentId(), shift);
                }
            }
            CollectionReference shiftCollection = db.collection("shifts");
            shiftCollection.whereEqualTo("userId", userId)
                    .whereEqualTo("jobDocumentId", documentId)
                    .get().addOnSuccessListener(queryDocumentSnapshots -> {
                        for (QueryDocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
                            Shift shift = new Shift();
                            shift.convertCloudToObject(documentSnapshot);
                            if(shiftDocuments.containsKey(shift.getDocumentId())){
                                shift.setId(shiftDocuments.get(shift.getDocumentId()).getId());
                                viewModel.updateShift(shift);
                            }else {
                                shift.setJobId(jobId);
                                viewModel.insertShift(shift);
                            }
                        }
                    });
        }

        @Override
        protected Void doInBackground(Void... voids) {
            downloadData();
            return null;
        }

        @Override
        protected void onPreExecute() {
            db = FirebaseFirestore.getInstance();
            userId = FirebaseAuth.getInstance().getUid();
            super.onPreExecute();
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
    }

我得到的完整堆栈跟踪是:

java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
        at androidx.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:228)
        at androidx.room.RoomDatabase.query(RoomDatabase.java:255)
        at androidx.room.util.DBUtil.query(DBUtil.java:54)
        at com.*.daos.ShiftDao_Impl.getAllShiftsForJobExport(ShiftDao_Impl.java:909)
        at com.*.MainRepository.getAllShiftsForJobExport(MainRepository.java:203)
        at com.*.CloudViewModel.getAllShiftForJob(CloudViewModel.java:33)
        at com.*.CloudHandler$DownloadDataFromCloud.downloadShiftData(CloudHandler.java:216)
        at com.*.CloudHandler$DownloadDataFromCloud.access$300(CloudHandler.java:171)
        at com.*.CloudHandler$DownloadDataFromCloud$1.onSuccess(CloudHandler.java:200)
        at com.*.CloudHandler$DownloadDataFromCloud$1.onSuccess(CloudHandler.java:190)
        at com.google.android.gms.tasks.zzn.run(Unknown Source:4)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

编辑 - GetShiftsForJobAsync 的代码:

private static class GetShiftsForJobAsync extends AsyncTask<Integer, Void, List<Shift>>{

        private ShiftDao shiftDao;

        GetShiftsForJobAsync(ShiftDao shiftDao){
            this.shiftDao = shiftDao;
        }

        @Override
        protected List<Shift> doInBackground(Integer... integers) {
            return shiftDao.getAllShiftsForJobExport(integers[0]);
        }
    }

【问题讨论】:

  • 将您的代码添加到您尝试获取数据的位置。您的错误清楚地表明您正在主线程上进行操作
  • @VedprakashWagh 我很快就发帖了,现在都更新了。
  • 顺便说一句,您不需要添加 AsyncTask 来从 Firebase 获取数据。您可以在 UI 线程上直接运行代码以从 Firebase 数据库中获取数据。它在内部完成所有工作
  • @VedprakashWagh 我在 AsyncTask 中运行它的原因是因为我也在访问数据库,但它仍然给我错误,我不明白......
  • GetShiftsForJobAsync 的代码在哪里?

标签: android android-asynctask android-room


【解决方案1】:

AsyncTask 上使用get() 时,您将同步 执行它。这将在主线程上生成应该在其他线程上执行的工作。尝试从调用函数中删除get()

根据documentation

如有必要,等待计算完成,然后检索其结果。

【讨论】:

  • 我已经阅读了这个材料,但是即使它是从 AsyncTask 调用的,它仍然在主线程上工作吗?这就是我正在做的事情。我从 AsyncTask 开始一个方法,因为我想处理网络操作,并且在该 AsyncTask 中,我需要数据库中的 List 以便可能从云上传/下载数据。我还尝试在没有我在编辑中添加的 AsyncTask 的情况下运行数据库方法,但即使它在后台线程中运行,我仍然会收到错误消息
  • 问题不是 AsyncTask 中的代码,而是 AsyncTask 调用本身。由于它有一个 get() ,它将在同一个调用线程上执行(我想是主 UI 线程,因为它中断了)。尝试删除 get(),以便调用在它自己的分离线程上执行。您仍然可以在 postExecute() 方法上处理 UI 线程。
  • 我会在我回到家后尝试一下,但只是为了澄清一下。即使我在我的 GetShiftsForJobAsync 类上使用 get() 方法,该方法是从我的 DownloadDataFromCloud 类调用的,那么 get() 仍然在主线程上执行?我可能对 AsyncTask 的工作原理有完全错误的想法......
  • 可能是 onSuccess Firestore 回调是主 UI 线程。要进行快速测试,您可以使用 Anko 的 doAsync{} 在另一个线程上执行该块。
  • 哦,没想到,我以后看看,让你知道我的发现!
猜你喜欢
  • 2023-01-19
  • 2016-12-01
  • 2013-08-06
  • 1970-01-01
  • 2016-07-18
  • 1970-01-01
  • 2019-12-23
  • 1970-01-01
  • 2014-06-15
相关资源
最近更新 更多