從事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.aidl
在com.glong.aidlclient
包下
5孩灯、在aidl目錄下新建com.glong.aidlclient包,并在該包下新建Book.aidl
先新建 Package 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工程效果如下: