This is the 13th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

After learning Android for so long, I haven’t studied Service thoroughly and comprehensively, so the starting point of this article is to analyze Service comprehensively. As we know, Service in our project is mainly used to handle some background operations or things that need to be handled for a long time, such as common music playback, even when we exit the interface, and it can also serve us. That’s the beauty of a service. Let’s begin with a comprehensive study of Service.

I. Classification of Services

  • Local Service A Local Service runs in the main process of an application and in the same process with other components. This saves resources and facilitates communication between components in the same process. The downside is that if the main process is killed, the service terminates. Common case: music players
  • The Remote Service runs in a separate process, so when we register the Service in the manifest, we need Android :process=”: Remote “to specify the running process. The advantage of this is that the service is not affected by other processes, making it easy to serve multiple processes. The disadvantage is that independent processes occupy certain system resources and communication is complicated.

2, according to the operation effect:

  • The foreground service is used to notify the user when the system kills the service due to insufficient memory. Therefore, the foreground service must have a Notificattion status bar, which is displayed on the notification bar. Common cases: mobile phone butler, music player
  • Background service The default service is a background service when we start a service and do nothing about it. Common cases: automatic news updates, weather access

Start and stop the Service

Intent intent = new Intent(this, HelloService.class);
startService(intent);
Copy the code

Another way to start the service is through the bindService() method. Such as:

Intent intent = new Intent(MainActivity.this,MyService.class);     

bindService(intent,serviceConnection,BIND_AUTO_CREATE);
Copy the code

There are also two ways to stopService. When starting Service through startService, stopService() or stopSelf or service.stopselfresult () is used to stopService. When starting with bindService, we need to call unbindService() to stop the service.

Here is a brief introduction! So that we can test the life cycle of the Service later.

Life cycle of a Service

Create a new Module in Android Studio called ServiceStudy. Then create a new subclass of Service, MyService.

public class MyService extends Service { private static final String TAG = "MyService"; public MyService() { } @Override public IBinder onBind(Intent intent) { Log.d(TAG,"onBind"); return null; } @Override public void onCreate() { super.onCreate(); Log.d(TAG,"onCreate"); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); Log.d(TAG,"onStart"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG,"onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { Log.d(TAG,"onDestroy"); super.onDestroy(); } @Override public boolean onUnbind(Intent intent) { Log.d(TAG,"onUnbind"); return super.onUnbind(intent); } @Override public void onRebind(Intent intent) { Log.d(TAG,"onRebind"); super.onRebind(intent); }}Copy the code

In the MyService class, we don’t do any business, just print the Log for testing. We create an XML layout file for our main Activity.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity">
	    <LinearLayout
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:orientation="horizontal">
	        <Button
	            android:id="@+id/btn_start"
	            android:text="start service"
	            android:layout_width="wrap_content"
	            android:layout_height="wrap_content" />
	        <Button
	            android:id="@+id/btn_stop"
	            android:text="stop service"
	            android:layout_width="wrap_content"
	            android:layout_height="wrap_content" />
	    </LinearLayout>
	    <LinearLayout
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:layout_marginTop="10dp"
	        android:orientation="horizontal">
	        <Button
	            android:id="@+id/btn_bind"
	            android:text="bind service"
	            android:layout_width="wrap_content"
	            android:layout_height="wrap_content" />
	        <Button
	            android:id="@+id/btn_unbind"
	            android:text="unbind service"
	            android:layout_width="wrap_content"
	            android:layout_height="wrap_content" />
	    </LinearLayout>
	</LinearLayout>
Copy the code

Let’s look at the life cycle of the Service by handling four button events.

public class MainActivity extends ActionBarActivity implements View.OnClickListener { private static final String TAG = "MyService:MainActivity"; private Button btn_startService; private Button btn_stopService; private Button btn_bindService; private Button btn_unbindService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_startService = (Button) findViewById(R.id.btn_start); btn_stopService = (Button) findViewById(R.id.btn_stop); btn_bindService = (Button) findViewById(R.id.btn_bind); btn_unbindService = (Button) findViewById(R.id.btn_unbind); btn_startService.setOnClickListener(this); btn_stopService.setOnClickListener(this); btn_bindService.setOnClickListener(this); btn_unbindService.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_start: Intent intentStart = new Intent(MainActivity.this,MyService.class); startService(intentStart); break; case R.id.btn_stop: Intent intentStop = new Intent(MainActivity.this,MyService.class); stopService(intentStop); break; case R.id.btn_bind: Intent intentBind = new Intent(MainActivity.this,MyService.class); bindService(intentBind,serviceConnection,BIND_AUTO_CREATE); break; case R.id.btn_unbind: unbindService(serviceConnection); break; } } private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG,"onServiceConnected"); } @Override public void onServiceDisconnected(ComponentName name) { Log.d(TAG,"onServiceDisconnected"); }}; }Copy the code

The final and crucial step is to register our Service in the Manifest:

<service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true" >
    </service>
Copy the code

The StartService() method is used to start the service lifecycle:

	MyService﹕ onCreate
	MyService﹕ onStartCommand
	MyService﹕ onStart
Copy the code

We click the StartService button again to start the service. Look at its lifecycle flow:

	MyService﹕ onStartCommand
	MyService﹕ onStart
Copy the code

OnCreat — >onStartCommand — >onstart. OnCreat — >onStartCommand — >onstart. OnCreat — >onStartCommand — >onStart. I’m just going to use the onStartCommand method.

Stop the service life cycle by stopService() :

MyService﹕ onDestroy
Copy the code

The service is destroyed by calling onDestroy() directly on the service. Note that the stopSelf() method of the Service is used to stop the Service. Note that this is a superclass method. After this method is called, the Service stops at an unspecified time. OnCreate () is called first, followed by the onStart() method.

We should also note that if multiple service start requests are sent to onStartCommand() at the same time, stopSelf() should not be called after processing one request; If the service is destroyed, the new request will not be processed. In this case stopSelf(int startId) should be called.

StartService makes it easy to start a service without having to interact with it.

3. Start the service life cycle through bindService:

	MyService﹕ onCreate
	MyService﹕ onBind
Copy the code

The process is: onCreate — >onBind method. Unbind service unbind service unbind service unbind service

	MyService﹕ onUnbind
	MyService﹕ onDestroy
Copy the code

So the life cycle of calling bindService is: onCreate –> onBind –> onUnbind –> onDestory.

We know that onBind() returns an instance of the IBind interface to the client. IBind allows the client to call back on methods of the Service, such as getting an instance of the Service, its running status, or other operations. Srevice will call onUnbind->onDestroy to exit the Context. It also implements communication between our activities and other components and services.

4, We click start Service and then bind Service to see the life cycle:

	MyService﹕ onCreate
	MyService﹕ onStartCommand
	MyService﹕ onStart
	MyService﹕ onBind
Copy the code

Then I hit Stop Service, and it doesn’t call onDestroy, and THEN I hit unbind Service.

	MyService﹕ onUnbind
	MyService﹕ onDestroy
Copy the code

At this point, the Service unbinds and then destroys. Life cycle of started and bound services: If a Service is started and bound, it will always run in the background. OnCreate is always called once, and startService is called the same number of times as Service onStart. It is not possible to stop the Service by calling unbindService or stopService or stopSelf of Service alone; both must be called.

Now that we know how to start and stop a service and its life cycle, let’s summarize. 1. In our application, we need to deal with some useless resources after stopping the service to prevent occupying system resources. We release some resources in the onDestroy() method. 2. The service runs in the main thread, so if we have internal operations that interact with the activity, we should not take too much time to block the activity and prevent ANR exceptions. We should start a child thread to execute.

  • The user does not respond for 5 seconds after performing an operation.
  • The broadCastReceiver operation did not complete for 10 seconds.
  • Service did not return a result for 20 seconds.

3. Life cycle diagram comparison of the two startup modes of Service:

  • Full life cycle: from the onCreate() method — >onDestroy().
  • Activity life cycle: Starts from onStartCommand() or onBind(). If you start with start, this life cycle is the same as the full life cycle. If you start with bind, this life cycle lasts until onUnbind.

4. The life cycle of the service restarts when the activity is destroyed. The service is automatically unchained when the activity is destroyed.

Modify service tags to Manifet

  • Android :enabled: indicates whether service is available. The value can be true or false
  • Android: Exported: Whether to use this service for other applications. True yes, false no.
  • Android :name: indicates the service path name. This parameter must be set
  • Android :process: the name of the process in which the service runs. The default is the main process

4. Service and Activity communication

Create a subclass of MyBinder:

Public void showBinderLog(){log.d (TAG,"showBinderLog"); } public int sumNumber(){return 12+12; }}Copy the code

Return the corresponding instance in the onBind method:

	@Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind");
        return new MyBinder();
    }
Copy the code

Finally, improve our ServiceConnection in the Activity.

private MyService.MyBinder myBinder; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG,"onServiceConnected"); myBinder = (MyService.MyBinder) service; myBinder.showBinderLog(); Log.d(TAG,"Sum=" + myBinder.sumNumber()); } @Override public void onServiceDisconnected(ComponentName name) { Log.d(TAG,"onServiceDisconnected"); }};Copy the code

Let’s take a look at the execution result:

MyService: onCreate MyService: onBind MyService:MainActivity: onServiceConnected MyService: ShowBinderLog MyService: MainActivity: Sum = 24Copy the code

As you can see, we can use ServiceConnection to monitor the state of the service, and then control the execution of the service after the connection is successful, and obtain the corresponding execution result. In this way, we can control the service in the activity. The two communicate with each other through Binder objects.

Front desk Service

public class MyService extends Service { ... @Override public void onCreate() { super.onCreate(); Log.d(TAG,"onCreate"); createForegroundService(); }... /** * Create a foreground service */ private void createForegroundService(){if(build.version.sdk_int <= 11){Notification Notification = new Notification(); notification.icon = R.drawable.god; Notification. tickerText=" Instance of foreground service "; notification.when=System.currentTimeMillis(); notification.defaults=Notification.DEFAULT_SOUND; Notification. SetLatestEventInfo (this, "notice", "service", PendingIntent. GetActivity (this, 0, new Intent (this, MainActivity.class), 0)); startForeground(1,notification); Builder Builder = new notification. Builder(this); Builder.setticker (" foreground service instance "); builder.setSmallIcon(R.drawable.god); builder.setWhen(System.currentTimeMillis()); builder.setDefaults(Notification.DEFAULT_SOUND); Builder.setcontenttext (" This is a foreground service instance "); Builder.setcontenttitle (" Foreground service instance "); builder.setOngoing(true); builder.setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0)); NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); if(Build.VERSION.SDK_INT < 16){ notificationManager.notify(1,builder.getNotification()); }else{ notificationManager.notify(1,builder.build()); }}}}Copy the code

Take a look at the rendering:

The difference between a Service and a Thread

  • My Foreground process is kept coming over the Foreground
  • 2, Visble process: visible process, visible cannot interact
  • E.g. A Service process is a Service process
  • 4, Background process
  • Empty process: Empty process

The Android system reclaims processes from low priority to high priority. When the system memory is insufficient, the system reclaims processes from low priority to high priority. Note that the system does not easily reclaim service, visible, and foreground processes. When all the application interfaces are closed, the thread attached to the activity becomes an empty process and can easily be destroyed.

A. Service B. Intentservice C. Service D. Intentservice

	public class MyIntentService extends IntentService {
	    public MyIntentService(){
	        super("MyIntentServices");
	    }
	
	    /**
	     * Creates an IntentService.  Invoked by your subclass's constructor.
	     *
	     * @param name Used to name the worker thread, important only for debugging.
	     */
	    public MyIntentService(String name) {
	        super(name);
	    }
	
	    @Override
	    protected void onHandleIntent(Intent intent) {
	       
	    }
	}
Copy the code

We must implement the onHandleIntent method to handle our Intent, and we must declare a constructor with no parameters, otherwise the Intent will run with an exception:

Caused by: java.lang.InstantiationException: can't instantiate class test.drision.com.servicestudy.MyIntentService; no empty constructor
Copy the code

From the constructor comment, we know that IntentService creates an internal thread called worker Thread to handle time-consuming work, so we don’t need to start a thread like Service to handle time-consuming work. For comparison, we add a thread sleep to the MyService onCreate method:

@Override public void onCreate() { super.onCreate(); try { Thread.sleep(20000); } catch (InterruptedException e) { e.printStackTrace(); }}Copy the code

Add the same sleep 20s to MyIntentService’s onHandleIntent:

@Override protected void onHandleIntent(Intent intent) { try { Thread.sleep(20000); } catch (InterruptedException e) { e.printStackTrace(); }}Copy the code

The final result is:An ANR exception occurred 20 seconds after MyService started, but MyIntentService did not. That’s the big difference.

Use of remote services

public class RemoteService extends Service { private static final String TAG = "RemoteService"; public RemoteService() { } @Override public IBinder onBind(Intent intent) { return new MyRemoteBind(); } public class MyRemoteBind extends Binder{ public void showLog(){ Log.d(TAG,"showLog"); }}}Copy the code

Take a look at the registry in manifest:

	 <service
            android:name="remoteservice.RemoteService"
            android:enabled="true"
            android:exported="true"
            android:process=":remote">
     </service>
Copy the code

We specify the process to run the service using Android: Process. In MainActivity we add a Button to start the remote service.

	 Intent intentRemote = new Intent(MainActivity.this,RemoteService.class);
     bindService(intentRemote,serviceConnectionRemote,BIND_AUTO_CREATE);

	 private RemoteService.MyRemoteBind remoteBind;
     private ServiceConnection serviceConnectionRemote = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            remoteBind = (RemoteService.MyRemoteBind) service;
            remoteBind.showLog();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
Copy the code

Let’s take a look at the interface:

I wiped it! How did it crash! It did. Let’s take a look at the Log:

	java.lang.ClassCastException: android.os.BinderProxy cannot be cast to remoteservice.RemoteService$MyRemoteBind
Copy the code

According to Log, we can’t cast now, which means we can’t communicate with the remote service the way we used to, so at this point, we need to find an alternative, AIDL.

AIDL stands for Android Interface Definition Language (Android Interface Definition Language). It is an Android internal process communication Interface description language, through which we can define the communication Interface between processes, so as to achieve cross-process communication.

With that in mind, let’s take a look at how AIDL works. Creating an AIDL file in Eclipse takes a lot of work, and I have to change the file name suffix. Since I’m using Android Studio, I’ll just create a new AIDL. Check out the renderings:

After clicking “Finish”, AS will automatically generate an AIDL file for us, which will store our files.

Take a look at the code:

	package remoteservice;

	interface IMyAidlInterface {
	    void showRemoteAIDL();
	}
Copy the code

We define only one method. In Eclipse, when we define an AIDL, the ADT plugin automatically generates a Java file for us in the Gen directory. In AS, we need to Make Project under the Build menu before we can generate it. View under the Project structure:

Let’s open up this file called IMyAidlInterface, which is automatically generated by the system for the AIDL file that we created.

/* * This file is auto-generated. DO NOT MODIFY. * Original file: E:\\AndroidStudioProject\\FirstTestApp\\servicestudy\\src\\main\\aidl\\remoteservice\\IMyAidlInterface.aidl */ package remoteservice; public interface IMyAidlInterface extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements remoteservice.IMyAidlInterface { private static final java.lang.String DESCRIPTOR = "remoteservice.IMyAidlInterface"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an remoteservice.IMyAidlInterface interface, * generating a proxy if needed. */ public static remoteservice.IMyAidlInterface asInterface(android.os.IBinder obj) { if  ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin! =null)&&(iin instanceof remoteservice.IMyAidlInterface))) { return ((remoteservice.IMyAidlInterface)iin); } return new remoteservice.IMyAidlInterface.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_showRemoteAIDL: { data.enforceInterface(DESCRIPTOR); this.showRemoteAIDL(); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements remoteservice.IMyAidlInterface { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public void showRemoteAIDL() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_showRemoteAIDL, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_showRemoteAIDL = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public void showRemoteAIDL() throws android.os.RemoteException; }Copy the code

System automatically generated, code layout is not very standard, I will pick a few points to illustrate:

1. According to the class annotation, the class is automatically generated by the system and we are not allowed to modify it.

2. According to the class definition

	public static abstract class Stub extends android.os.Binder implements remoteservice.IMyAidlInterface
Copy the code

Binder is an abstract class that implements the AIDL interface we defined and is a subclass of Binder. And is a static inner class of the IMyAidlInterface interface. The asBinder() method returns a Binder object. 4. When writing Aidl files, note the following:

  • The interface name is the same as the AIDL file name.
  • Interfaces and methods do not use access modifiers such as public,private,protected, or final or static.
  • Aidl supports Java primitive types (int, Long, Boolean, etc.) and (String, List, Map, CharSequence, etc.) by default. Import statements are not required for these types. Element types in lists and maps must be supported by Aidl. If a custom type is used as a parameter or return value, the custom type must implement the Parcelable interface.
  • Custom types and other interface types generated by AIDL should be explicitly imported in the AIDL description file, even if the class is in the same package as the defined package.
  • All non-Java primitive type parameters must be marked in, out, and inout in aiDL files to indicate whether they are input parameters, output parameters, or input/output parameters.
  • The default tag for Java primitive types is IN, not any other tag.

From the above analysis, we basically know that we need to communicate with this Stub class. Let’s do it now. We make the changes directly in our RemoteService class. Let’s create a Stub object and use it.

public class RemoteService extends Service { private static final String TAG = "RemoteService"; public RemoteService() { } @Override public IBinder onBind(Intent intent) { return myRemoteBinder; } IMyAidlInterface.Stub myRemoteBinder = new IMyAidlInterface.Stub() { @Override public void showRemoteAIDL() throws RemoteException {log. d(TAG, "this is through AIDL "); }}; }Copy the code

Let’s modify the ServiceConnection object:

private IMyAidlInterface iMyAidlInterface; private ServiceConnection serviceConnectionRemote = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);  try { iMyAidlInterface.showRemoteAIDL(); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } };Copy the code

In this way, we can click the Remote Service button to communicate with each other and see the result of the run log:

At this point, we have completed cross-process communication for the remote service. But in retrospect, we used remote services in the same project, and did not use the power of remote services. So we create a project to test the invocation of the remote service.

Create a Module named Servicetest and copy the aiDL folder under ServiceStudy above to the Test project along with the following files:

Note: At this time we need to make project under the Build menu to use, so that the corresponding files can be generated under the Build folder for our use. Then we can follow the steps above to use the remote service.

Then we create a button in the XML file:

	<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_bindRemoteService"
        android:text="BindRemoteService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"/>

	</RelativeLayout>
Copy the code

Processing in MainActivity:

public class MainActivity extends Activity { private Button btn_bindService; private IMyAidlInterface iMyAidlInterface; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);  try { iMyAidlInterface.showRemoteAIDL(); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_bindService = (Button) findViewById(R.id.btn_bindRemoteService); btn_bindService.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("test.dsw.com.remoteservice.RemoteService"); bindService(intent,serviceConnection,BIND_AUTO_CREATE); }}); }}Copy the code

Take a look at the results:

At this point, we have completed cross-process communication of the Service. This time in Android Studio development, or encountered a little trouble, as eclipse copy package to the aspects, mainly remember to create aiDL file must make project, and then can be used normally.

Since I wrote this article yesterday, I wanted to give it up several times during the process. I felt that Service was too huge and I could not write it comprehensively. I referred to the official English document and felt that there were many regulations that were not clear. If there is a chance, there is a chance to write a music player with the use of service. Welcome to leave a comment.