为什么在单独的线程上执行任务时,UI 线程会被阻塞?

3
在我的应用中,我有一个ViewPager,它有大约10个页面。当应用程序首次打开时,所有页面都会被实例化并立即开始加载数据以显示。每个页面(这些是片段)都创建了一个AsyncTask来查询数据库并使用适当的数据填充自身。问题在于:尽管工作是在不同的线程上完成的,但UI在数据库查询期间停止更新(这些查询是按顺序进行的,总共需要1-3秒)。这在我的Nexus 5和旧Samsung手机上都发生过,所以我知道问题不是硬件无法跟上。

因此,我想知道为什么UI线程会被后台线程的工作阻塞。我对线程的理解是,在一个线程上做的工作不会长时间地阻塞另一个线程。如果我的理解是错误的,请解释一下。提前感谢您的帮助。

我认为这里不需要代码,但如果需要,请让我知道,我会发布相关部分。


1
UI线程真的被阻塞了吗?还是后台操作只是比您预期的时间长?Android最近默认情况下将所有AsyncTasks在单个线程上按顺序运行(因为人们做出了一些一致性错误),但您可以指定其他设置。 - Chris Stratton
在操作栏中,我有一个无限的ProgressBar来显示正在加载的内容。在第一个数据库查询开始后,它立即停止动画,并在最后一个数据库查询完成后立即重新开始动画(我知道这些事情发生的时间,感谢LogCat打印)。我也不能在选项卡之间滚动,打开溢出菜单或与我的应用程序的UI进行交互。如果有关系的话,我正在使用默认的AsyncTask执行器并行运行AsyncTasks。 - Nathan Walters
1
如果您说UI线程本身被阻塞了,那么找出它在等待什么(例如,不断点击按钮直到ANR,然后拉取并检查跟踪)。您是否有任何锁定或使用“synchronized”使AsyncTask可能会锁定UI?您是否从UI调用AsyncTask的.get()方法? - Chris Stratton
我从不调用 get()。我使用锁来控制对数据库的访问,但这些锁都是在 doInBackground() 中获取和释放的。 - Nathan Walters
1个回答

3
第一个数据库查询开始后,动画立即停止,最后一个数据库查询完成后立即重新开始动画。因此,您可能没有在后台线程上执行工作,而是在主应用程序线程上执行工作。Traceview可以帮助您识别各个线程上的操作,而StrictMode则可以帮助您解决明显的问题(磁盘I/O和网络I/O在主应用程序线程上)。在这种情况下,您可能会被如何处理工作所困扰:每个页面(它们是片段)创建一个AsyncTask来查询数据库并使用适当的数据填充自己。如果您在doInBackground()中进行查询,但没有在doInBackground()中操作结果Cursor,那么查询实际上还没有完成。Cursor是SQLiteCursor,并且它会在第一次使用数据时延迟执行查询。这是另一种“现在做事情的方式真的很酷的想法”,但实际上不太好。解决方法是在doInBackground()中调用Cursor的getCount(),以确保查询实际上是在后台线程上执行的。

我在doInBackground()中尽可能地完成所有工作,包括查询数据库并使用所得数据构造一个ListAdapter。而我在onPostExecute()中唯一要做的就是将我在doInBackground()中创建的适配器设置为我的ListView的适配器。 - Nathan Walters
我也尝试在开发者选项中启用严格模式,但是我的应用程序屏幕从未闪烁过。 - Nathan Walters
我之前检查过Choreographer警告,但从来没有看到有关丢帧的任何警告,事实上也没有任何警告。我知道没有任何阻止输入的东西,因为如果我在屏幕上快速向左右滑动,当它“冻结”时,每秒钟会绘制一帧,显示我的ViewPager已收到输入并滚动了一些。 - Nathan Walters
@NathanWalters:你的主应用程序线程不能被阻塞(否则ViewPager将无法工作)。毫无疑问,“Async Task #3”正在做很多工作,这并没有帮助你的情况,但如果它阻止了CPU处理主应用程序线程,我会预期Choreographer会出现丢帧投诉。我唯一能建议的是尝试“减轻”后台线程正在执行的任务,并查看是否有所帮助。例如,注释掉整个内容,看看是否有改善,然后慢慢重新引入后台工作。 - CommonsWare
我会尽力而为。该线程已经在继续运行时做了最少的事情,所以这将是棘手的。感谢你的帮助。 - Nathan Walters
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接