调用 removeCallbacks() 时,在新线程上启动的可运行对象不会停止
Posted
技术标签:
【中文标题】调用 removeCallbacks() 时,在新线程上启动的可运行对象不会停止【英文标题】:runnable that started on a new thread does not stop when removeCallbacks() is called 【发布时间】:2013-10-06 09:02:22 【问题描述】:我有以下代码。
//This is global for the activity.
Handler handler = new Handler()
@Override
public void handleMessage(Message msg)
//update the UI
;
在我的 onResume() 中,我正在启动一个运行可运行对象的新线程。
protected void onResume()
super.onResume();
Thread t = new Thread(runnable);
t.start();
我的runnable如下:
Runnable runnable = new Runnable()
public void run()
// some networking stuff
// some db stuff
// whatever
handler.sendEmptyMessage(0);
handler.postDelayed(new Runnable()
public void run()
new Thread(runnable).start();
, 30000);
我在 onPause() 里面有这个:
protected void onPause()
super.onPause();
handler.removeCallbacks(runnable);
最后我调用handler.sendEmptyMessage(0);
,以便调用handleMessage(Message msg)
并更改UI,我重复任务但启动一个新的runnable,它启动一个运行与此相同的runnable的新线程.
澄清问题:
我正在我的 onResume() 中启动一个新线程,这意味着可运行对象没有在 UI 线程上运行,但是,处理程序是在 UI 线程上创建的,并且自然附加到 UI 线程. UI 是如何完美改变的?
它应该用于 handler.removeCallbacks(runnable),但是,每当我最小化应用程序时,runnable 仍然每 30 秒运行一次。 (这可能是因为它在一个新线程上,与创建的处理程序无关)。我怎样才能让它停止?
【问题讨论】:
对于 2。我想说这是因为您可能没有删除同一个实例。看到您在每个handler.postDelayed
中创建一个新的 Runnable
实例,但您似乎只删除了一个共享实例。
啊哈,可能是。有什么诀窍?
不要创建新的可运行文件。使用this
可运行。并失去额外的线程。把那行写成handler.postDelayed(this, 30000);
我认为您值得仔细阅读 Handler 和 Runnable 的文档。您的代码看起来就像您已经组装了一些您找到的代码示例,但没有完全理解它们的作用。除非在handleMessage()
中完成了大量工作,否则您将不需要新线程来执行您正在做的事情——但是,就目前而言,您无论如何都在 UI 线程上进行这项工作,因为 Handler 已绑定到实例化它的线程。
@323go 100%。我是一个新手,把这些东西从我在这里和那里读到的点点滴滴中。你是对的,主要的事情发生在线程内部。您能否将代码引导到我正在做的“我的想法”?关于在 UI 线程上做的工作,我想我不是,因为我没有得到任何 networkOnMainThread 异常。
【参考方案1】:
public class MainActivity extends Activity
public static final int UPDATE = 1;
public static final int WORK = 2;
private Handler uiHandler = new Handler()
@Override
public void handleMessage(Message msg)
switch (msg.what)
case UPDATE:
// Perform UI updates here
....
// UI Updates done, schedule WORK in 30 seconds:
this.sendMessageDelayed(this.obtainMessage(WORK), 30000);
break;
case WORK:
new Thread(doWork).start();
break;
default:
super.handleMessage(msg);
;
private WeakReference<Handler> handlerRef = new WeakReference<Handler>( uiHandler );
private Runnable doWork = new Runnable()
@Override
public void run()
// This will run on a different thread.
// If UI is still around, tell it to update
Handler ui = handlerRef.get();
if( ui != null )
ui.sendEmptyMessage(MainActivity.UPDATE);
;
@Override
protected void onPause()
uiHandler.removeMessages(WORK);
super.onPause();
@Override
protected void onResume()
super.onResume();
// Resume UI updates in 500ms, allowing UI to settle
uiHandler.sendMessageDelayed(uiHandler.obtainMessage(WORK), 500);
....
此模式在 UI 线程上使用单个处理程序。后台工作在 Runnable 中完成,ui 处理程序将发布到新线程,因此避免 NetworkOnMainThreadException
和 - 更重要的是 - 无响应的 UI。此外,在后台进程完成后 30 秒安排新的更新,以避免长时间运行的更新对系统造成负担。后台线程使用WeakReference
到ui 处理程序,所以如果Activity
在线程工作时被杀死,它不会向它发送UI 更新。
【讨论】:
在初始测试中,代码运行完美。非常感谢。高度赞赏。 我还应该注意到,如果您在多个活动中使用相同的模式,最好将其卸载到服务,然后在更新完成后提交广播。可以使用 Binder 代替广播,但稍微复杂一些。 不,这只会发生在我的主要活动中。 (每 1 分钟)。顺便说一句,this.obtainMessage(WORK) 的诀窍是什么,您这样做是为了避免创建新的 Bundle、Message 并像这样发送 Message? Bundles 和 Messages 不是一回事(但在功能上非常相似)。obtainMessage(int)
只是在线程上创建消息然后设置what
的一种非常简单的方法。 API 提供的不错的小快捷方式。
我知道它们是同一个东西,但是你需要一个 Bundle 来把它放在 Message 中。 - 无论如何 - 我不知道这种方法。这绝对是一个不错的小捷径,可以快速完成这项工作。以上是关于调用 removeCallbacks() 时,在新线程上启动的可运行对象不会停止的主要内容,如果未能解决你的问题,请参考以下文章
jqgrid postData setGridParam 调用多次时查询条件累加的问题--详情页查询导致的无法在新的页面中查询