【问题标题】:How can a remote Service send messages to a bound Activity?远程服务如何向绑定的 Activity 发送消息?
【发布时间】:2012-05-19 19:50:51
【问题描述】:

我已阅读documentation about Bound Services,其中显示您可以轻松地通过 Messages 从 Activity 与远程(即不在同一上下文中)服务进行通信,但有什么方法将消息从Service 发送到绑定的Activity?例如,我的活动绑定到同一应用程序的正在运行的后台服务,向它发送一条消息,并在收到此消息后,服务回复一条消息给活动。我该如何实现呢?您能否指出一些解释该主题的文档?

【问题讨论】:

    标签: android android-service ipc android-binder


    【解决方案1】:

    应该可以使用 . AIDL file 就像 android billing api 一样。它是一种进行 RPC 调用(跨远程进程通信)的方法。但是您必须声明要使用的每种方法。有点像上面已经提到的界面。

    【讨论】:

      【解决方案2】:

      1) 在自己的 Binder.class 和实现 IInterface.class 对象(匿名或通过直接扩展类)的绑定器代理中实现 transact/onTransact 方法,方法是使用传入的 Parcel.class 对象
      2)将本地接口附加到自己的Binder对象 3) 创建服务并从 onBind 方法返回一个 binder 代理实现 4) 使用 bindService(ServiceConnection) 创建绑定 5) 这将导致通过在接口实现中创建的绑定返回代理绑定器

      这是一个使用内核绑定空间的 IPC 的 android 实现

      简化代码示例:

      class ServiceIPC extends Service {
      
          @Override
          public Binder onBind()  {
              return new IInterface() {
      
                  IInterface _local = this;         
      
                  @Override 
                  public IBinder asBinder() {
                     return new Binder() 
      
                                 {   
                                     //
                                     // allow distinguish local/remote impl 
                                     // avoid overhead by ipc call 
                                     // see Binder.queryLocalInterface("descriptor");
                                     //
                                     attachLocalInterface(_local,"descriptor");
                                 }
      
                                 @Override
                                 public boolean onTransact(int code,
                                                           Parcel in,
                                                           Parcel out,
                                                           int flags) 
                                         throws RemoteException {
                                     //
                                     //  your talk between client & service goes here 
                                     //
                                     return whatsoever // se super.onTransact(); 
                                 }
                            }
                  }    
      
              }.asBinder();
          }
      
      }
      

      *那么您可以在客户端和服务端使用 IBinder 的事务方法相互交谈(4 个使用奇数/前夕代码来恶心本地远程端的示例,因为我们在展位端使用相同的 onTransact 方法)

      【讨论】:

        【解决方案3】:

        注意:这仅适用于进程中的服务和活动,而不是远程问题。

        使用服务与活动通信涉及创建一个侦听器,您可以将其从活动传递给服务。

        您需要创建一个绑定到活动的服务。

        第一步是提供服务。在服务中确保你有一个 Binder 对象和返回一个 binder 对象的方法。以下是我在服务中用于检索活页夹的示例。另请注意,此绑定器有一个设置监听器的方法,该监听器将作为 BoundServiceListener 类型字段保存在服务中。

        /**
         * Class used for the client Binder.  Because we know this service always
         * runs in the same process as its clients, we don't need to deal with IPC.
         */
        public class DownloadBgBinder extends Binder {
        
            public DownloadBgService getService() {
                // Return this instance of LocalService so clients can call public methods
                return DownloadBgService.this;
            }
        
            public void setListener(BoundServiceListener listener) {
                mListener = listener;
            }
        }
        
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
        

        现在您需要创建某种接口,您可以将其传递给您的服务可以用来向其发送更新的 binder 对象。下面是我的 BoundServiceListener。

        public interface BoundServiceListener {
        
            public void sendProgress(double progress);
            public void finishedDownloading();
        }
        

        现在在您的活动中,您需要创建一个用于绑定到服务的 ServiceConnection 对象。所以添加这样的东西。

        /** Defines callbacks for service binding, passed to bindService() */
        private ServiceConnection mConnection = new ServiceConnection() {
        
            @Override
            public void onServiceDisconnected(ComponentName arg0) {
                mBound = false;
            }
        
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                // We've bound to LocalService, cast the IBinder and get LocalService instance
                DownloadBgBinder binder = (DownloadBgBinder) service;
                mService = binder.getService();
                binder.setListener(new BoundServiceListener() {
        
                    @Override
                    public void sendProgress(double progress) {
                        // Use this method to update our download progress
                    }
        
                    @Override
                    public void finishedDownloading() {
        
                    }   
                });
        
                mBound = true;
            }
        

        现在要注意的关键是

        binder.setListener(new BoundServiceListener() {
        
            @Override
            public void sendProgress(double progress) {
                // Use this method to update our download progress
            }
        
            @Override
            public void finishedDownloading() {
        
            }
        });
        

        这部分是我实际将我的 BoundServiceListener 接口发送到服务类的地方。然后服务类在此处使用该侦听器对象

            if (mListener!=null)
                mListener.finishedDownloading();
            if (mListener!=null)
                mListener.sendProgress(percent);
        

        现在您可以将它放在您的服务类中您需要的任何位置,并且您的活动将收到您的进度更新。

        还要确保在您的活动中包含以下内容以实际绑定和启动服务。

        Intent intent = new Intent(this, DownloadBgService.class);
        startService(intent);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        

        请记住,即使您绑定到服务,它也不会真正启动,直到您调用 start service。绑定到服务只是将服务连接到活动。 startService() 方法调用服务

        onStartCommand(Intent intent, int flags, int startId)
        

        还要在清单中声明您的服务

        <service android:name=".services.DownloadBgService" />
        

        在活动离开时也解除绑定服务

        @Override
        protected void onStop() {
            super.onStop();
            // Unbind from the service
            if (mBound) {
                unbindService(mConnection);
                mBound = false;
            }
        }
        

        希望这会有所帮助。

        【讨论】:

        • 很好的例子,感谢您花这么多时间来解释这个概念。
        • @Danuofr 这工作正在进行中。有没有办法跨进程边界发送任意侦听器?
        • 这个答案的唯一问题是:它仅适用于本地服务。但是这个问题被明确地问到如何使用远程服务来做到这一点。但无论如何,谢谢你写了这篇文章。但也许请注意,这不是真正问题的答案:)
        • @Danuofr 你能告诉我如何实现这样的文件上传吗?
        【解决方案4】:

        Remote Messenger Service Sample 的参考文档中找到示例。

        【讨论】:

        • 现实世界中是否存在我们需要远程服务绑定的情况?
        猜你喜欢
        • 2011-12-31
        • 1970-01-01
        • 1970-01-01
        • 2021-02-05
        • 1970-01-01
        • 2013-05-06
        • 2013-06-15
        • 2015-03-24
        相关资源
        最近更新 更多