Easy Tutorial
❮ Shell Process Substitution Verilog2 Clock Division ❯

4.2.3 Mastering Service

Category Android Basic Tutorial

Introduction to This Section:

>

In this section, we will continue to explore the Service component. This section will introduce some concepts of AIDL (Android Interface Definition Language) for inter-process communication in Android, without delving into the source code level. For now, it's enough to understand what it is and how to use it. Let's get started with this section~ The corresponding official documentation: Binder


1. Introduction to Binder Mechanism


1) What are IBinder and Binder?

Let's see what the official documentation says:

>

IBinder is the basic interface for remote objects, designed as the core part of a lightweight remote procedure call mechanism for high performance. It is not only used for remote calls but also for in-process calls. This interface defines the protocol for interacting with remote objects. However, you should not directly implement this interface but extend (extends) Binder instead.

The main API of IBinder is transact(), and the corresponding API is Binder.onTransact(). With the former, you can send calls to a remote IBinder object, and the latter allows your remote object to respond to received calls. The APIs of IBinder are executed synchronously, such as transact() which only returns after the other party's Binder.onTransact() method has been called. When the call occurs within a process, this is undoubtedly the case, and between processes, it achieves the same effect with the help of IPC.

The data sent via transact() is Parcel, which is a general buffer not only containing data but also metadata describing its content. The metadata is used to manage references to IBinder objects, ensuring that when a Parcel moves from one process to another, these references are preserved. This ensures that when an IBinder is written to a Parcel and sent to another process, if that process sends back a reference to the same IBinder, the original process receives the reference to the IBinder it sent. This mechanism makes IBinder and Binder manage references across processes like unique identifiers.

The system maintains a thread pool for each process to dispatch all IPC calls coming from other processes. For example, when an IPC call is sent from process A to process B, the thread in A that made the call (which should not be in the thread pool) blocks on transact(). A thread from the interaction thread pool in process B receives the call, it calls Binder.onTransact(), and after completion, returns a Parcel as the result. Then the waiting thread in process A continues execution upon receiving the returned Parcel. In essence, another process appears as just another thread in the current process, but it is not created by the current process.

The Binder mechanism also supports recursive calls between processes. For example, process A executes its IBinder's transact() to call process B's Binder, and process B, in its Binder.onTransact(), makes another transact() call back to process A. Process A, while waiting for its call to return, will also respond to process B's transact() with Binder.onTransact(). The result of Binder is that it makes cross-process calls feel no different from in-process calls.

When operating on remote objects, you often need to check if they are valid. There are three ways to do this:

PS: The Chinese translation is excerpted from: Android Development: What is IBinder

Well, you might be feeling a bit overwhelmed after reading all that. Here's a simple summary:

IBinder is an interface provided by Android for inter-process communication, and we usually do not directly implement this interface, but instead inherit from the Binder class to achieve inter-process communication. It is a way to implement IPC (inter-process communication) in Android!


2) Brief Analysis of Binder Mechanism

>

The Binder mechanism in Android consists of several system components: Client, Server, Service Manager, and Binder driver

The general call flow is as follows, although Service Manager is complex and not detailed here!

Flow Analysis:

>

-> When Client calls a method in a proxy interface, the proxy interface method packages the parameters passed by Client into a Parcel object; -> Then the proxy interface sends this Parcel object to the Binder driver in the kernel; -> The Server then reads the request data from the Binder driver, and if it is intended for itself, unpacks the Parcel object, processes it, and returns the result; AIDL (Android Interface Definition Language)!


3) Why does Android use the Binder mechanism for inter-process communication?

>

Of course, as a novice developer, we don't care about the above. The most direct benefit that the Binder mechanism brings us is: We don't need to care about how the underlying implementation works, just follow the rules of AIDL, define an interface file, and then call the methods in the interface to complete inter-process communication!


2. Detailed Explanation of AIDL Usage


1) What is AIDL?

>

Hey, earlier we mentioned the term IPC, which stands for inter-process communication. In Android, each application runs in its own process, and processes generally cannot exchange data directly. To achieve inter-process communication, Android provides us with the Binder mechanism mentioned above, and the interface language used by this mechanism is: AIDL (Android Interface Definition Language). Its syntax is simple, and this interface language is not a true programming language but merely defines an interface for communication between two processes. The code that conforms to the communication protocol is generated by the aidl.exe tool in the platform-tools directory of the Android SDK, and the corresponding interface file is generated in the gen directory, typically an: Xxx.java interface! In this interface, there is an internal class called Stub, which implements the IBinder interface and the custom communication interface. This class will serve as the callback class for the remote Service—implementing the IBinder interface, so it can be the return value of the Service's onBind() method!


2) Simple Inter-process Communication with AIDL

Before starting to write the AIDL interface file, we need to be aware of some注意事项:

AIDL注意事项:

>

Additionally, if you are using Eclipse to write aidl, note: Do not create a new file directly and then establish it! This way, the file cannot be opened and cannot be written!

If you are using Android Studio, unlike Eclipse, if you create an AIDL file as you would in Eclipse, you will find that the corresponding XXX.java file is not compiled and generated. In AS, you need to create an aidl folder in the main directory, then define a package with the same name as the aidl package, and finally create an aidl file. Then press ctrl + f9 to recompile.

The successful compilation results for both are as follows, and you can find the corresponding AIDL files in the respective directories.


1. Server Side:

Step 1: Create an AIDL file:

IPerson.aidl

package com.jay.aidl;

interface IPerson {
    String queryPerson(int num);
}

Let's open IPerson.java to see the code inside:

IPerson.java

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: C:\\Code\\ASCode\\AIDLServer\\app\\src\\main\\aidl\\com\\jay\\aidl\\IPerson.aidl
 */
package com.jay.aidl;
public interface IPerson extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.jay.aidl.IPerson
{
private static final java.lang.String DESCRIPTOR = "com.jay.aidl.IPerson";
/** Construct the stub at attach it to the interface. */
public Stub()
{

This is an Chinese to English translation, please provide the English translation for this text. Do not provide any explanations or text apart from the translation. Chinese: this.attachInterface(this, DESCRIPTOR); } /**


Here we are only focusing on the **asInterface(IBinder)** method and the **queryPerson()** method defined in our interface!

This method converts an object of type IBinder to type IPerson, and generates a proxy object if necessary to return the result!

We can skip the rest and move on to the next step.

**Step 2:** Customize our Service class, complete the following operations:

1) Inherit the Service class, and also define a PersonQueryBinder class to **inherit the IPerson.Stub class**, which **implements the IPerson interface and the IBinder interface**

2) Instantiate the custom Stub class, and override the onBind method of Service to return a binder object!

**AIDLService.java**

package com.jay.aidlserver;

import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import com.jay.aidl.IPerson.Stub;

/**

private IBinder binder = new PersonQueryBinder();
private String[] names = {"B神","艹神","基神","J神","翔神"};

private String query(int num)
{
    if(num > 0 && num < 6){
        return names[num - 1];
    }
    return null;
}

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

private final class PersonQueryBinder extends Stub{
    @Override
    public String queryPerson(int num) throws RemoteException {
        return query(num);
    }
}

}


**Step 3:** Register the Service in the AndroidManifest.xml file

<service android:name=".AIDLService"> <intent-filter> <action android:name="android.intent.action.AIDLService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>


Here we do not provide an Activity interface, but the application provides a Service that can be called by other apps!

---

**2. Client** **Implement the ServiceConnection interface** **bindService(service, conn, BIND_AUTO_CREATE);** **The ServiceConnection for binding the remote Service cannot directly obtain the onBind() method of the Service** **onBind()** method returns a **proxy object**, which needs to be handled as follows: **iPerson = IPerson.Stub.asInterface(service);**

The specific code is as follows:

**MainActivity.java**

package com.jay.aidlclient;

import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView;

import com.jay.aidl.IPerson;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

private EditText edit_num;
private Button btn_query;
private TextView txt_name;
private IPerson iPerson;
private PersonConnection conn = new PersonConnection();


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    bindViews();
    // Bind the remote Service
    Intent service = new Intent("android.intent.action.AIDLService");
    service.setPackage("com.jay.aidlserver");

    bindService(service, conn, BIND_AUTO_CREATE);
    btn_query.setOnClickListener(this);
}

private void bindViews() {
    edit_num = (EditText) findViewById(R.id.edit_num);
    btn_query = (Button) findViewById(R.id.btn_query);
    txt_name = (TextView) findViewById(R.id.txt_name);
}

@Override
public void onClick(View v) {

String number = edit_num.getText().toString(); int num = Integer.valueOf(number); try { txt_name.setText(iPerson.queryPerson(num)); } catch (RemoteException e) { e.printStackTrace(); } edit_num.setText(""); }

private final class PersonConnection implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service) { iPerson = IPerson.Stub.asInterface(service); } public void onServiceDisconnected(ComponentName name) { iPerson = null; } } }


Next, start the AIDLServivce first, then start the AIDLClient, and input the query number to get the corresponding name!
Of course, you can also directly start the AIDLClient, and you will get the same effect:

**Effect diagram as follows:**
```java
public static final Parcelable.Creator<Salary> CREATOR = new Parcelable.Creator<Salary>() {
    // Read data from Parcel and return Salary object
    @Override
    public Salary createFromParcel(Parcel source) {
        return new Salary(source.readString(), source.readInt());
    }

    @Override
    public Salary[] newArray(int size) {
        return new Salary[size];
    }
};

public String toString() {
    return "Job: " + type + "    Salary: " + salary;
}

Step 3: Create an ISalary.aidl file and write a simple method to get salary information:

package com.jay.example.aidl;

import com.jay.example.aidl.Salary;
import com.jay.example.aidl.Person;
interface ISalary {
    // Define a Person object as an input parameter
    // When defining methods in the interface, specify the parameter passing mode, here it is input, so there is an 'in'
    Salary getMsg(in Person owner);
}

Note: Remember to import custom data types if used!!!

Step 4: Write the core Service: Define a SalaryBinder class that extends Stub, thereby implementing the ISalary and IBinder interfaces; define a Map collection to store information! Override the onBind method to return an instance of the SalaryBinder class!

AidlService.java

package com.jay.example.aidl_complexservice;

import java.util.HashMap;
import java.util.Map;
import com.jay.example.aidl.ISalary.Stub;
import com.jay.example.aidl.Person;
import com.jay.example.aidl.Salary;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class AidlService extends Service {

    private SalaryBinder salaryBinder;
    private static Map&lt;Person, Salary> ss = new HashMap&lt;Person, Salary>();
    // Initialize the Map collection, this is done in a static block, but you can also do it in the constructor
    static {
        ss.put(new Person(1, "Jay"), new Salary("Coder", 2000));
        ss.put(new Person(2, "GEM"), new Salary("Singer", 20000));
        ss.put(new Person(3, "XM"), new Salary("Student", 20));
        ss.put(new Person(4, "MrWang"), new Salary("Teacher", 2000));
    }

    @Override
    public void onCreate() {
        super.onCreate();
        salaryBinder = new SalaryBinder();
    }

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

    // Extends Stub, thus implementing both ISalary and IBinder interfaces
    public class SalaryBinder extends Stub {
        @Override
        public Salary getMsg(Person owner) throws RemoteException {
            return ss.get(owner);
        }
    }

    @Override
    public void onDestroy() {
        System.out.println("Service ended!");
        super.onDestroy();
    }
}

Register the Service:

<service android:name=".AidlService">
    <intent-filter>
        <action android:name="android.intent.action.AIDLService" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

2 — Client Writing

Step 1: Copy the AIDL files from the server, the directory after copying is as follows:

Step 2: Write a simple layout, then implement the core MainActivity: Define a ServiceConnection object, override the corresponding methods, similar to the previous ordinary data Then bindService, and then get the Salary object and display it in the Button's click event!

MainActivity.java

package com.jay.example.aidl_complexclient;

import com.jay.example.aidl.ISalary;
import com.jay.example.aidl.Person;
import com.jay.example.aidl.Salary;

import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity {

    private ISalary salaryService;
    private Button btnquery;
    private EditText editname;
    private TextView textshow;
    private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            salaryService = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // Returns the proxy object, you need to call this method!
            salaryService = ISalary.Stub.asInterface(service);
        }
    };

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

        btnquery = (Button) findViewById(R.id.btnquery);
        editname = (EditText) findViewById(R.id.editname);
        textshow = (TextView) findViewById(R.id.textshow);

        Intent it = new Intent();
        it.setAction("com.jay.aidl.AIDL_SERVICE");
        bindService(it, conn, Service.BIND_AUTO_CREATE);

        btnquery.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    String name = editname.getText().toString();
                    Salary salary = salaryService.getMsg(new Person(1, name));
                    textshow.setText(name + salary.toString());
try {
    // Code that might throw a RemoteException
} catch (RemoteException e) {
    e.printStackTrace();
}

Screenshot of Execution:

Follow on WeChat

❮ Shell Process Substitution Verilog2 Clock Division ❯