IPC也看過很多次,每次看完后記會忘記屈糊,這次自己給它寫下來加深印象
IPC
進(jìn)程間通信(Inter-Process Communication),簡稱IPC,就是指進(jìn)程與進(jìn)程之間進(jìn)行通信
基于Binder的AIDL和Messager的同一個應(yīng)用的通信
步驟一
首先寫個最基本的就是重寫IBinder,增強(qiáng)服務(wù)端的的功能 看代碼:
//IpcService.java文件
public class IpcService extends Service {
private IBinder mIBinder;
@Override
public void onCreate() {
super.onCreate();
mIBinder = new MyBinder();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mIBinder;
}
public class MyBinder extends Binder {
public void print() {
Log.d("PHN", "服務(wù)Service,執(zhí)行任務(wù)");
}
}
}
注意要在清單文件中注冊服務(wù)
public class MainActivity extends AppCompatActivity {
private IpcService.MyBinder myBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//自動綁定服務(wù)
Intent intent = new Intent(this, IpcService.class);
MyServiceConnection serviceConnection = new MyServiceConnection();
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != myBinder) {
myBinder.print();
}
}
});
}
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (IpcService.MyBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
執(zhí)行點(diǎn)擊服務(wù)端會打印信息,說明這個活動和服務(wù)進(jìn)行通信是成功的昧诱!
步驟二
我們用Messager來做剛才的功能
1.首先在Service里面創(chuàng)建一個Hander用來接受消息:
private final static Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d("PHN", "服務(wù)Service,執(zhí)行任務(wù)");
}
};
2.在Service里面創(chuàng)建一個Messager,并把Handler放入其中
private final static Messenger mMessenger = new Messenger(mHandler);
3.重寫onbind方法,返回Messager里面的Binder
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
貼出完整的服務(wù)代碼
public class IpcService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
private final static Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d("PHN", "服務(wù)Service凶掰,執(zhí)行任務(wù)");
}
};
private final static Messenger mMessenger = new Messenger(mHandler);
}
再貼出活動里的代碼
public class MainActivity extends AppCompatActivity {
private Messenger messenger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//自動綁定服務(wù)
Intent intent = new Intent(this, IpcService.class);
MyServiceConnection serviceConnection = new MyServiceConnection();
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != messenger) {
Message message = Message.obtain();
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
messenger = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
執(zhí)行看看結(jié)果
果然可以得到同樣的效果懦窘!
步驟三
上面我們可能看到只能是活動讓服務(wù)執(zhí)行任務(wù)稚配,那么怎樣才能讓服務(wù)端讓活動也執(zhí)行任務(wù)呢,我們先對服務(wù)器端的代碼進(jìn)行修改,首先修改Service的Handler
看下面服務(wù)端代碼處理:
public class IpcService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
private final static Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d("PHN", "服務(wù)Service道川,執(zhí)行任務(wù)");
//獲取Messager
Messenger messenger = msg.replyTo;
//創(chuàng)建消息
Message msg_reply = Message.obtain();
try {
//發(fā)送
messenger.send(msg_reply);
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
private final static Messenger mMessenger = new Messenger(mHandler);
}
接著我們在活動端也增加一個Handler和Messager來處理消息,貼出代碼
public class MainActivity extends AppCompatActivity {
private Messenger messenger;
private final static Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d("PHN", "活動Acitivity冒萄,執(zhí)行任務(wù)");
}
};
private final static Messenger mReplyMessager = new Messenger(mHandler);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//自動綁定服務(wù)
Intent intent = new Intent(this, IpcService.class);
MyServiceConnection serviceConnection = new MyServiceConnection();
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != messenger) {
Message message = Message.obtain();
//通過msg把客戶端的Messager傳送到服務(wù)器端(關(guān)鍵代碼)
message.replyTo =mReplyMessager;
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
messenger = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
還有一個比較關(guān)鍵的地方,就是要在客戶端發(fā)送消息的時候把客戶端的Messager通過消息傳送到服務(wù)器端
(msg.replyTo =mReplyMessager)
來執(zhí)行看看結(jié)果:
果然可以!扇单!
步驟四
上面的通信是基于本應(yīng)用里的奠旺,下面我們來看看不同的兩個應(yīng)用之間的通信AIDL施流,我是參照任玉剛的開發(fā)藝術(shù)里寫例子鄙信,
1.首先那就是新建AIDL文件瞪醋,很簡單:
那我就直接創(chuàng)建兩個aidl文件Book.aidl,BookManager.aidl
下面直接看文件的具體內(nèi)容银受,我貼上來:
//Book.java
public class Book implements Parcelable{
protected Book(Parcel in) {
name = in.readString();
price = in.readInt();
}
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 String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
private String name;
private int price;
public Book() {}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(price);
}
/**
* 參數(shù)是一個Parcel,用它來存儲與傳輸數(shù)據(jù)
* @param dest
*/
public void readFromParcel(Parcel dest) {
//注意鸦采,此處的讀值順序應(yīng)當(dāng)是和writeToParcel()方法中一致的
name = dest.readString();
price = dest.readInt();
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
// Booka.aidl
package com.hanny.aidldemo;
// Declare any non-default types here with import statements
parcelable Book;
// BookManager.aidl
package com.hanny.aidldemo;
import com.hanny.aidldemo.Book;
// Declare any non-default types here with import statements
interface BookManager {
//所有的返回值前都不需要加任何東西,不管是什么數(shù)據(jù)類型
List getBooks();
//傳參時除了Java基本類型以及String顶霞,CharSequence之外的類型
//都需要在前面加上定向tag锣吼,具體加什么量需而定
void addBook(in Book book);
}
文件搞好了,先編譯下玄叠,看看會不會錯,應(yīng)該會錯。
這是因?yàn)榘袯ook.java文件放到了aidl下隧膘,所以要在build.gradle 的android{ }的閉包下加入
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
不會錯了寺惫,那就來寫AIDL的service文件:
public class AIDLService extends Service {
//包含Book對象的list
private List mBooks = new ArrayList<>();
private BookManager.Stub bookManager = new BookManager.Stub() {
@Override
public List getBooks() throws RemoteException {
if (mBooks != null) {
return mBooks;
}
return new ArrayList();
}
@Override
public void addBook(Book book) throws RemoteException {
synchronized (this) {
if (mBooks == null) {
mBooks = new ArrayList();
}
if (book == null) {
book = new Book();
}
book.setPrice(23);
if (!mBooks.contains(book)) {
mBooks.add(book);
}
//打印mBooks列表,觀察客戶端傳過來的值
Log.e("PHN", "增加后的圖書 : " + mBooks.toString());
}
}
};
@Override
public void onCreate() {
super.onCreate();
Book book = new Book();
book.setName("Android開發(fā)藝術(shù)探索");
book.setPrice(28);
mBooks.add(book);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return bookManager;
}
}
服務(wù)端寫好了互墓,那就新建一個工程蒋搜,然后把服務(wù)端的aidl下的文件全部拷貝過來~放在main下級目錄篡撵!
接著在活動里用隱式意圖開啟服務(wù)育谬!這樣的話就要在服務(wù)端的清單文件里給服務(wù)配上action
<service android:name=".AIDLService">
<intent-filter>
<action android:name="action.ipc"/>
</intent-filter>
</service>
看看客戶端活動里的代碼:
public class MainActivity extends AppCompatActivity {
//標(biāo)志當(dāng)前與服務(wù)端連接狀況的布爾值帮哈,false為未連接膛檀,true為連接中
private boolean mBound = false;
private BookManager bookManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.bt_get).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addBook();
}
});
}
private void addBook() {
if (!mBound) {
attemptToBindService();
Toast.makeText(this, "當(dāng)前與服務(wù)端處于未連接狀態(tài)咖刃,正在嘗試重連泳炉,請稍后再試", Toast.LENGTH_SHORT).show();
return;
}
if (bookManager == null) return;
Book book = new Book();
book.setName("APP研發(fā)錄In");
book.setPrice(30);
try {
bookManager.addBook(book);
Log.e(getLocalClassName(), book.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
//開啟服務(wù)
private void attemptToBindService() {
Intent intent = new Intent();
intent.setAction("action.ipc");
intent.setPackage("com.hanny.aidldemo");
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookManager = BookManager.Stub.asInterface(service);
mBound = true;
if (bookManager != null) {
try {
List books = bookManager.getBooks();
Log.d("PHN", "初始化得到的書: "+books.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
@Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mServiceConnection);
mBound = false;
}
}
}
下面先運(yùn)行服務(wù)端apk 花鹅,然后運(yùn)行客服務(wù)apk然后點(diǎn)擊增加圖書枫浙,看看客戶端和服務(wù)端的打印結(jié)果:
反正就是這么簡單,動手實(shí)踐加深印象箩帚,并沒有什么難的地方,
注意地方
AIDL 發(fā)生異常的原因 Android java.lang.SecurityException: Binder invocation to an incorrect interface
解決方法如下:
在使用上請注意盔然,服務(wù)端與客戶端都要有相同的接口(使用到的)焕参,這里的“相同”是指完全相同,包括包名叠纷,也就是說要在不同工程下建立相同的包名潦嘶,這樣一來,問題應(yīng)該迎刃而解了掂僵!