Android AIDL learning (client)

Move up   Android AIDL learning (server) Today, let's look at how the client implements the call.

Let's first create a client project, AClient

Development environment: Android Studio 3.6.2

Where to keep it, you can keep it anywhere according to your habits.

 

  To realize the call of AIDL, we need to provide AIDL service as our AIDL interface file and the necessary categories.

The BService server needs to provide me with the slow AIDL interface and the definition of the Book class used.

You also need to provide us with the action of calling the service: com.example.bservice.bookService

AIDL interface provided by the server: IMyBookManager

// IMyBookManager.aidl
package com.example.bservice;

// Declare any non-default types here with import statements
//All package names need to be imported.
import com.example.bservice.Book;

//It should be noted that the book we introduced is a serialized class.
parcelable Book;

interface IMyBookManager {
    /**
     * The basic type interface added by default can be deleted if not used
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    String login(String userName, String pwd);

    Book queryByName(String bookName);
}

The server provides the definition of the Book class

package com.example.bservice;

import android.os.Parcel;
import android.os.Parcelable;

import androidx.annotation.NonNull;

public class Book implements Parcelable {
    private int price;
    private String name = "";

    public Book(){}

    protected Book(Parcel in) {
        price = in.readInt();
        name = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

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

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @NonNull
    @Override
    public String toString() {
        return "name:" + name + ",price:" + price;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(price);
        dest.writeString(name);
    }
}

We added these two classes to our project

Right click main and select Create a new folder named aidl

Right click didl to create a new package   com.example.bservice is the registration of AIDL files provided by the service. It must be the same.

In this package, we add the aidl file given to us by the server

 

Then paste the interface content of the server into the file.  

After processing the interface file, let's process the class Book returned from the interface file.

Create a new package com.example.bservice in src\main\java \;

Right click the new package to create a new Book class.

  Save the structure of the Book class provided by the server to a file.

  As like as two peas, we can add the interface files and class files to the server. We add them to our project exactly as required.

Since AIDL provides services externally by binding services, the first step is to start the binding service and obtain the returned binder object after the service is started. After obtaining the object, you can complete the corresponding work by calling the interface provided by the object.

We implement a simple call function, with a connection button to bind the service, a login to simulate login verification, a book query function and display the returned book information.

Well, I'll implement this function from the layout file of mainactivity.

The code is as follows

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btnConnection"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="140dp"
        android:layout_marginTop="52dp"
        android:text="connect"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/txtConnectionState"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="38dp"
        app:layout_constraintBottom_toBottomOf="@+id/btnConnection"
        app:layout_constraintStart_toEndOf="@+id/btnConnection"
        app:layout_constraintTop_toTopOf="@+id/btnConnection"
        tools:text="Connection status" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="44dp"
        android:layout_marginTop="160dp"
        android:text="user name:"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="60dp"
        android:text="password:"
        app:layout_constraintStart_toStartOf="@+id/textView2"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />

    <EditText
        android:id="@+id/etName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="40dp"
        android:ems="10"
        android:hint="Login user name"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toBottomOf="@+id/textView2"
        app:layout_constraintStart_toEndOf="@+id/textView2"
        app:layout_constraintTop_toTopOf="@+id/textView2" />

    <EditText
        android:id="@+id/etPwd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="password"
        android:inputType="textPassword"
        app:layout_constraintBottom_toBottomOf="@+id/textView3"
        app:layout_constraintStart_toStartOf="@+id/etName"
        app:layout_constraintTop_toTopOf="@+id/textView3"
        app:layout_constraintVertical_bias="0.423" />

    <Button
        android:id="@+id/btnLogin"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="44dp"
        android:layout_marginTop="56dp"
        android:text="Sign in"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView3" />

    <TextView
        android:id="@+id/etLoginState"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="72dp"
        app:layout_constraintBottom_toBottomOf="@+id/btnLogin"
        app:layout_constraintStart_toEndOf="@+id/btnLogin"
        app:layout_constraintTop_toTopOf="@+id/btnLogin"
        app:layout_constraintVertical_bias="0.517"
        tools:text="Login status" />

    <EditText
        android:id="@+id/etBookInfo"
        android:layout_width="303dp"
        android:layout_height="141dp"
        android:layout_marginTop="36dp"
        android:ems="10"
        android:gravity="start|top"
        android:hint="Display the contents of the queried books"
        android:inputType="textMultiLine"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/etBookName" />

    <TextView
        android:id="@+id/textView5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="60dp"
        android:text="title"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btnLogin" />

    <Button
        android:id="@+id/btnQuery"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="query"
        app:layout_constraintBottom_toBottomOf="@+id/textView5"
        app:layout_constraintStart_toEndOf="@+id/etBookName"
        app:layout_constraintTop_toTopOf="@+id/textView5"
        app:layout_constraintVertical_bias="0.517" />

    <EditText
        android:id="@+id/etBookName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="17dp"
        android:ems="10"
        android:hint="Enter the title of the book to query"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toBottomOf="@+id/textView5"
        app:layout_constraintStart_toEndOf="@+id/textView5"
        app:layout_constraintTop_toTopOf="@+id/textView5" />
</androidx.constraintlayout.widget.ConstraintLayout>

This is a learning example of connecting AIDL. Don't worry too much about whether the specific functions are meaningful.

After the interface definition is completed, let's take a look at the specific diam implementation

Let me first define the controls I need.

package com.example.aclient;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private Button btnConnection;
    private TextView txtConnectionState;
    private EditText etName;
    private EditText etPwd;
    private Button btnLogin;
    private TextView etLoginState;
    private EditText etBookInfo;
    private Button btnQuery;
    private EditText etBookName;

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

        initViews();
    }

    private void initViews(){
        btnConnection = findViewById(R.id.btnConnection);
        txtConnectionState = findViewById(R.id.txtConnectionState);
        etName = findViewById(R.id.etName);
        etPwd = findViewById(R.id.etPwd);
        btnLogin = findViewById(R.id.btnLogin);
        etLoginState = findViewById(R.id.etLoginState);
        etBookInfo = findViewById(R.id.etBookInfo);
        btnQuery = findViewById(R.id.btnQuery);
        etBookName = findViewById(R.id.etBookName);
    }
}

After defining the control, let's see how the connection is implemented. Add click events to btnConnection and invoke bindService to open the service.

 boolean bindService( Intent service,   ServiceConnection conn,  int flags);

The development service requires three parameters

Service passes in the service to be bound.

conn returns the binding object after successful connection.

When writing to the server, the server defines an action: com.example.bservice.bookService

Define a ServiceConnection object conn;

public class MainActivity extends AppCompatActivity {

//Control definition
......

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

......


}

Define another IMyBookManager to accept the object returned by the connection, and this object is also required for subsequent calls.

public class MainActivity extends AppCompatActivity {

//Control definition
......

private IMyBookManager bookManager = null;

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = IMyBookManager.Stub.asInterface(service);
            txtConnectionState.setText("Connection succeeded!");
        }

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

......


}

Let me implement the click event of btnConnection button.

        btnConnection.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Intent intent = new Intent();
                intent.setAction("com.example.bservice.bookService");
                intent.setPackage("com.example.bservice");
                bindService(intent, conn, Context.BIND_AUTO_CREATE);
                
            }
        });

In the event, we define an intent and send bindSevice for binding.

Let me implement the login event

btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(bookManager == null)
                {
                    Toast.makeText(MainActivity.this, "The service has not been bound.", Toast.LENGTH_LONG).show();
                    return;
                }
                if(etName.getText().toString().isEmpty() || etPwd.getText().toString().isEmpty()){
                    Toast.makeText(MainActivity.this, "User name or password cannot be empty.", Toast.LENGTH_LONG).show();
                    return;
                }

                try {
                    String resStr = bookManager.login(etName.getText().toString(), etPwd.getText().toString());

                    if(resStr.compareToIgnoreCase("success") == 0){
                        etLoginState.setText("Login succeeded!");
                   } else{
                        throw new RemoteException("Login failed");
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                    etLoginState.setText("Login succeeded!");
                }

            }
        });

Finally, let's implement the event of book query:

btnQuery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(bookManager == null)
                {
                    Toast.makeText(MainActivity.this, "The service has not been bound.", Toast.LENGTH_LONG).show();
                    return;
                }
                if(etBookName.getText().toString().isEmpty()){
                    Toast.makeText(MainActivity.this, "Please enter the book you want to find.", Toast.LENGTH_LONG).show();
                    return;
                }
                try {
                    Book book = bookManager.queryByName(etBookName.getText().toString());

                    if(book != null){
                        etBookInfo.setText(book.toString());
                    } else{
                        throw new RemoteException("Query failed");
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                    etBookInfo.setText("Query failed");
                }
            }
        });

All codes:

package com.example.aclient;

import android.content.ComponentName;
import android.content.Context;
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.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.example.bservice.Book;
import com.example.bservice.IMyBookManager;

public class MainActivity extends AppCompatActivity {

    private Button btnConnection;
    private TextView txtConnectionState;
    private EditText etName;
    private EditText etPwd;
    private Button btnLogin;
    private TextView etLoginState;
    private EditText etBookInfo;
    private Button btnQuery;
    private EditText etBookName;


    private IMyBookManager bookManager = null;

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = IMyBookManager.Stub.asInterface(service);
            txtConnectionState.setText("Connection succeeded!");
        }

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

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

        initViews();
    }

    private void initViews(){
        btnConnection = findViewById(R.id.btnConnection);
        btnConnection.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Intent intent = new Intent();
                intent.setAction("com.example.bservice.bookService");
                intent.setPackage("com.example.bservice");
                bindService(intent, conn, Context.BIND_AUTO_CREATE);

            }
        });

        txtConnectionState = findViewById(R.id.txtConnectionState);
        etName = findViewById(R.id.etName);
        etPwd = findViewById(R.id.etPwd);
        btnLogin = findViewById(R.id.btnLogin);
        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(bookManager == null)
                {
                    Toast.makeText(MainActivity.this, "The service has not been bound.", Toast.LENGTH_LONG).show();
                    return;
                }
                if(etName.getText().toString().isEmpty() || etPwd.getText().toString().isEmpty()){
                    Toast.makeText(MainActivity.this, "User name or password cannot be empty.", Toast.LENGTH_LONG).show();
                    return;
                }

                try {
                    String resStr = bookManager.login(etName.getText().toString(), etPwd.getText().toString());

                    if(resStr.compareToIgnoreCase("success") == 0){
                        etLoginState.setText("Login succeeded!");
                   } else{
                        throw new RemoteException("Login failed");
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                    etLoginState.setText("Login succeeded!");
                }

            }
        });

        etLoginState = findViewById(R.id.etLoginState);
        etBookInfo = findViewById(R.id.etBookInfo);
        btnQuery = findViewById(R.id.btnQuery);
        btnQuery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(bookManager == null)
                {
                    Toast.makeText(MainActivity.this, "The service has not been bound.", Toast.LENGTH_LONG).show();
                    return;
                }
                if(etBookName.getText().toString().isEmpty()){
                    Toast.makeText(MainActivity.this, "Please enter the book you want to find.", Toast.LENGTH_LONG).show();
                    return;
                }
                try {
                    Book book = bookManager.queryByName(etBookName.getText().toString());

                    if(book != null){
                        etBookInfo.setText(book.toString());
                    } else{
                        throw new RemoteException("Query failed");
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                    etBookInfo.setText("Query failed");
                }
            }
        });
        etBookName = findViewById(R.id.etBookName);
    }
}

Operation results

 

Tags: Android

Posted on Mon, 22 Nov 2021 14:00:48 -0500 by jtp51