Service of the four components of the Android APP Complete Basic Tutorial (06)

1 Diagram Interpreting Service

Here is a diagram of life cycle: basic concepts & interpretation of critical callback methods

2. Interpretation of Service Class Relationships

The inheritance diagram for the parent and child classes of the Service class is as follows:

3 Configure Service

Both Service and Active are android system components, which need to be in android Manifest. The XML configuration file declares otherwise the service will not be recognized or executed by the system. A simple service component is as follows:

<service android:name=".FirstService">
</service>

Common attributes are name, export (whether to be allowed to be called by other applications), permission (the privilege required to start a service), process (the process in which the service is located, default is in the APP process, but other processes can be specified), and label attributes are not required (because there is no interface). If the intent-filter option is not set, the Intent cannot be directly responded to, but can only be started by specifying the Intent of the Component.

4 Service Operation

4.1 Service Start/Stop

The key API s for starting and stopping services are as follows:

//Define Intent, carry service
Intent intent = new Intent(MainActivity.this,com.ags.XXXService.class);

//Start Services
startService(intent);

//Out of Service
stopService(intent);

About service:

  • The onCreate callback function executes once and the onStartCommand callback function executes more than once after the service has been started several times in a row.
  • The API used to start/stop a service does not have the ability to exchange/communicate data with the original caller. To be able to communicate, you need to bind the service.

Note: Starting with android 5.0, Google requires that service components must be started using explicit Intent.

4.2 Bind Service s and communicate

If the service wants to communicate and exchange data with the initiator, the service should be started/closed using the bindService() and unbindService() methods. Here is an example of the bindService and unbindService methods. Two keys are set here, one binds the service and calls the service component's method addNum after the binding is successful. Another unbound service.

First, write a custom service inheriting system service named BinderService, which is implemented as follows:

public class BinderService extends Service {
    private static String TAG = "BinderService";

    public class AgsBinder extends Binder {
        public BinderService getService(){
            return BinderService.this;
        }
    }

    //Communication between caller and service via binder
    private AgsBinder binder = new AgsBinder();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnBind");
        return super.onUnbind(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    /**Parametric Interpretation:
     *service: Specify the service to start through intent
     * conn: Listen for callback processing for successful/failed connections between visitors and service s
     * flags: Whether to automatically create a service
     */
    @Override
    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
        Log.d(TAG, "bindService");
        return super.bindService(service, conn, flags);
    }

    @Override
    public void unbindService(ServiceConnection conn) {
        super.unbindService(conn);
        Log.d(TAG, "unbindService");
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }

    public int addNum(int a,int b){
        Log.d(TAG, "addNum: "+(a+b));
        return a+b;
    }
}

Second, the MainActivity code is implemented as follows:

public class MainActivity extends AppCompatActivity {
    private static String TAG = "MainActivity";
    private Button btn_bind;
    private Button btn_unbind;
    private BinderService binderservice = null;
    private boolean isBound = false;

    private ServiceConnection connection = new ServiceConnection() {
        @Override //Connection Successful Callback
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected");
            isBound = true;
            BinderService.AgsBinder binder = (BinderService.AgsBinder)service;
            binderservice = binder.getService();
            Log.d(TAG, "Activity onServiceConnected");
            //Once the connection is successful, you can call various methods of the binderservice.
            int x = binderservice.addNum(3,4);
            Log.d(TAG, "Activity call binderservice.addNum:"+x);
        }

        @Override //Unexpected callback occurs, unbind does not cause callback
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected");
            isBound = false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_bind = findViewById(R.id.btn_bind);
        btn_unbind = findViewById(R.id.btn_unbind);

        btn_bind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, com.ags.myapplication.BinderService.class);
                //intent.putExtra("from", "MainActivity");
                Log.d(TAG, "onClick:bindService");
                //Start and bind the service
                bindService(intent,connection,BIND_AUTO_CREATE);
            }
        });
        btn_unbind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(isBound){
                    Log.d(TAG, "onClick:unbindService");
                    //Unbind service
                    unbindService(connection);
                    isBound = false;
                }
            }
        });
    }
}

Note: onServiceDisconnected will not be invoked when the end and Service unbundling are enabled. The onServiceDisconnected call occurs when the enabled end and Service connection are unexpectedly lost, at which point it is determined that the enabled end and Service must be disconnected.

5 IntentService

The problem with a service is that it is in the same process as the application and cannot directly handle time-consuming tasks in the service. To solve this problem, you need to use IntentService, which has the following advantages:

  • A separate worker thread is created to process all Intent requests and code for the onHandleIntent method implementation.
  • Automatically stop when request processing is complete without stopSelf method.
  • Simply implement the onHandleIntent method to start the service to perform time-consuming work in the background.

Here is an example of how time-consuming tasks are handled. The intentService code for time-consuming tasks is implemented as follows:

public class SecondService extends IntentService {
    private static String TAG = "IntentService";

    public SecondService() {
        super("SecondService");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    //This method allows time-consuming functions to be executed without blocking the UI main thread
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.d(TAG, "onHandleIntent: Start Task");
        synchronized (this){
            try {
                wait(20*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        Log.d(TAG, "onHandleIntent: End Task");
    }
}

The MainActivity code is as follows:

public class MainActivity extends AppCompatActivity {
    private static String TAG = "MainActivity";
    private Button btn_start;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_start = findViewById(R.id.btn_start);

        btn_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Start as normal service.
                Intent intent = new Intent(MainActivity.this, com.ags.myapplication.SecondService.class);
                startService(intent);
            }
        });
    }
}

Since IntentService uses a separate worker thread, the UI main thread is not blocked.

6 Cross-process AIDL Service

Introduction to 6.1 aidl

Key Word Interpretation:

  • IPC (Inter-Process Communication): Cross-process communication for data exchange between processes.
  • AIDL (android interface definition language): The Android interface definition language is used to allow a Service to communicate across processes with multiple application components, thereby enabling multiple applications to share the same Service.

AIDL's purpose is to define a remote interface, that is, to define a communication interface between two processes.

Why do I need AIDL? Implementing cross-process requires a lot of complex code to be written, so android provides AIDL. By writing simple AIDL files, the compiler generates complex code based on AIDL rules, simplifying complexity is essential.

6.2 Interface Rules and Compilation

The data types supported by the Aidl interface file (.aidl is a suffix) are as follows:

  • Basic data types byte, char, short, int, long, float, double, boolean (default tag is in)
  • String, CharSequence (default tag is in)
  • Implement the data type of the Parcelable interface (import is required)
  • List type (content must be of type supported by AIDL, or an interface declared by other AIDL, requires import)
  • Map type (content must be an AIDL-supported type, or an interface to other AIDL declarations, requires import)

About directed tags: A directed tag in AIDL represents the direction of data flow in cross-process communication:

  • in indicates that data can only flow from the client to the server. It can be understood as client input and server output.
  • out means that data can only flow from the server to the client. It can be understood as server-side input and client-side output.
  • inout indicates that data can flow in both directions between the server and the client. Client/server can input/output.

Next, simply create a new aidl file, which reads as follows:

// IMyAidlInterface.aidl
package com.ags.myservice;

interface IMyAidlInterface {
    void testMethod1(String str);
    void testMethod2(int id);
}

Compile to generate IMyAidlInterface after making project. Java file.

6.3 Use of Aidl

There are two things to look at here: client (getting and using services) and service (implementing and providing services):

  • Key steps on the service side: Set up and define the aidl file - > Implement Interface Method and Lifecycle Method - > In AndroidMainfest. Register service in XML file & declare as remote service.
  • Client-side key steps: Copy the server-side AIDL file to the client directory - > Bind the specified Service through Intent - > Use Stub. The asInterface interface gets the Service-side Binder and calls the interface method provided by the Service.

The case is as follows:

@1 Server-side Service:

Here, create an aidl file that directly copies the above as follows:

// IMyAidlInterface.aidl
package com.ags.myservice;

interface IMyAidlInterface {
    void testMethod1(String str);
    void testMethod2(int id);
}

After compilation, the IMyAidlInterface is generated. Java. There's a lot of content here, just the framework section, as shown below:

package com.ags.myservice;
public interface IMyAidlInterface extends android.os.IInterface
{
  public static class Default implements com.ags.myservice.IMyAidlInterface
  {
    //...
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  public static abstract class Stub extends android.os.Binder implements com.ags.myservice.IMyAidlInterface
  {
    private static final java.lang.String DESCRIPTOR = "com.ags.myservice.IMyAidlInterface";
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    public static com.ags.myservice.IMyAidlInterface asInterface(android.os.IBinder 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
    {
      //...
    }
    private static class Proxy implements com.ags.myservice.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;
      }
	  //...
      public static com.ags.myservice.IMyAidlInterface sDefaultImpl;
    }
    static final int TRANSACTION_testMethod1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_testMethod2 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    //...
    public static com.ags.myservice.IMyAidlInterface getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public void testMethod1(java.lang.String str) throws android.os.RemoteException;
  public void testMethod2(int id) throws android.os.RemoteException;
}

With the IMyAidlInterface class, we create a Service that implements the methods in the aidl, as follows:

public class TestAIDLService extends Service {
    private static String TAG = "TestAIDLService";

    //Instantiate the tub class of AIDL (the Binder subclass) to implement the method in Aidl
    IMyAidlInterface.Stub mBinder = new  IMyAidlInterface.Stub(){
        @Override
        public void testMethod1(String str) throws RemoteException {
            Log.d(TAG, "service Impl:testMethod1:exec");
        }
        @Override
        public void testMethod2(int id) throws RemoteException {
            Log.d(TAG, "service Impl:testMethod1:exec");
        }
    };

    //life cycle functions
    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate: exec");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: exec");
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind: exec");
        //Returns a Binder of the tub type inherited from Binder
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnbind: exec");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy: exec");
        super.onDestroy();
    }
}

In AndroidMainfest. Register Service in XML - Declares as a remote service and is configured to:

<service
    android:name=".TestAIDLService"
    android:process=":remote"  
    android:exported="true">
    <intent-filter>
        <action android:name="com.ags.myservice.IMyAidlInterface"/>
    </intent-filter>
</service>

Be careful:

  • Here remote means that the local service is set to a remote service.
  • Here Intent's action must be written as "Server-side package name.aidl file name".

@2 Client MainActivity:

Copy and compile the package containing the server-side AIDL file into the client directory ($Project/app/src/main). Then write the code as follows:

public class MainActivity extends AppCompatActivity {
    private static String TAG = "MainActivity";
    private Button btn_start;
    private IMyAidlInterface aidlService;

    private ServiceConnection connection = new ServiceConnection(){
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            aidlService = IMyAidlInterface.Stub.asInterface(service);
            try {
                aidlService.testMethod1("testString");
                Log.d(TAG, "onServiceConnected");
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_start = findViewById(R.id.btn_start);
        btn_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Here the parameter is the same as the server-side action, which is "Server Package Name.aidl Interface File Name"
                Intent intent = new Intent("com.ags.myservice.IMyAidlInterface");
                //Note: Android5. Bind remote services only through explicit Intent after 0
                intent.setPackage("com.ags.myservice");//Specify Package Name
                bindService(intent, connection, Context.BIND_AUTO_CREATE);
            }
        });
    }
}

The process spans two applications, the caller is the client and the callee is the server. This AIDL communication case is complete.

More about AIDL can be found: Android Interface Definition Language (AIDL) for Android developers

7 System Service

This is the main explanation of getSystemService method, if Android applications can send text messages, process incoming calls, receive gyroscope data, and so on. Getting system services is essential, and getSystemService is an important API for Android. It is a method of Activity to get corresponding Object s based on the incoming NAME and get system services. Use the following:

public class MainActivity extends AppCompatActivity {
    private static String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TelecomManager telecomManager = (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
    }
}

Now that you have the system services, you can get the methods you need based on each of the different services. More about getSystemService can be found in the documentation: Android activity key method getSystemService detailed

8 Official Document Index

See the documentation for more about service: Service component of Android component

Keywords: Java Android Android Studio app

Added by Wabin on Wed, 19 Jan 2022 19:17:19 +0200