Concurrency on Android with Service

1. The Service Component
The Service component is a very important part of Android's concurrency framework. It fulfills the need to perform a long-running operation within an application, or it supplies some functionality for other applications. In this tutorial we’ll concentrate exclusively on Service’s long-running task capability, and how to use this power to improve concurrency.
What is a Service?
A Service is a simple component that's instantiated by the system to do some long-running work that doesn't necessarily depend on user interaction. It can be independent from the activity life cycle and can also run on a complete different process.
Before diving into a discussion of what a Service represents, it's important to stress that even though services are commonly used for long-running background operations and to execute tasks on different processes, a Service doesn't represent a Thread or a process. It will only run in a background thread or on a different process if it's explicitly asked to do so.
A Service has two main features:
A facility for the application to tell the system about something it wants to be doing in the background.
A facility for an application to expose some of its functionality to other applications.
Services and Threads
There is a lot of confusion about services and threads. When a Service is declared, it doesn't contain a Thread. As a matter of fact, by default it runs directly on the main thread and any work done on it may potentially freeze an application. (Unless it's a IntentService, a Service subclass that already comes with a worker thread configured.)
So, how do services offer a concurrency solution? Well, a Service doesn't contain a thread by default, but it can be easily configured to work with its own thread or with a pool of threads. We'll see more about that below.
Disregarding the lack of a built-in thread, a Service is an excellent solution for concurrency problems in certain situations. The main reasons to choose a Service over other concurrency solutions like AsyncTask or the HaMeR framework are:
A Service can be independent of activity life cycles.
A Service is appropriate for running long operations.
Services don't depend on user interaction.
When running on different processes, Android can try to keep services alive even when the system is short on resources.
A Service can be restarted to resume its work.
Service Types
There are two types of Service, started and bound.
A started service is launched via Context.startService(). Generally it performs only one operation and it will run indefinitely until the operation ends, then it shuts itself down. Typically, it doesn't return any result to the user interface.
The bound service is launched via Context.bindService(), and it allows a two-way communication between client and Service. It can also connect with multiple clients. It destroys itself when there isn't any client connected to it.
To choose between those two types, the  Service must implement some callbacks: onStartCommand() to run as a started service, and onBind() to run as a bound service. A Service may choose to implement only one of those types, but it can also adopt both at the same time without any problems. 

1.服务组件
服务组件是Android并发框架的一个非常重要的组成部分。它满足在应用程序中执行长时间运行操作的需要,或者为其他应用程序提供一些功能。在本教程中,我们将专注于服务的长时间运行的任务能力,以及如何使用此能力来提高并发性。
什么是服务?
服务是一个简单的组件,由系统实例化,以执行一些不一定依赖于用户交互的长时间运行的工作。它可以独立于活动生命周期,并且可以在完全不同的过程上运行。
在讨论服务代表什么之前,需要强调的是,即使服务通常用于长期运行的后台操作和在不同进程上执行任务,服务也不代表Thread或进程。它只会在后台线程或不同的进程上运行,如果它明确要求这样做。
服务有两个主要特点:
应用程序的一个设施,告诉系统它想在后台做什么。
应用程序的一些功能向其他应用程序公开的功能。
服务和线程
有很多关于服务和线程的混乱。当声明服务时,它不包含线程。事实上,默认情况下,它直接在主线程上运行,并且对其执行的任何操作都可能冻结应用程序。 (除非它是一个IntentService,一个服务子类,已经配置了一个工作线程。)
那么,服务如何提供并发解决方案?好了,服务默认不包含线程,但它可以很容易地配置为使用自己的线程或线程池。我们将在下面详细了解。
不考虑缺少内置线程,服务是在某些情况下的并发问题的出色解决方案。选择服务而不是其他并发解决方案(例如AsyncTask或HaMeR框架)的主要原因是:
A服务可以独立于活动生命周期。
A服务适合长时间运行。
服务不依赖于用户交互。
当在不同的进程上运行时,Android可以尝试保持服务活动,即使系统资源短缺。
可以重新启动服务以恢复其工作。
服务类型
有两种类型的服务,启动和绑定。
已启动的服务通过Context.startService()启动。通常它只执行一个操作,它将无限期运行,直到操作结束,然后它自己关闭。通常,它不向用户界面返回任何结果。
绑定服务通过Context.bindService()启动,它允许客户端和服务之间的双向通信。它也可以连接多个客户端。当没有任何客户端连接到它时,它会自毁。
要在这两种类型之间进行选择,服务必须实现一些回调:onStartCommand()作为启动服务运行,onBind()作为绑定服务运行。服务可以选择仅实现这些类型中的一种,但是它也可以同时采用两者而没有任何问题。
2. Service Implementation
To use a service, extend the Service class and override its callback methods, according to the type of Service . As mentioned before, for started services the onStartCommand() method must be implemented and for bound services, the onBind() method. Actually, the onBind() method must be declared for either service type, but it can return null for started services.

2.服务实施
要使用服务,请根据服务类型扩展Service类并覆盖其回调方法。如前所述,对于启动的服务,必须实现onStartCommand()方法,对于绑定服务,必须实现onBind()方法。实际上,onBind()方法必须为任一服务类型声明,但它可以为已启动的服务返回null。

01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 public class CustomService extends Service {      @Override      public int onStartCommand(Intent intent, int flags, int startId) {          // Execute your operations          // Service wont be terminated automatically          return Service.START_NOT_STICKY;      }        @Nullable      @Override      public IBinder onBind(Intent intent) {          // Creates a connection with a client          // using a interface implemented on IBinder          return null ;      } }
onStartCommand(): launched by Context.startService(). This is usually called from an activity. Once called, the service may run indefinitely and it's up to you to stop it, either calling stopSelf() or stopService().
onBind(): called when a component wants to connect to the service. Called on the system by Context.bindService(). It returns an IBinder that provides an interface to communicate with the client.
The service's life cycle is also important to take into consideration. The onCreate()  and onDestroy() methods should be implemented to initialize and shut down any resources or operations of the service.
Declaring a Service on Manifest
The Service component must be declared on the manifest with the element. In this declaration it's also possible, but not obligatory, to set a different process for the Service to run in.

onStartCommand():由Context.startService()启动。 这通常从活动中调用。 一旦被调用,服务可以无限期地运行,它由你停止它,调用stopSelf()或stopService()。
onBind():当组件想要连接到服务时调用。 通过Context.bindService()在系统上调用。 它返回一个IBinder,它提供了一个与客户端通信的接口。
服务的生命周期也是重要的考虑因素。 应该实现onCreate()和onDestroy()方法来初始化和关闭服务的任何资源或操作。
在清单上声明服务
服务组件必须使用元素在清单上声明。 在本声明中,也可以但不强制地为服务设置不同的进程。

1 2 3 4 5 6 7 8 9 < manifest ... >    ...    < application ... >        < service          android:name = ".ExampleService"          android:process = ":my_process" />        ...    application > manifest >
2.2. Working with Started Services
To initiate a started service you must call Context.startService() method. The Intent must be created with the Context and the Service class. Any relevant information or data should also be passed in this Intent.

2.2。 使用启动服务
要启动已启动的服务,必须调用Context.startService()方法。 Intent必须使用Context和Service类创建。 任何相关信息或数据也应在本意图中传递。

1 2 3 4 5 6 7 8 Intent serviceIntent = new Intent( this , CustomService. class ); // Pass data to be processed on the Service Bundle data = new Bundle(); data.putInt( "OperationType" , 99 ); data.putString( "DownloadURL" , "http://mydownloadurl.com" ); serviceIntent.putExtras(data); // Starting the Service startService(serviceIntent);
In your Service class, the method that you should be concerned about is the onStartCommand(). It's on this method that you should call any operation that you want to execute on the started service. You'll process the Intent to capture information sent by the client. The startId represents an unique ID, automatically created for this specific request and the flags can also contain extra information about it.

在你的Service类中,你应该关心的方法是onStartCommand()。 在这个方法上,你应该调用任何你想在启动的服务上执行的操作。 您将处理Intent以捕获客户端发送的信息。 startId表示为此特定请求自动创建的唯一ID,标记还可以包含有关该标识的额外信息。

01 02 03 04 05 06 07 08 09 10 11 12 13 14 @Override public int onStartCommand(Intent intent, int flags, int startId) {        Bundle data = intent.getExtras();      if (data != null ) {          int operation = data.getInt(KEY_OPERATION);          // Check what operation to perform and send a msg          if ( operation == OP_DOWNLOAD){              // make a download          }      }        return START_STICKY; }
The onStartCommand() returns a constant int that controls the behavior:
Service.START_STICKY: Service is restarted if it gets terminated.
Service.START_NOT_STICKY: Service is not restarted.
Service.START_REDELIVER_INTENT: The service is restarted after a crash and the intents then processing will be redelivered.
As mentioned before, a started service needs to be stopped, otherwise it will run indefinitely. This can be done either by the Service calling stopSelf() on itself or by a client calling stopService() on it.

onStartCommand()返回一个控制行为的常量int:
Service.START_STICKY:如果服务终止,服务将重新启动。
服务.START_NOT_STICKY:服务未重新启动。
Service.START_REDELIVER_INTENT:服务在崩溃后重新启动,并且Intents然后处理将重新提交。
如前所述,启动的服务需要停止,否则将无限期运行。 这可以通过Service本身调用stopSelf()或调用stopService()的客户端来完成。

1 2 3 4 5 void someOperation() {          // do some long-running operation          // and stop the service when it is done          stopSelf();      }
Binding to Services
Components can create connections with services, establishing a two-way communication with them. The client must call Context.bindService(), passing an Intent, a ServiceConnection interface and a flag as parameters. A Service can be bound to multiple clients and it will be destroyed once it has no clients connected to it.

绑定到服务
组件可以创建与服务的连接,与它们建立双向通信。 客户端必须调用Context.bindService(),传递Intent,ServiceConnection接口和标志作为参数。 一个服务可以绑定到多个客户端,一旦没有客户端连接到它,它将被销毁。

void bindWithService() {          Intent intent = new Intent( this , PlayerService. class );          // bind with Service          bindService(intent, mConnection, Context.BIND_AUTO_CREATE);      }

It's possible to send Message objects to services. To do it you'll need to create a Messenger on the client side in a ServiceConnection.onServiceConnected interface implementation and use it to send Message objects to theService.

可以向服务发送Message对象。 为此,您需要在客户端在ServiceConnection.onServiceConnected接口实现中创建一个Messenger,并使用它来向服务发送Message对象。

01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 private ServiceConnection mConnection = new ServiceConnection() {          @Override          public void onServiceConnected(ComponentName className,                                         IBinder service) {              // use the IBinder received to create a Messenger              mServiceMessenger = new Messenger(service);              mBound = true ;          }            @Override          public void onServiceDisconnected(ComponentName arg0) {              mBound = false ;              mServiceMessenger = null ;          }      };
It's also possible to pass a response  Messenger  to the  Service  for the client to receive messages. Watch out though, because the client may not no longer be around to receive the service's message. You could also use  BroadcastReceiver  or any other broadcast solution.

还可以将响应Messenger传递给服务以供客户端接收消息。 注意,但是,因为客户端可能不再在周围接收服务的消息。 您还可以使用BroadcastReceiver或任何其他广播解决方案。

01 02 03 04 05 06 07 08 09 10 11 12 13 private Handler mResponseHandler = new Handler() {      @Override      public void handleMessage(Message msg) {          // handle response from Service      } }; Message msgReply = Message.obtain(); msgReply.replyTo = new Messenger(mResponseHandler); try {      mServiceMessenger.send(msgReply); } catch (RemoteException e) {      e.printStackTrace(); }

It's important to unbind from the Service when the client is being destroyed.

当客户端被销毁时,解除绑定到服务很重要。

1 2 3 4 5 6 7 8 9 @Override      protected void onDestroy() {          super .onDestroy();          // disconnect from service          if (mBound) {              unbindService(mConnection);              mBound = false ;          }      }

On the Service side, you must implement the Service.onBind() method, providing an IBinder provided from aMessenger. This will relay a response Handler to handle the Message objects received from client.

在服务端,您必须实现Service.onBind()方法,提供从Messenger提供的IBinder。 这将中继响应处理程序以处理从客户端接收的消息对象。

01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 IncomingHandler(PlayerService playerService) {          mPlayerService = new WeakReference<>(playerService);      }        @Override      public void handleMessage(Message msg) {          // handle messages      } }   public IBinder onBind(Intent intent) {      // pass a Binder using the Messenger created      return mMessenger.getBinder(); }   final Messenger mMessenger = new Messenger( new IncomingHandler( this ));
3 Concurrency Using Services
Finally, it's time to talk about how to solve concurrency problems using services. As mentioned before, a standard Service doesn't contain any extra threads and it will run on the main Thread by default. To overcome this problem you must add an worker Thread, a pool of threads or execute the Service on a different process. You could also use a subclass of Service called IntentService that already contains a Thread.
Making a Service Run on a Worker Thread
To make the Service execute on a background Thread you could just create an extra Thread and run the job there. However Android offers us a better solution. One way to take the best advantage of the system is to implement the HaMeR framework inside the Service, for example by looping a Thread with a message queue that can process messages indefinitely.
It's important to understand that this implementation will process tasks sequentially. If you need to receive and process multiple tasks at the same time, you should use a pool of threads. Using thread pools is out of the scope of this tutorial and we won't talk about it today. 
To use HaMeR you must provide the Service with a Looper, a Handler and a HandlerThread.

3并发使用服务
最后,现在是讨论如何使用服务解决并发问题的时候了。如前所述,标准服务不包含任何额外的线程,它将默认在主线程上运行。要克服这个问题,您必须添加一个工作线程,一个线程池或在不同的进程上执行服务。您还可以使用已经包含Thread的名为IntentService的Service子类。
在服务线程上运行服务
要使服务在后台线程上执行,您可以创建一个额外的线程并在那里运行作业。但Android为我们提供了更好的解决方案。充分利用系统的一种方法是在服务中实现HaMeR框架,例如通过使用可以无限地处理消息的消息队列循环线程。
重要的是要理解这个实现将顺序处理任务。如果您需要同时接收和处理多个任务,则应使用线程池。使用线程池超出了本教程的范围,我们今天不会谈论它。
要使用HaMeR,您必须向服务提供Looper,处理程序和HandlerThread。

02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private Looper mServiceLooper; private ServiceHandler mServiceHandler; // Handler to receive messages from client private final class ServiceHandler extends Handler {      ServiceHandler(Looper looper) {          super (looper);      }        @Override      public void handleMessage(Message msg) {          super .handleMessage(msg);          // handle messages            // stopping Service using startId          stopSelf( msg.arg1 );      } }   @Override public void onCreate() {      HandlerThread thread = new HandlerThread( "ServiceThread" ,              Process.THREAD_PRIORITY_BACKGROUND);      thread.start();        mServiceLooper = thread.getLooper();      mServiceHandler = new ServiceHandler(mServiceLooper);  

If there is no need for the Service to be kept alive for a long time, you could use IntentService, a Servicesubclass that's ready to run tasks on background threads. Internally, IntentService is a Service with a very similar implementation to the one proposed above. 

To use this class, all you have to do is extend it and implement the onHandleIntent(), a hook method that will be called every time a client calls startService() on this Service. It's important to keep in mind that theIntentService will stop as soon as its job is completed.

IntentService
如果不需要长时间保持服务,您可以使用IntentService,一个准备在后台线程上运行任务的服务子类。 在内部,IntentService是一个具有非常类似于上面提出的实现的服务。
要使用这个类,所有你需要做的是扩展它并实现onHandleIntent(),一个钩子方法,每当客户端调用此服务上的startService()时将被调用。 重要的是要记住IntentService将在其作业完成后立即停止。

01 02 03 04 05 06 07 08 09 10 11 public class MyIntentService extends IntentService {            public MyIntentService() {          super ( "MyIntentService" );      }        @Override      protected void onHandleIntent(Intent intent) {          // handle Intents send by startService      } }


Service can run on a completely different Process, independently from all tasks that are happening on the main process. A process has its own memory allocation, thread group, and processing priorities. This approach can be really useful when you need to work independently from the main process.

Communication between different processes is called IPC (Inter Process Communication). In a Servicethere are two main ways to do IPC: using a Messenger or implementing an AIDL interface. 

We've learned how to send and receive messages between services. All that you have to do is use create aMessenger using the IBinder instance received during the connection process and use it to send a reply Messenger back to the Service.

IPC(进程间通信)
服务可以在完全不同的进程上运行,独立于主进程上发生的所有任务。进程具有自己的内存分配,线程组和处理优先级。当您需要独立于主流程工作时,此方法可以真正有用。
不同进程之间的通信称为IPC(进程间通信)。在服务中有两种主要方式来做IPC:使用Messenger或实现AIDL接口。
我们学习了如何在服务之间发送和接收消息。所有你需要做的是使用在连接过程中收到的IBinder实例创建一个Messenger,并使用它来发送一个回复Messenger到服务。

01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 private Handler mResponseHandler = new Handler() {      @Override      public void handleMessage(Message msg) {          // handle response from Service      } };   private ServiceConnection mConnection = new ServiceConnection() {      @Override      public void onServiceConnected(ComponentName className,                                     IBinder service) {          // use the IBinder received to create a Messenger          mServiceMessenger = new Messenger(service);            Message msgReply = Message.obtain();          msgReply.replyTo = new Messenger(mResponseHandler);          try {              mServiceMessenger.send(msgReply);          } catch (RemoteException e) {              e.printStackTrace();          }      }

The AIDL interface is a very powerful solution that allows direct calls on Service methods running on different processes and it's appropriate to use when your Service is really complex. However, AIDL is complicated to implement and it's rarely used, so its use won't be discussed in this tutorial.

AIDL接口是一个非常强大的解决方案,允许直接调用在不同进程上运行的服务方法,它适合在您的服务非常复杂时使用。然而,AIDL实现起来很复杂,很少使用,因此本教程中不会讨论它的使用。

4. Conclusion
Services can be simple or complex. It depends on the needs of your application. I tried to cover as much ground as possible on this tutorial, however I've focused just on using services for concurrency purposes and there are more possibilities for this component. I you want to study more, take a look at the documentation and Android guides. 

4。结论
服务可以简单或复杂。 这取决于您的应用程序的需要。 我试图在本教程覆盖尽可能多的地面,但我只专注于使用服务的并发目的,这个组件有更多的可能性。 我想学习更多,看看文档和Android指南。

更多相关文章

  1. 图解 Android Handler 线程消息机制
  2. android中的UI单线程模型
  3. [Android]你不知道的Android进程化(3)--进程等级
  4. Android中native进程内存泄露的调试技巧

随机推荐

  1. Android的状态机模式StateMachine与State
  2. Android学习笔记-Android非布局activity
  3. android multicast 多播(组播)问题
  4. 2.1……Android中的单位简介
  5. Android(安卓)ListView pull up to refre
  6. android studio开发工具升级到3.6.0以后,a
  7. android正则表达式匹配中文正解
  8. Android Gradle 初识
  9. android camera以时间来命名照片文件
  10. Android解决fragment重影问题