调用 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() 时,在新线程上启动的可运行对象不会停止的主要内容,如果未能解决你的问题,请参考以下文章

转Android中removeCallbacks失效原因

C# 在新线程中调用方法

jqgrid postData setGridParam 调用多次时查询条件累加的问题--详情页查询导致的无法在新的页面中查询

C++ 单线程工作,在新线程上它调用“abort()”

在新页面上加载 ajax 成功数据

在新实例中重新定义类属性(在getter函数调用之前)失败