我正在从我的 Activity 中启动一个新线程,该线程执行 10 秒的操作,然后使用 runOnUiThread()
向 UI 报告
在 10 秒操作期间,UI 变得无响应并且不响应任何用户交互。在本例中,我尝试使用工具栏中的按钮关闭 Activity 。抛出 ANR 错误,但按钮单击会在工作线程完成后处理。
尽管线程正在工作时,应用程序仍然能够显示旋转的 ProgressBar
,如果工作在 UI 线程上完成,则不会发生这种情况。
Profiler
显示 UI 线程在此工作期间正在 hibernate ,因此根据我的理解,它应该具有响应能力?我尝试使用 AsyncTask
代替,但这也不起作用。无论如何,这里有一些代码:
当窗口成为焦点时,新的线程
启动:
Activity :
@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 秒的繁重操作房间数据库操作。
这是 Profiler
的快照,显示 sleep UI 线程和辅助线程
(AsyncTask #6):
编辑:
好的,所以我认为问题出在房间数据库操作/viewModel
内。将 getAllWords()
的内容替换为 Thread.sleep(10000);
可以释放 UI 线程以供用户交互,因此下面的代码(对于某些原因)阻止用户输入:
编辑2:
按照建议,我现在使用 onPostExecute()
以及一个接口(interface)来检索单词:
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();
}
}
效果很好,单词已成功返回到我的 Activity,但在执行操作时UI 仍然没有响应。
最佳答案
编辑 1
您对 AsyncTask 调用 .get()
。调用线程等待 AsyncTask 完成。您可以实现接口(interface)回调来解决这个问题。
Here is a solution for you're problem
编辑2:
我仔细查看了您的代码,再次发现您在此处发布的代码中没有错误。
将 AsyncTask 与回调结合使用是一种可能的解决方案。您的代码在后台线程中运行,结果将传递到主线程而不阻塞它。
我认为您的错误在于将数据从回调传输到 ViewModel 或 MainActivity。
解决这个问题的最佳解决方案是使用 LiveData 。
我尝试尽可能地重建您的代码。也许它会帮助你找到错误。
WordDb
@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);
}
}
MainViewModel
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 - Coroutines on Android (part I): Getting the background
CodeLabs - Using Kotlin Coroutines in your Android App
Medium - RxAndroid Basics: Part 1
Medium - RxJava VS. Coroutines In Two Use Cases
我已经成功地用大约 300,000 条记录测试了这段代码。运行此操作已阻止我的模拟器上的异步任务大约 30 秒。在此过程中主线程是可访问的。
我希望这一次也适合您
关于java - Android:另一个线程导致 UI 无响应?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58844492/