从服务中访问UI线程处理程序

96

我正在尝试在Android上尝试一些新的东西,因此需要访问UI线程的处理程序。

我知道以下内容:

  1. UI线程有自己的处理程序和循环器
  2. 任何消息都会被放入UI线程的消息队列中
  3. Looper接收事件并将其传递给处理程序
  4. 处理程序处理消息并将特定事件发送到UI

我想要一个普通服务,该服务必须获取UI线程处理程序并将消息放入此处理程序中。 这样,该消息将被处理并发出到UI。 这里的服务将是由某个应用程序启动的普通服务。

我想知道是否可能。 如果可以,请提供一些代码片段,以便我可以尝试。

问候 Girish

7个回答

187

以下代码片段构建了一个与主(UI)线程相关联的Handler:

Handler handler = new Handler(Looper.getMainLooper());

然后,您可以像这样发布在主(UI)线程上执行的内容:

handler.post(runnable_to_call_from_main_thread);

如果处理程序本身是从主(UI)线程创建的,则可以省略参数以增加简洁性:

Handler handler = new Handler();

关于进程和线程的信息,可以查看Android开发指南


3
测试过了,效果很棒!一个使用场景示例:我有一个网络接口,在设备上直接运行服务。由于接口可以直接与UI进行交互,而且服务器需要在自己的线程上运行,我需要一种方法从Activity之外的地方触发UI线程。你描述的方法非常好用。 - mrPjer
完美 ^^ 我刚刚用它来从流式服务更新我的用户界面。正是我所需要的,谢谢! - An-droid
你知道我是否可以创建一个 Handler 的单例实例,并在每次需要在 UI 线程上运行某些东西时使用它吗? - HelloWorld
我想我们永远不会知道。 - Denny
@HelloWorld 是的,假设 Looper.getMainLooper() 返回的 Looper 没有改变,那应该可以工作。你试过了吗? - volley
@volley 我不记得了,但我认为说它是否有效并不重要。要确保它始终有效,Android文档应明确说明,或者应检查源代码以查看是否可以推断出来 :) - HelloWorld

28
在你的Handler上创建一个附加的Messenger对象,并将其传递给Service(例如,在startService()Intent extra中)。然后,Service可以通过MessengerHandler发送Message。这里有一个演示此过程的sample application,你可以参考此链接。

@iLikeAndroid:据我所知,如果你没有创建Handler,就无法访问它。 - CommonsWare
据我所知,Android SDK 中没有 ViewRoot。@iLikeAndroid - CommonsWare
@CommonsWare - 我知道这是一篇旧文章,但如果Activity只绑定到已经启动的服务上,你会怎么做呢?将这个相同的handler发送给绑定到服务的Activity是否是一个好的实践呢? - hadez30
1
@hadez30:就我个人而言,我不太常使用绑定服务。但你仍然可以使用Handler/Messenger,不过我会用事件总线(例如greenrobot的EventBus)来替换所有这些。 - CommonsWare
我明白了,谢谢你,CommonsWare。一直在尝试避免在活动之间传递处理程序。我想这是正确的方法。最终会尝试使用我一直听说的EventBus。 - hadez30
显示剩余2条评论

5

我建议尝试以下代码:

    new Handler(Looper.getMainLooper()).post(() -> {

        //UI THREAD CODE HERE



    });

需要一些额外的解释来帮助原帖作者。 - Moog

4

目前我更喜欢使用事件总线库,例如Otto来解决这种问题。只需订阅所需的组件(活动):

protected void onResume() {
    super.onResume();
    bus.register(this);
}

然后提供一个回调方法:

public void onTimeLeftEvent(TimeLeftEvent ev) {
    // process event..
}

当您的服务执行以下语句时:

bus.post(new TimeLeftEvent(340));

那个POJO将会被传递到你上面的活动和所有其他订阅组件。简单而优雅。


2

您可以通过广播接收器获取值......如下所示,首先创建您自己的IntentFilter,如下:

Intent intentFilter=new IntentFilter();
intentFilter.addAction("YOUR_INTENT_FILTER");

然后创建内部类BroadcastReceiver,如下所示:

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    /** Receives the broadcast that has been fired */
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction()=="YOUR_INTENT_FILTER"){
           //HERE YOU WILL GET VALUES FROM BROADCAST THROUGH INTENT EDIT YOUR TEXTVIEW///////////
           String receivedValue=intent.getStringExtra("KEY");
        }
    }
};

现在,在onResume()中注册您的广播接收器,如下所示:

registerReceiver(broadcastReceiver, intentFilter);

最后,在onDestroy()中取消注册广播接收器,如下所示:

unregisterReceiver(broadcastReceiver);

现在最重要的部分是...您需要从需要发送数据的任何地方触发广播...请执行以下操作:
Intent i=new Intent();
i.setAction("YOUR_INTENT_FILTER");
i.putExtra("KEY", "YOUR_VALUE");
sendBroadcast(i);

....cheers :)


1

kotlin 中,您可以这样做

假设您想从服务中显示 Toast 消息

val handler = Handler(Looper.getMainLooper())
handler.post {
   Toast.makeText(context, "This is my message",Toast.LENGTH_LONG).show()
}

0

解决方案:

  1. 创建一个带有主线程的HandlerLooper:requestHandler
  2. 从主线程创建一个带有LooperHandler:responseHandler,并覆盖handleMessage方法
  3. 在requestHandler上发布一个Runnable任务
  4. Runnable任务中,调用responseHandler上的sendMessage
  5. 这个sendMessage结果会调用responseHandler中的handleMessage。
  6. Message获取属性并处理它,更新UI

示例代码:

    /* Handler from UI Thread to send request */

    Handler requestHandler = new Handler(Looper.getMainLooper());

     /* Handler from UI Thread to process messages */

    final Handler responseHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {

            /* Processing handleMessage */

            Toast.makeText(MainActivity.this,
                    "Runnable completed with result:"+(String)msg.obj,
                    Toast.LENGTH_LONG)
                    .show();
        }
    };

    for ( int i=0; i<10; i++) {
        Runnable myRunnable = new Runnable() {
            @Override
            public void run() {
                try {
                   /* Send an Event to UI Thread through message. 
                      Add business logic and prepare message by 
                      replacing example code */

                    String text = "" + (++rId);
                    Message msg = new Message();

                    msg.obj = text.toString();
                    responseHandler.sendMessage(msg);
                    System.out.println(text.toString());

                } catch (Exception err) {
                    err.printStackTrace();
                }
            }
        };
        requestHandler.post(myRunnable);
    }

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