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


89

我正在尝试Android上的一些新功能,需要访问UI线程的处理程序。

我知道以下几点:

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

我想拥有必须获取UI线程处理程序并将消息放入此处理程序的服务。这样就可以处理此消息并将其发布到UI。在这里,该服务将是由某些应用程序启动的普通服务。

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

关于吉里什

Answers:


179

此代码段构造了一个与主(UI)线程关联的Handler:

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

然后,您可以在主(UI)线程中发布要执行的内容,如下所示:

handler.post(runnable_to_call_from_main_thread);

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

Handler handler = new Handler();

有关进程和线程的《Android开发指南》提供了更多信息。


2
测试了一下,效果很好!一个用例的例子:我有一个Web界面,该界面由直接在设备上运行的服务器提供服务。由于可以使用该界面直接与UI进行交互,并且由于服务器需要在其自己的线程上运行,因此我需要一种从Activity外部触摸UI线程的方法。您描述的方法效果很好。
mrPjer 2012年

1
辉煌。作品就像一种魅力,非常有用。谢谢。
JRun,

完美^^刚刚用它来从StreamingService更新我的用户界面。正是我需要的谢谢!
An-droid 2014年

你知道我是否可以创建一个处理程序的单例实例,并在每次需要在ui线程上运行某些东西时使用它吗?
HelloWorld '18

我想我们永远不会知道
丹尼

28

创建一个Messenger连接到你的对象Handler,并传递MessengerService(例如,在Intent额外的startService())。然后,Service可以通过将传送Message到。这是一个演示此情况的示例应用程序HandlerMessenger


感谢您的提示。这很有帮助。请参阅以下堆栈,了解到我的活动MyDemo.dispatchTouchEvent(MotionEvent)行的触摸事件流:20 PhoneWindow $ DecorView.dispatchTouchEvent(MotionEvent)行:1696 ViewRoot.handleMessage(Message)行:1658 ViewRoot(Handler).dispatchMessage(Message) )行:99 Looper.loop()行:123 //事件处理从此处开始ActivityThread.main(String [])行:4203这里,ViewRoot是一个Handler。我想获取此处理程序的引用...是否可以从我的应用程序中获取它?
iLikeAndroid 2011年

@iLikeAndroid:如果您未创建Handler,则无法访问AFAIK。
CommonsWare,

谢谢。我试图创建ViewRoot的实例。这不过是一个处理程序。现在,我可以在此处理程序上发出消息了。处理程序正在获取消息。但是,由于未正确初始化,因此ViewRoot无法处理该消息。我需要调用ViewRoot.setView()来初始化ViewRoot的正确数据。我想知道是否可以使用默认视图或基本视图等进行初始化?
iLikeAndroid 2011年

@iLikeAndroid:ViewRootAndroid SDK中没有AFAICT。
CommonsWare

1
@ hadez30:就我个人而言,我很少使用绑定服务。仍然欢迎您使用Handler/ Messenger,尽管我将全部替换为事件总线(例如,greenrobot的EventBus)。
CommonsWare,2015年

4

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

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

然后提供一个回调方法:

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

然后在您的服务执行如下语句时:

bus.post(new TimeLeftEvent(340));

该POJO将传递到您的上述活动和所有其他订阅组件。简洁大方。


3

我建议尝试以下代码:

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

        //UI THREAD CODE HERE



    });

需要一些其他说明来帮助OP。
Moog

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()中取消注册BroadcastReceiver为,

unregisterReceiver(broadcastReceiver);

现在最重要的部分...您需要从任何需要发送值的地方触发广播.....

Intent i=new Intent();
i.setAction("YOUR_INTENT_FILTER");
i.putExtra("KEY", "YOUR_VALUE");
sendBroadcast(i);

....欢呼:)


1

kotlin就是你怎么做

假设您要显示服务中的Toast消息

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

0

解:

  1. 从主线程使用Looper创建处理程序:requestHandler
  2. 从主线程创建一个Handlerwith Looper:responseHandler和override 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);
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.