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