2012-05-09 74 views
0

新的Android程序员在这里。我有一个执行套接字管理和异步I/O的服务,我需要在它和应用程序中的活动之间建立一条通信路径。Android中的活动和服务之间的通信方法

当前的方法是为Service和Activity配备BroadcastReceivers并使用它们将活动的'命令'意图发送到服务,并将“提醒”意图从服务发送到活动。

我的服务有一个runnable,这是socket read()发生的地方;当接收到的数据的,可运行发送“输入数据的意图的服务,谁再提醒活动:

@Override 
     public int onStartCommand(Intent intent, int flags, int startId) { 
      super.onStartCommand(intent, flags, startId); 
      if (m_IsRunning == false) { 
       m_IsRunning = true; 
       (new Thread(new Runnable() { 
        byte[] inputBuffer = new byte[512]; 
        public void run() { 
         while (m_IsRunning) { 
          if (m_IsConnected) { 
           try { 
            m_Nis = m_Socket.getInputStream(); 
            m_Nis.read(inputBuffer, 0, 512); 
            Intent broadcast = new Intent(); 
            Bundle bun = new Bundle(); 
            bun.putString("ServiceCmd", "ALERT_INCOMING_DATA"); 
            bun.putByteArray("MsgBuffer", inputBuffer); 
            broadcast.putExtras(bun); 
            broadcast.setAction(BROADCAST_TO_SERVICE); 
            sendBroadcast(broadcast); 
           } catch (IOException e) { 
            // Send fault to activity 
           } 
          } 
         } 
        } 
       })).start(); 
      } 
      return START_STICKY; 
     } 

我与广播接收器的做法是这样的:

 private BroadcastReceiver serviceReceiver = new BroadcastReceiver() { 
      @Override 
      public void onReceive(Context context, Intent intent) { 
       Bundle bun = intent.getExtras(); 
       String cmdString = bun.getString("ServiceCmd"); 

       if (cmdString.equals("CMD_SETHOSTINFO")) { 
        // The activity has requested us to set the host info 
        String hostAddr = bun.getString("HostAddressString"); 
        int hostPort = bun.getInt("HostPortNumber"); 
        processSetHostInfoCommand(hostAddr, hostPort); 
       } 
       else if (cmdString.equals("CMD_CONNECT")) { 
        // The activity has requested us to connect 
        if ((m_IsRunning) && (m_IsConnected == false)) { 
         // Attempt to connect 
         processConnectCommand(); 
        } 
       } 
       else if (cmdString.equals("CMD_DISCONNECT")) { 
        // The activity has requested us to disconnect 
        if ((m_IsRunning) && (m_IsConnected == true)) { 
         // Attempt to disconnect 
         processDisconnectCommand(); 
        } 
       } 
       else if (cmdString.equals("CMD_SENDDATA")) { 
        // The activity has requested us to send data 
        if ((m_IsRunning) && (m_IsConnected == true)) { 
         // Attempt to send data 
         byte[] msgBuffer = bun.getByteArray("MsgBuffer"); 
         processSendDataCommand(msgBuffer); 
        } 
       } 
       else if (cmdString.equals("ALERT_INCOMING_DATA")) { 
        // Our TCP receiver thread has received data 
        if (m_IsRunning) { 
         byte[] msgBuffer = bun.getByteArray("MsgBuffer"); 
         processIncomingDataAlert(msgBuffer); 
        } 
       } 
      } 
     }; 

(那些processWhatever()方法一般做套接字管理和数据传输。)

就像我说的,它似乎工作正常,但我想知道这是不是使用消息和处理程序不会更合适的情况。

因此,具体问题是:

  1. 什么是“Android的陶”在决定何时使用广播接收器/意图或处理器/信息?

  2. 当决定使用哪种方法时,是否存在跨线程考虑因素?

(而且,虽然这是题外话,最后一个问题):

  1. 适合做那种基于套接字的我的服务/ O我想要做什么?

回答

4

的陶广播意图,意图和处理

广播意图是一个一对多的发布/订阅的情况,其中一个组件希望让世界知道发生了什么事情,但不关心是否有人/多少听众有,或者他们是否正在运行。

定期意图是一对一情景,其中一个组件需要代表其完成特定处理,但不关心/知道是否有特定的组件能够这样做,或者此组件是否正在运行。在另一方面

处理程序是一个对一个同步/异步场景中双方都是众所周知的,当前正在运行。

上面如何与您的情况

在最简单的实现,你不需要意图或处理程序,您可以直接调用方法对您的服务从后台运行的,就像这样:

MyService.this.processIncomingDataAlert(inputBuffer); 

注意,这将执行在后台线程的方法,并且将阻止套接字收听器,而处理数据。这让我们了解了你所要求的线程考虑。

如果你想解锁插座监听器,和/或处理该UI线程上的数据,你可以在onStartCommand()创建一个处理程序,并用它从可运行后的另一个可运行回到UI线程,这样:

myServiceHandler.post(new Runnable() { 
    public void run() { 
     MyService.this.processIncomingDataAlert(inputBuffer); 
    } 
}; 

注意,这将导致竞争条件当UI线程处理到onIncomingData了Runnable呼叫,这可能会更新inputBufffer后台线程监听器之间,所以你可能会想创建一个副本。

当然,还有现在也该处理发生在UI线程,这是在服务和活动之间的共享上的问题。所以,如果你的数据处理速度很慢,它会影响你的用户界面的响应速度。

如果你想确保两个背景套接字监听-and_ UI线程响应,你应该处理上(还)另一个线程数据。您可以为每个Runnable启动一个新线程,但这会导致大量线程快速创建,并浪费系统资源。更不用说,创建单独的线程会导致处理多个数据块之间的竞争条件,这会使您的应用程序逻辑过于复杂。

幸运的是,Android的这种场景提供AsyncTask

AsyncTask有一个后台线程池,它在其中运行通过它调度的Runnables。如果您在GingerBread或更低版本上运行,或者在ICS或更高版本上运行,AsyncTask也会将它们序列化并一次只运行一项任务。

阅读this article对线程,处理器和的AsyncTask很好的介绍。请注意,本文展示了一个例子,其中描述了AsyncTask的子类并实现了doOnBackground,但这对你来说有点矫枉过正;静态的execute(Runnable)方法将适用于你。

是我们的服务适合您的方案

这个问题有些正交的休息。你需要一个后台线程来监听套接字,这是给定的。但你需要一个服务是不是很清楚。

服务是其中一个应用程序需要做后台处理,并不需要通过用户界面来吸引该用户,或继续进行处理后,用户切换到另一个应用场景。

因此,如果您的情况需要你只能同时显示在屏幕上的活动和用户正在积极地与它配合的插座上听,你并不需要的服务。您可以从您的活动中启动后台线程,并使用处理程序将新数据回发给它或AsyncTask,如上所述。

然而,如果你确实需要继续监听套接字上(你是否应该是一个独立的主题:-))用户已经关闭后您的活动,你所需要的服务。

这里的主要问题是Android中的流程生命周期。为了正确管理设备资源,操作系统将终止它认为处于空闲状态的进程。如果没有任何活动或服务在其中运行,则认为该进程处于空闲状态。仅仅启动后台线程不足以让操作系统知道进程仍然很忙。所以,如果你没有服务,一旦你的活动关闭,从Android的角度来看,你的过程什么都不做,它可以杀死它。

希望这会有所帮助。

+0

Franci - 很多很多谢谢 - 你的文章不仅解决了我的问题,而且还回答了其他几个未经询问)的问题! – Literata

1

您并不需要使用服务。如果它们都在同一个进程中运行,那么只需将网络代码设置为单例,并让活动直接调用方法即可。对于通知,你可以简单地让活动实现一个特定的接口(onData(String data)等),并让他们注册到网络类。当活动消失时请注意取消注册(onStop())。另一方面,如果网络代码需要在没有UI可见的情况下运行(例如,不同的应用程序位于前台),则需要使用服务。

消息和意图实际上是不同的:您使用handlers.messages与特定线程进行通信,而意图实际上是IPC - 它们可以被传递到不同的应用程序/进程。使用广播接收器的好处是,Android将创建一个流程来处理传入的意图,如果目前没有运行。

不知道这是否是“道”,但一般:

  • 如果你需要无UI运行的东西,使用服务(调度网络IO等。)
  • 如果UI存在,只是它在不同的THEAD运行(AscynTask提供了一个方便的方式来做到这一点)
  • ,如果你与你自己的应用程序通信(在一个单一的过程中),使用处理器/消息
  • ,如果你需要跨应用程序(进程)通信,使用意图
  • 如果你需要处理一些异步事件,并有机会应用程序来处理它不会被运行,使用广播接收器。然后,如果需要某个时间,则可能需要启动服务(IntentService在工作线程中进行处理)来执行实际工作。此外,您可能需要获得一个激活锁定,以保持设备再次入睡,而你正在执行你的工作(网络IO等)
+0

'Android将创建一个处理传入意图的过程,如果一个目前没有运行' - 我有一个预感,你做了一个区分,这对我的理解很重要;非常感谢! (顺便说一句,没有什么,我看到了你的博客网站 - 很多有用的加密信息!) – Literata

相关问题