2

我正在从我的活动中启动一个新线程,该线程执行 10 秒操作,然后向 UI 报告runOnUiThread()

在 10 秒的操作期间,UI 变得无响应并且不响应任何用户交互。在这种情况下,我试图使用工具栏中的按钮关闭活动。引发ANR错误,但在工作线程完成后处理按钮单击。

虽然,当线程工作时,应用程序仍然能够显示旋转ProgressBar,如果工作在 UI 线程上完成,则不会发生这种情况。

显示ProfilerUI 线程在此工作期间处于休眠状态,所以据我了解它应该是响应式的?我尝试过使用AsyncTask,但这也不起作用。无论如何,这里有一些代码:

Thread当窗口成为焦点时开始新的:

活动:

  @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);

        if(hasFocus && !recyclerSetup){
            progressBar.setIndeterminate(true);
            progressBar.setVisibility(View.VISIBLE);
            WorkThread thread = new WorkThread();
            thread.start();
        }
    }

线:

  private class WorkThread extends Thread {

        @Override
        public void run() {
            getViewModelAndWords();
            runOnUiThread(() -> setupRecycler());
        }
    }

  private void getViewModelAndWords() {
        viewModel = ViewModelProviders.of(this).get(WordViewModel.class);
        adapter = new WordDetailedAdapter(this, viewModel, this, this, !favGroup.equals(ANY_WORD_PARAM));
        allWords = viewModel.getAllWords();
    }

我不确定这是否viewModel与问题有关,但它是viewModel.getAllWords()执行繁重的 10 秒 Room db 操作的方法。

这是Profiler显示睡眠UI 线程和工作线程Thread(AsyncTask #6)的快照:

在此处输入图像描述

编辑:

好的,所以我认为问题在于房间数据库操作 / viewModelgetAllWords()用释放的 UI 线程替换内容以Thread.sleep(10000);进行用户交互,因此以下代码(出于某种原因)阻止了用户输入:

编辑2:

正如建议的那样,我现在使用onPostExecute()一个接口来检索单词:

 public static class GetAllWordsWithCallBackTask extends AsyncTask<Void, Void, List<Word>>{

        WordViewModel.iGetWords listener;
        WordDao wordDao;

        public GetAllWordsWithCallBackTask(WordDao wordDao, WordViewModel.iGetWords listener) {
            this.listener = listener;
            this.wordDao = wordDao;
        }

        @Override
        protected List<Word> doInBackground(Void... voids) {
            return wordDao.getAllWords();
        }

        @Override
        protected void onPostExecute(List<Word> words) {
            listener.gotWords(words);
        }
    }

get()已被删除,我只是执行任务,传入listener处理回调:

  public void getAllWordsWithCallBack(WordViewModel.iGetWords listener) {
        try {
            new GetAllWordsWithCallBackTask(wordDao, listener).execute();
        } catch (Exception e) {
            Crashlytics.log("Getting all words exception: "+e.getMessage());
            e.printStackTrace();
        }
    }

这很好用,单词成功返回到我的活动,但在执行操作时UI 仍然无响应。

4

2 回答 2

1

编辑 1 您调用.get()AsyncTask。调用线程等待 AsyncTask 完成。您可以实现接口回调来解决此问题。

这是您的问题的解决方案

编辑 2:
我仔细查看了您的代码,同样,您在此处发布的代码没有错误。

使用带有回调的 AsyncTask 是一种可能的解决方案。您的代码在后台线程中运行,结果被传递给主线程而不阻塞它。

我认为您的错误在于将数据从回调传输到 ViewModel 或 MainActivity。
解决此问题的最佳解决方案是使用LiveData

我试图尽可能地重建你的代码。也许它会帮助你找到错误。

字数据库

@Database(entities = {Word.class}, version = 3)
@TypeConverters(DateConverter.class)
public abstract class WordDb extends RoomDatabase {

    private static WordDb INSTANCE;

    public abstract WordDao wordDao();

    static synchronized WordDb getInstance(Context contextPassed){
        if(INSTANCE == null){
            INSTANCE = Room.databaseBuilder(contextPassed.getApplicationContext(),WordDb.class,"word_db")
                    .fallbackToDestructiveMigration()
                    .build();
        }
        return INSTANCE;
    }
}

WordRepo

class WordRepo {
    private WordDao wordDao;

    WordRepo(Context applicationContext) {
        WordDb wordDb = WordDb.getInstance(applicationContext);
        wordDao = wordDb.wordDao();
    }

    void getAllWords(WordRepo.iGetWords listener) {
        try {
            Log.i("WordRepo", String.format("getAllWords() called from %s", Thread.currentThread().getName()));
            new GetAllWordsWithCallBackTask(wordDao, listener).execute();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static class GetAllWordsWithCallBackTask extends AsyncTask<Void, Void, List<Word>> {

        WordRepo.iGetWords listener;
        WordDao wordDao;

        GetAllWordsWithCallBackTask(WordDao wordDao, WordRepo.iGetWords listener) {
            this.listener = listener;
            this.wordDao = wordDao;
        }

        @Override
        protected List<Word> doInBackground(Void... voids) {

            Log.i("WordRepo", String.format("GetAllWordsWithCallBackTask.doInBackground() called from %s", Thread.currentThread().getName()));
            return wordDao.getAll();
        }

        @Override
        protected void onPostExecute(List<Word> words) {
            Log.i("WordRepo", String.format("GetAllWordsWithCallBackTask.onPostExecute() called from %s", Thread.currentThread().getName()));

            listener.gotWords(words);
        }
    }

    public interface iGetWords {
        void gotWords(List<Word> words);
    }
}

主视图模型

public class MainViewModel extends AndroidViewModel {

    MutableLiveData<List<Word>> wordList = new MutableLiveData<>();
    private static final String TAG = "MainViewModel";

    public MainViewModel(@NonNull Application application) {
        super(application);
    }

    void getAllWords() {

        Log.i(TAG, String.format("getAllWords() called from %s", Thread.currentThread().getName()));
        WordRepo repo = new WordRepo(getApplication());

        repo.getAllWords(new WordRepo.iGetWords() {
            @Override
            public void gotWords(List<Word> words) {
                wordList.setValue(words);
            }
        });
    }
}

MainActivity 中的 getViewModelAndWords()

    private void getViewModelAndWords() {

        Log.i(TAG, String.format("getViewModelAndWords() called from %s", Thread.currentThread().getName()));

        viewModel = ViewModelProviders.of(this).get(MainViewModel.class);

        viewModel.wordList.observe(this, new Observer<List<Word>>() {
            @Override
            public void onChanged(List<Word> words) {
                //Do something with youre result
                Log.i(TAG, String.format("viewModel.wordList livedata returned %d results", words != null ? words.size() : -1));
            }
        });

        viewModel.getAllWords();


        Log.i(TAG, "viewModel.getAllWords() done");


    }

如果您发现您的代码出了什么问题,请发表评论

正如@mayokun 已经提到的,我建议使用 RxJava 或将您的项目迁移到 Kotlin + Coroutines 以保持您的代码干净整洁。

在这里你可以找到更多:

Medium - Android 上的协程(第一部分):获取背景

CodeLabs - 在您的 Android 应用程序中使用 Kotlin 协程

中 - RxAndroid 基础:第 1 部分

中 - RxJava VS。两个用例中的协程

我已经用大约 300,000 条记录成功地测试了这段代码。运行此操作已阻止我的模拟器上的异步任务约 30 秒。在此过程中可以访问主线程。

我希望这一次也对你有用

于 2019-11-13T21:48:15.747 回答
1
  return new GetAllWordAsyncTask(wordDao).execute().get();

通过调用get(),您将强制当前调用线程同步等待结果返回,这会使您的后台查询在执行时阻塞主线程。

解决方案是使用回调onPostExecute而不是阻塞主线程来获取查询结果。

于 2019-11-14T18:35:14.883 回答