Android Studio實現(xiàn)進程間通信AIDL詳細(xì)步驟

從事Android開發(fā)已經(jīng)好幾個年頭了微服,寫博客的想法從沒有忘記過,而寫博客的行動從沒有落實過缨历。實現(xiàn)一個功能第一次踩了很多坑以蕴,過段時間實現(xiàn)同樣的功能還會踩同樣的坑,很大一部分原因是因為沒有整理辛孵、總結(jié)丛肮。所以我覺得非常有必要將自己掌握的東西總結(jié)出來,提升自己能力的同時也能幫助到別人魄缚。廢話不多說宝与,本篇將是我的第一篇技術(shù)博客(從老土的標(biāo)題應(yīng)該就能看出來??),詳細(xì)講解Android studio實現(xiàn)AIDL通信步驟冶匹。

概述

Android IPC方式有:Intent附加extras傳遞信息习劫、通過共享文件方式共享數(shù)據(jù)、Messenger嚼隘、AIDL诽里、ContentProvider、Socket飞蛹。AIDL相比于其他幾種IPC方式有很多優(yōu)點:功能強大须肆,支持一對多并發(fā)通信,支持實時通訊桩皿,可以調(diào)用其他進程的方法豌汇,應(yīng)用場景廣泛,但缺點就是使用稍微復(fù)雜泄隔,不過沒有關(guān)系拒贱,本篇將詳細(xì)講解AIDL實現(xiàn)步驟,只要動手實現(xiàn)一次就會顯得簡單了很多。

舉個應(yīng)用場景的栗子:我的小米逻澳、小米商城闸天、小米金融、小米視頻都屬于MIUI系統(tǒng)的預(yù)裝應(yīng)用斜做,它們都是獨立的apk苞氮,當(dāng)我們打開我的小米,注冊登陸之后瓤逼,再打開小米其他預(yù)裝應(yīng)用比如小米視頻笼吟,會發(fā)現(xiàn)小米視頻已經(jīng)登陸了,這是怎么回事呢霸旗?

其實這就涉及到了AIDL贷帮,當(dāng)打開小米視頻的時候,小米視頻(進程)會去調(diào)用我的小米(進程)里的方法判斷是否登陸诱告,如果登陸還會獲取登陸信息(如用戶名撵枢,昵稱等),這樣就實現(xiàn)了小米視頻自動登陸了精居。

說明

  • 本篇重點講解studio實現(xiàn)AIDL步驟锄禽,不涉及到AIDL原理;
  • 本篇通過兩個apk實現(xiàn)基于AIDL的IPC靴姿,更切合實際應(yīng)用場景沃但;
  • 本篇先實現(xiàn)服務(wù)端AIDLService的編寫,再實現(xiàn)客戶端AIDLClient的編寫空猜;
  • 本篇實現(xiàn)的功能是客戶端調(diào)用服務(wù)端方法獲取圖書列表和向服務(wù)端添加圖書绽慈;
  • 本篇將會詳細(xì)講解每個步驟恨旱;
  • 文章末尾附上源碼辈毯;
  • thanks 《Android第一行代碼》、《Android開發(fā)藝術(shù)探索》搜贤;

服務(wù)端實現(xiàn)詳細(xì)步驟

1谆沃、新建項目AIDLService

注:服務(wù)端的包名是com.glong.aidlservice

2仪芒、新建Book.java唁影,實現(xiàn)可序列化,提供get/set方法

Book.java

package com.glong.aidlservice;

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

public class Book implements Parcelable {
    private int bookId;
    private String bookName;

    public Book(){
    }

    public Book(int bookId,String bookName){
        this.bookId = bookId;
        this.bookName = bookName;
    }


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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.bookId);
        dest.writeString(this.bookName);
    }

    protected Book(Parcel in) {
        this.bookId = in.readInt();
        this.bookName = in.readString();
    }

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

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

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }
}

這一步很簡單掂名,五秒搞定据沈,還不能五秒搞定的童鞋戳這里

3、新建AIDL Folder

生成的aidl文件夾跟java文件夾屬于同一層級

4饺蔑、在aidl目錄下新建IBookManager.aidl

IDE會自動在aidl文件夾下創(chuàng)建com.glong.aidlservice包锌介,并在該包下幫我們生成IBookManager.aidl

我們稍做改動增加兩個接口,getBookList()獲取圖書列表 addBook(in Book book)向服務(wù)端添加一本書

IBookManager.aidl

package com.glong.aidlservice;
import com.glong.aidlservice.Book;
// Declare any non-default types here with import statements

interface IBookManager {

    /*
     * 向客戶端提供獲取圖書列表接口
     */
    List<Book> getBookList();

    /*
     * 向客戶端提供添加圖書接口
     */
    void addBook(in Book book);
}

注意:

  • 第二行顯式import了Book類,AIDL中所有Parcelable對象要顯式的import進來孔祸,而此處我們接口中使用到了自定義的Parcelable對象Book類型隆敢;

  • 在AIDL文件中用到了自定義的Parcelable對象時,必須新建一個和該對象同名的.aidl文件崔慧,所以接下來我們還需新建Book.aidl拂蝎;

    以上兩點如果沒有import或者import包名有誤或者沒有創(chuàng)建Parcelable對象對應(yīng)的.aidl文件,項目就不能夠編譯通過惶室,會報"Process 'command 'D:\SDK\build-tools\27.0.3\aidl.exe'' finished with non-zero exit value 1"錯誤温自,找不到對應(yīng)的對象;

  • addBook()方法形參前的in表示方向拇涤。AIDL中除基本數(shù)據(jù)類型捣作,其他類型的參數(shù)必須標(biāo)上方向:in、out或者inout鹅士,in表示輸入性參數(shù)券躁,out表示輸出型參數(shù),inout表示輸入輸出型參數(shù)掉盅;

5也拜、在aidl目錄下的com.glong.aidlservice包下新建Book.aidl

注意如果直接New → ADIL→ AIDL File 然后輸入Book,IDE會提示"interface Name must be unique"趾痘,我們使用如下步驟創(chuàng)建Book.aidl慢哈;




Book.aidl

package com.glong.aidlservice;

parcelable Book;

6、Build編譯項目

編譯完成后將項目結(jié)構(gòu)模式切換成Project永票,在build文件夾下可以看到IDE幫我們生成了IBookManager.java由于本篇重點將步驟卵贱,這個文件不多做講解,項目結(jié)構(gòu)重新切回到Android模式

7侣集、實現(xiàn)遠(yuǎn)程Service

在java文件夾的com.glong.aidlservice包下新建BookManagerService.java

BookManagerService.java

package com.glong.aidlservice;

import android.app.Service;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import com.glong.aidlservice.database.MyDatabaseHelper;

import java.util.ArrayList;
import java.util.List;

public class BookManagerService extends Service {
    private static final String TAG = "guo.BookManagerService";

    private SQLiteDatabase database;
    private List<Book> mBookList = new ArrayList<>();

    private Binder mBinder = new IBookManager.Stub() {

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            addBookIntoDB(book);
        }
    };

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

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

        queryBooksFromDB();
    }

    /**
     * 從數(shù)據(jù)庫中獲取Book列表键俱,數(shù)據(jù)庫操作{@link MyDatabaseHelper}
     */
    private void queryBooksFromDB() {
        Log.i(TAG, "service starting query Book from db.");
        MyDatabaseHelper dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
        database = dbHelper.getReadableDatabase();
        Cursor cursor = database.query("Book", null, null, null, null, null, null);
        if (cursor.moveToFirst()) {
            do {
                int bookId = cursor.getInt(cursor.getColumnIndex("bookId"));
                String bookName = cursor.getString(cursor.getColumnIndex("bookName"));
                Book book = new Book(bookId, bookName);
                mBookList.add(book);
            } while (cursor.moveToNext());
        }
        cursor.close();
    }

    private void addBookIntoDB(Book book) {
        Log.i(TAG, "service starting add Book to db.");
        ContentValues values = new ContentValues();
        values.put("bookId", book.getBookId());
        values.put("bookName", book.getBookName());
        database.insert("Book", null, values);
    }
}

服務(wù)端的Book列表存儲在數(shù)據(jù)庫中燎字,當(dāng)客戶端遠(yuǎn)程連接服務(wù)端時硬贯,BookManagerService服務(wù)開啟,并且在onCreate()中查找數(shù)據(jù)庫初始化mBookList圖書列表工碾,下面再附上MyDatabaseHelper.java

package com.glong.aidlservice.database;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class MyDatabaseHelper extends SQLiteOpenHelper {

    private static final String TAG = "long.MyDatabaseHelper";

    private static final String CREATE_BOOK = "create table Book (" +
            "id integer primary key autoincrement," +
            "bookId integer," +
            "bookName text)";

    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(CREATE_BOOK);
        Log.i(TAG, "Book database create succeeded!");
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
}

代碼很簡單臭埋,就是簡單的創(chuàng)建數(shù)據(jù)庫踪央。
最后不要忘記在AndroidMenifest.xml中配置該服務(wù),并且指定啟動的Action瓢阴,以便客戶端通過此Action啟動遠(yuǎn)程服

    <service android:name=".BookManagerService">
        <intent-filter>
            <action android:name="com.glong.aidlservice.action.BookManagerService" />
        </intent-filter>
    </service>

8畅蹂、給數(shù)據(jù)庫添加數(shù)據(jù),方面后面我們后面驗證客戶端是否請求到了服務(wù)端的數(shù)據(jù)

修改MainActivity.java代碼:

package com.glong.aidlservice;

import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import com.glong.aidlservice.database.MyDatabaseHelper;

public class MainActivity extends AppCompatActivity {

    private SQLiteDatabase database;

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

        MyDatabaseHelper dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
        database = dbHelper.getReadableDatabase();

        //如果數(shù)據(jù)庫為空就向數(shù)據(jù)庫添加幾個數(shù)據(jù)
        if (!database.query("Book", null, null, null, null, null, null).moveToFirst()) {
            addBooksToDB();
        }
    }

    private void addBooksToDB() {
        ContentValues values = new ContentValues();
        values.put("bookId", 0);
        values.put("bookName", "玉女真經(jīng)");
        database.insert("Book", null, values);
        values.clear();

        values.put("bookId", 1);
        values.put("bookName", "辟邪劍法");
        database.insert("Book", null, values);
        values.clear();

        values.put("bookId", 2);
        values.put("bookName", "如來神掌");
        database.insert("Book", null, values);
        values.clear();

        values.put("bookId", 3);
        values.put("bookName", "降龍十八掌");
        database.insert("Book", null, values);
        values.clear();

        values.put("bookId", 4);
        values.put("bookName", "九陰真經(jīng)");
        database.insert("Book", null, values);
        values.clear();

        values.put("bookId", 5);
        values.put("bookName", "九陽神功");
        database.insert("Book", null, values);
        values.clear();

        values.put("bookId", 6);
        values.put("bookName", "打狗棒法");
        database.insert("Book", null, values);
        values.clear();

        values.put("bookId", 7);
        values.put("bookName", "乾坤大挪移");
        database.insert("Book", null, values);
        values.clear();

        values.put("bookId", 8);
        values.put("bookName", "易筋經(jīng)");
        database.insert("Book", null, values);
        values.clear();

        values.put("bookId", 9);
        values.put("bookName", "六脈神劍");
        database.insert("Book", null, values);
        values.clear();

        values.put("bookId", 10);
        values.put("bookName", "黯然銷魂掌");
        database.insert("Book", null, values);
        values.clear();

        values.put("bookId", 11);
        values.put("bookName", "九陰白骨爪");
        database.insert("Book", null, values);
        values.clear();

        values.put("bookId", 12);
        values.put("bookName", "獨孤九劍");
        database.insert("Book", null, values);
    }
}

同樣代碼很簡單荣恐,先在onCreate()方法中獲取database對象液斜,判斷如果database沒有數(shù)據(jù),則調(diào)用addBooksToDB()方法添加十幾本書(加上這個判斷就不會出現(xiàn)每進入一次服務(wù)端應(yīng)用,數(shù)據(jù)庫就多一組重復(fù)的數(shù)據(jù))旗唁。

到此畦浓,服務(wù)端已經(jīng)寫完了,下面開始寫客戶端检疫。

客戶端實現(xiàn)詳細(xì)步驟

客戶端的實現(xiàn)前三步和服務(wù)端一樣讶请,這里依舊貼上步驟

1、新建項目AIDLClient

注:客戶端的包名是com.glong.aidlclient

2屎媳、新建Book.java夺溢,實現(xiàn)可序列化,提供get/set方法

和服務(wù)端Book.java保持一致

Book.java

package com.glong.aidlclient;

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

public class Book implements Parcelable {
    private int bookId;
    private String bookName;

    public Book() {
    }

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.bookId);
        dest.writeString(this.bookName);
    }

    protected Book(Parcel in) {
        this.bookId = in.readInt();
        this.bookName = in.readString();
    }

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

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

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }
}

3烛谊、新建AIDL Folder

4风响、在aidl目錄下新建IBookManager.aidl

這里需要注意兩點:

  • 客戶端的IBookManager.aidl必須跟服務(wù)端的IBookMananger.aidl包名相同,否則客戶端運行連接服務(wù)端時會報SecturityException丹禀;

  • 客戶端的IBookManager.aidl必須跟服務(wù)端的IBookManager.aidl擁有相同的方法和方法數(shù)状勤,否則在通過AIDL進行IPC時會出現(xiàn)亂碼等現(xiàn)象;

    總之双泪,客戶端和服務(wù)端的IBookManager.aidl保持一致(除import的Parcelable對象部分)持搜,包括包名和aidl文件夾一下的路徑,所以這一步的創(chuàng)建IBookManager.aidl和服務(wù)端的創(chuàng)建稍有不同焙矛,aidl文件夾下 → New → Package

然后輸入服務(wù)端IBookManager.aidl的包名葫盼,點擊OK

接著在該包下創(chuàng)建IBookMananger.aidl,New → FIle

接下來實現(xiàn)IBookManager.aidl村斟,或者copy服務(wù)端IBookManager.aidl代碼贫导,修改下import的包即可(import的包和當(dāng)前工程的包對應(yīng)

IBookManager.aidl

package com.glong.aidlservice;

import com.glong.aidlclient.Book;
interface IBookManager{

    /*
     * 獲取Book列表
     */
    List<Book> getBookList();

    /*
     *添加Book
     */
     List<Book> addBook(in Book book);

}

可以看到IBookManager.aidl文件包名和服務(wù)端包名一樣
值得注意的是import com.glong.aidlclient.Book和服務(wù)端的略有區(qū)別,import的包名和當(dāng)前工程包名須對應(yīng)蟆盹,所以接下來創(chuàng)建的Book.aidlcom.glong.aidlclient包下

5孩灯、在aidl目錄下新建com.glong.aidlclient包,并在該包下新建Book.aidl

先新建 Package com.glong.aidlclient

新建com.glong.aidlclient

接著在com.glong.aidlclient包下新建Book.aidl日缨,新建之后工程目錄是這樣的



Book.aidl

package com.glong.aidlclient;

parcelable Book;

6钱反、Build編譯項目

這一步跟服務(wù)端一樣掖看,不過多贅述

7匣距、客戶端實現(xiàn)

  • 首先綁定遠(yuǎn)程服務(wù)

  • 綁定成功后將服務(wù)端返回的Binder對象轉(zhuǎn)換成AIDL接口

  • 通過這個AIDL接口調(diào)用遠(yuǎn)程方法

MainActivity.java

package com.glong.aidlclient;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import com.glong.aidlservice.IBookManager;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "long.MainActivity";
    private static final String SERVICE_ACTION = "com.glong.aidlservice.action.BookManagerService";
    private static final String SERVICE_PACKAGE = "com.glong.aidlservice";

    private IBookManager mBookManager;

    ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mBookManager = IBookManager.Stub.asInterface(iBinder);
            try {
                List<Book> books = mBookManager.getBookList();
                if (books == null || books.size() == 0) return;
                for (int i = 0; i < books.size(); i++) {
                    Book book = books.get(i);
                    Log.i(TAG, i + ".[bookId:" + book.getBookId() + ",bookName:" + book.getBookName() + "]");
                }
            } catch (RemoteException e) {
                e.printStackTrace();
                Toast.makeText(MainActivity.this, "獲取Book列表失敗", Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

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

    /**
     * 連接遠(yuǎn)程服務(wù)
     */
    private void bindService() {
        Intent intent = new Intent();
        intent.setPackage(SERVICE_PACKAGE);
        intent.setAction(SERVICE_ACTION);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }
}

代碼依舊很簡單,這里使用隱式的方式綁定遠(yuǎn)程服務(wù)哎壳,在onServiceConnected()回調(diào)方法中獲取服務(wù)端Book列表并打印出來毅待。

到這里,服務(wù)端的實現(xiàn)也完成了归榕,以上是整個AIDL實現(xiàn)步驟尸红,后面內(nèi)容屬于驗證和擴展可以不用關(guān)心

驗證

  • run AIDLService工程
  • run AIDLClient工程
  • 查看日志
驗證

我們調(diào)用遠(yuǎn)程服務(wù)的接口成功了!

擴展

  • 客戶端調(diào)用遠(yuǎn)程服務(wù)getBookList()接口返回數(shù)據(jù)后,展示數(shù)據(jù);
  • 客戶端調(diào)用addBook(Book book)接口增加圖書

修改activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
    tools:context=".MainActivity">


    <Button
        android:id="@+id/btn_book_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="獲取Book列表"
        android:background="#603300cc"
        android:textSize="22sp" />

    <Button
        android:id="@+id/btn_book_add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right|bottom"
        android:background="#903300cc"
        android:text="添加圖書"
        android:textSize="22sp"
        android:visibility="gone" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view_book_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />


</FrameLayout>

兩個Button分別對應(yīng)獲取圖書列表功能和添加圖書功能外里,RecyclerView展示圖書列表

1怎爵、實現(xiàn)展示圖書列表功能

MyAdapter.java

package com.glong.aidlclient;

import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.List;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private List<Book> mBooks;

    public MyAdapter(List<Book> books) {
        mBooks = books;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_view, viewGroup, false);
        //這里給view設(shè)置一下間隙
        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams();
        lp.setMargins(0, 10, 0, 10);
        view.setBackgroundColor(Color.YELLOW);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
        Book book = mBooks.get(i);
        viewHolder.bookIdTextView.setText("\t\t" + (i + 1) + "." + "《" + book.getBookName() + "》");
        viewHolder.bookNameTextView.setText("ID:" + String.format("%08d", book.getBookId()));
    }

    @Override
    public int getItemCount() {
        return mBooks.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder {

        TextView bookIdTextView;
        TextView bookNameTextView;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            bookIdTextView = itemView.findViewById(R.id.tv_book_id);
            bookNameTextView = itemView.findViewById(R.id.tv_book_name);
        }
    }
}

item_view.xml

<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/tv_book_id"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center_vertical"
        android:minHeight="?android:attr/listPreferredItemHeightSmall"
        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        tools:ignore="RtlCompat" />

    <TextView
        android:id="@+id/tv_book_name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center_vertical"
        android:minHeight="?android:attr/listPreferredItemHeightSmall"
        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
        android:textAppearance="?android:attr/textAppearanceListItemSmall" />

</LinearLayout>

修改MainAcitivty.java

package com.glong.aidlclient;

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.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.glong.aidlservice.IBookManager;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "long.MainActivity";
    private static final String SERVICE_ACTION = "com.glong.aidlservice.action.BookManagerService";
    private static final String SERVICE_PACKAGE = "com.glong.aidlservice";

    private IBookManager mBookManager;

    private Button mBtnRequestBooks;
    private Button mBtnAddBook;
    private RecyclerView mRecyclerView;

    private List<Book> mBooks = new ArrayList<>();
    private MyAdapter mAdapter;

    private AddBookDialog mDialog;

    ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mBookManager = IBookManager.Stub.asInterface(iBinder);
            try {
                List<Book> list = mBookManager.getBookList();
                if (list != null && list.size() > 0) {
                    mBooks.addAll(list);
                }
                mRecyclerView.setVisibility(View.VISIBLE);
                mBtnRequestBooks.setVisibility(View.GONE);
                mBtnAddBook.setVisibility(View.VISIBLE);
                mAdapter.notifyDataSetChanged();
            } catch (RemoteException e) {
                e.printStackTrace();
                Toast.makeText(MainActivity.this, "獲取Book列表失敗", Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Toast.makeText(MainActivity.this, "onServiceDisconnected!", Toast.LENGTH_SHORT).show();
        }
    };

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

        initViews();
        initRecyclerView();
    }

    private void initViews() {
        mBtnAddBook = findViewById(R.id.btn_book_add);
        mBtnRequestBooks = findViewById(R.id.btn_book_list);

        mBtnRequestBooks.setOnClickListener(this);
        mBtnAddBook.setOnClickListener(this);
    }

    private void initRecyclerView() {
        mRecyclerView = findViewById(R.id.recycler_view_book_list);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(linearLayoutManager);
        mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
        mAdapter = new MyAdapter(mBooks);
        mRecyclerView.setAdapter(mAdapter);
    }

    /**
     * 連接遠(yuǎn)程服務(wù)
     */
    private void bindService() {
        Intent intent = new Intent();
        intent.setPackage(SERVICE_PACKAGE);
        intent.setAction(SERVICE_ACTION);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_book_list:
                requestBookList();
                break;
            case R.id.btn_book_add:
                showAddBookDialog();
                break;
            default:
                break;
        }
    }

    /**
     * show 添加圖書的Dialog
     */
    private void showAddBookDialog() {
        if (mDialog == null)
            mDialog = new AddBookDialog(this, new AddBookDialog.OnDialogDismissCallback() {
                @Override
                public void onOkButtonPressed(int bookId, String bookName) {
                    Log.d(TAG, "Ok button pressed!");
                    addBook(bookId, bookName);
                }

                @Override
                public void onCancelButtonPressed() {

                }
            });
        mDialog.show();
    }

    /**
     * 添加Book{@link Book}
     */
    private void addBook(int bookId, String bookName) {
        Log.i(TAG, "client add Book starting...id:" + bookId + " ,bookName:" + bookName);
        if (mBookManager == null) {
            Log.i(TAG, "mBookManager == null!!");
            return;
        }
        try {
            Book book = new Book(bookId, bookName);
            mBookManager.addBook(book);

            mBooks.add(book);
            mAdapter.notifyDataSetChanged();
            Toast.makeText(this, "Add succeed", Toast.LENGTH_SHORT).show();
        } catch (RemoteException e) {
            e.printStackTrace();
            Toast.makeText(this, "添加失敗", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 獲取Book列表
     */
    private void requestBookList() {
        bindService();
    }
}

2、實現(xiàn)向服務(wù)端添加圖書功能

功能很簡單盅蝗,點擊“添加圖書”按鈕后彈出dialog鳖链,輸入bookId和bookName,點擊“確定”

AddBookDialog.java

package com.glong.aidlclient;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.View;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class AddBookDialog extends Dialog implements View.OnClickListener {

    private OnDialogDismissCallback callback;
    private Button mBtnOk;
    private Button mBtnCancel;
    private EditText mBookIdTextView;
    private AutoCompleteTextView mBookNameTextView;

    public AddBookDialog(@NonNull Context context, OnDialogDismissCallback callback) {
        this(context, R.style.dialog, callback);
    }

    public AddBookDialog(@NonNull Context context, int themeResId, OnDialogDismissCallback callback) {
        super(context, themeResId);
        this.callback = callback;
        setOwnerActivity((Activity) context);
    }

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

        mBtnOk = findViewById(R.id.button_ok);
        mBtnCancel = findViewById(R.id.button_cancel);
        mBookIdTextView = findViewById(R.id.edit_book_id);
        mBookNameTextView = findViewById(R.id.edit_book_name);

        mBtnOk.setOnClickListener(this);
        mBtnCancel.setOnClickListener(this);
    }

    @Override
    public void show() {
        super.show();
        mBookIdTextView.setText("");
        mBookNameTextView.setText("");
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.button_ok:
                pressedOkBtn();
                break;
            case R.id.button_cancel:
                pressedCancelBtn();
                break;
            default:
                break;
        }
    }

    private void pressedCancelBtn() {
        dismiss();
    }

    private void pressedOkBtn() {
        String idString = mBookIdTextView.getText().toString();
        String name = mBookNameTextView.getText().toString();

        if (idString.isEmpty()) {
            Toast.makeText(getContext(), "ID不能為空", Toast.LENGTH_SHORT).show();
            return;
        }
        if (!idString.matches("\\d+")) {
            Toast.makeText(getContext(), "ID格式不正確", Toast.LENGTH_SHORT).show();
            return;
        }
        if (TextUtils.isEmpty(name)) {
            Toast.makeText(getContext(), "書名不能為空", Toast.LENGTH_SHORT).show();
            return;
        }
        dismiss();
        if (callback == null) return;
        int id = Integer.parseInt(idString);
        callback.onOkButtonPressed(id, name);
    }

    public interface OnDialogDismissCallback {
        /**
         * 通過點擊確定按鈕返回的
         *
         * @param bookId
         * @param bookName
         */
        void onOkButtonPressed(int bookId, String bookName);

        /**
         * 通過點擊取消按鈕返回的
         */
        void onCancelButtonPressed();
    }
}

Dialog的布局文件dialog_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFCC99"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:text="添加圖書"
        android:textColor="#60000000" />

    <LinearLayout
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:orientation="horizontal"
        android:paddingLeft="10dp"
        android:paddingRight="10dp">

        <TextView
            android:layout_width="80dp"
            android:layout_height="wrap_content"
            android:lines="1"
            android:text="Book ID:"
            android:textColor="#60000000" />

        <EditText
            android:id="@+id/edit_book_id"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="請輸入ID"
            android:inputType="number" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:orientation="horizontal"
        android:paddingLeft="10dp"
        android:paddingRight="10dp">

        <TextView
            android:layout_width="80dp"
            android:layout_height="wrap_content"
            android:lines="1"
            android:text="Book Name:"
            android:textColor="#60000000" />

        <AutoCompleteTextView
            android:id="@+id/edit_book_name"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="請輸入Name"
            android:inputType="text" />

    </LinearLayout>

    <FrameLayout
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="20dp"
        android:layout_marginTop="20dp">

        <Button
            android:id="@+id/button_ok"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left|center_vertical"
            android:layout_marginLeft="50dp"
            android:text="確定" />

        <Button
            android:id="@+id/button_cancel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right|center_vertical"
            android:layout_marginRight="50dp"
            android:text="取消" />
    </FrameLayout>
</LinearLayout>

現(xiàn)在重新run AIDLClient工程效果如下:


c8b51ab6-f17a-401a-8541-dff13d5c80fb.gif


源碼下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末墩莫,一起剝皮案震驚了整個濱河市芙委,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狂秦,老刑警劉巖灌侣,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異裂问,居然都是意外死亡侧啼,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門堪簿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慨菱,“玉大人,你說我怎么就攤上這事戴甩》龋” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵甜孤,是天一觀的道長协饲。 經(jīng)常有香客問我,道長缴川,這世上最難降的妖魔是什么茉稠? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮把夸,結(jié)果婚禮上而线,老公的妹妹穿的比我還像新娘。我一直安慰自己恋日,他們只是感情好膀篮,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著岂膳,像睡著了一般誓竿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谈截,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天筷屡,我揣著相機與錄音涧偷,去河邊找鬼。 笑死毙死,一個胖子當(dāng)著我的面吹牛燎潮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扼倘,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼跟啤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了唉锌?” 一聲冷哼從身側(cè)響起隅肥,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎袄简,沒想到半個月后腥放,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡绿语,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年秃症,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吕粹。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡种柑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出匹耕,到底是詐尸還是另有隱情聚请,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布稳其,位于F島的核電站驶赏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏既鞠。R本人自食惡果不足惜煤傍,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嘱蛋。 院中可真熱鬧蚯姆,春花似錦、人聲如沸洒敏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桐玻。三九已至篙挽,卻和暖如春荆萤,著一層夾襖步出監(jiān)牢的瞬間镊靴,已是汗流浹背铣卡。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留偏竟,地道東北人煮落。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像踊谋,于是被迫代替她去往敵國和親蝉仇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,164評論 25 707
  • Android跨進程通信IPC整體內(nèi)容如下 1殖蚕、Android跨進程通信IPC之1——Linux基礎(chǔ)2轿衔、Andro...
    隔壁老李頭閱讀 10,762評論 13 43
  • 本篇包含進程間通信——AIDL所涉及到的知識的自我總結(jié)(內(nèi)容詳細(xì)) 通過前段時間對AIDL的學(xué)習(xí)以及最近一些資料的...
    arvinljw閱讀 3,109評論 0 17
  • 慈母手中線,游子身上衣睦疫,臨行密密縫害驹,意恐遲遲歸。今天是母親節(jié),想講三件小事. 1.之所以把這篇小詩放到篇首,是因為...
    wenju_song閱讀 207評論 0 2
  • 交通 濟南出發(fā)機票來回往返大概900元左右底洗。700元左右的機票到重慶大概凌晨,時間不太好咕娄『ヒ荆看自行考慮吧。 重慶江北...
    _siximie閱讀 245評論 0 0