1) 應用某些模塊因爲特殊需求需要執行在單獨進程中。java
如消息推送,使消息推送進程與應用進程能單獨存活,消息推送進程不會因爲應用程序進程crash而受影響。
2) 爲加大一個應用可以使用的內存。需要多進程來獲取多分內存空間。android
給四大組件(Activity、Service、Receiver、ContentProvider)在AndroidMainfest中指定android:process屬性指定。數組
(1) 靜態成員和單例失效,數據同步失敗;
Android會爲每一個應用/每一個進程分配一個獨立的虛擬機。不一樣虛擬機在內存分配上有不一樣的地址空間,這就致使不一樣虛擬機中訪問同一個類對象會產生多個副本。緩存
(2) 線程同步機制失效。
因爲不一樣進程不是同一塊內存,不一樣進程鎖的不是同一對象。
(3) SharedPreferences可靠性降低;
SharedPreferences底層是經過讀/寫XML文件實現的。併發寫可能會出問題。因此它不支持多個進程同一時候去執行寫操做。不然會致使必定概率的數據丟失。
(4) Application會建立屢次。
當一個組件跑在一個新進程中,系統會給它又一次分配獨立虛擬機,這事實上就是啓動一個應用的過程,故執行在不一樣進程中的組件屬於不一樣的虛擬機和不一樣的Application。服務器
Intent和Binder數據傳輸時。或是對象持久化轉存/經過網絡傳輸給其它client時,需要使用Parcelable或Serializable將對象轉換成可以傳輸的形式。markdown
(1) Serializable接口
Serializable是Java提供的一個序列化接口,它是一個空接口。想要某個類實現序列化,僅僅需要對應類實現Serializable接口就能夠。Serializable是藉助ObjectOutputStream和ObjectInputStream實現對象的序列化和反序列化
通常在對應類實現Serializable類中還會定義一個final long型的serialVersionUID,不用它也能實現對象的序列化。它是用來輔助反序列化的。網絡
序列化時會把當前類的serialVersionUID寫入序列化文件裏。當反序列化系統會檢測文件裏的serialVersionUID,看它是否和當前類的serialVersionUID一致,假設一致說明序列化的類的版本號和當前類的版本號一樣,這時可反序化成功;不然說明當前類和序列化的類相比發生了變換,如添加或下降某個成員變量,這時沒法正常反序列化。
(2) Parcelable接口
在序列化過程當中需要實現的功能有:
1) 序列化:由writeToParcel方法完畢,終於經過Parcel中的一系列的write方法完畢。
2) 反序列化:由CREATOR完畢。內部標識了怎樣建立序列化對象和數組,終於經過Parcel的一系列read方法來完畢反序列化;
3) 內容描寫敘述符:由describeContents方法來完畢。差點兒所有狀況下該方法都返回0,僅噹噹前對象中存在文件描寫敘述符時返回1。
(3)二者差異
Serializable是Java中序列化接口,使用簡單但開銷大,序列和反序列化需要大量I/O操做。
Parcelable是Android中特有的序列化接口,使用複雜但開銷小,效率高,是Android推薦的序列化方法;
Parcelable主要用在內存序列化上,假設將對象序列化到存儲設備或將對象序列化後經過網絡傳輸。過程會比較複雜。建議使用Serializable。併發
Binder是什麼?socket
在一個進程中啓動還有一個進程的Activity, Service, Receiver組件時,可以使用Bundle附加上需要傳遞的消息給遠程進程。並經過Intent發送出去。ide
兩個進程經過讀/寫同一個文件來交換數據,還可以序列化一個對象到文件系統中。從還有一個進程中恢復這個對象。它的實現原理是經過ObjectOutputStream將文件寫入文件裏。再經過ObjectInputSteam將文件恢復。
經過文件共享的方式有必定侷限性。如併發讀/寫,讀出的內容可能不是最新的。併發寫就可能致使數據混亂。所以儘可能避免併發寫這樣的操做,或考慮用線程同步來限制多個線程的寫操做。文件共享適合在對數據要求不高的進程之間通訊。
SharedPreferences是Android提供的一種輕量級存儲方案,它經過鍵值對方式存儲數據,底層它是採用XML來存儲鍵值對。
因爲系統對SharedPreferences的讀寫有必定的緩存策略。即在內存中會有一份SharedPreferences文件的緩存,在多進程模式下,系統對它的讀/寫就變得不可靠,當面對高併發讀/寫訪問時。SharedPreferences會有很是大概率會丟失數據。所以不建議在進程間通訊中使用SharedPreferences。
Messenager可以在不一樣進程中傳遞Message對象。在Message中放入咱們要傳遞的數據。
它的底層實現是AIDL,如下是Messenger的兩個構造方法:
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
不管是IMessenger仍是Stub.asInterface,這樣的用法都代表它的底層是AIDL,它一次僅僅處理一個請求,不存在線程同步問題。
實現步驟:
(1) 服務端進程
1) 定義一個Service用於client的綁定,創建一個Handler,在handleMessage裏處理client發送過來的消息。
//用ServiceHandler接收並處理來自於client的消息
private class ServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if(msg.what == RECEIVE_MESSAGE_CODE){
Bundle data = msg.getData();
if(data != null){
String str = data.getString("msg");
}
//經過Message的replyTo獲取到client自身的Messenger,
//Service可以經過它向client發送消息
clientMessenger = msg.replyTo;
if(clientMessenger != null){
Message msgToClient = Message.obtain();
msgToClient.what = SEND_MESSAGE_CODE;
//可以經過Bundle發送跨進程的信息
Bundle bundle = new Bundle();
bundle.putString("msg", "你好,client,我是MyService");
msgToClient.setData(bundle);
try{
clientMessenger.send(msgToClient);
}catch (RemoteException e){
e.printStackTrace();
Log.e("DemoLog", "向client發送信息失敗: " + e.getMessage());
}
}
}
}
}
2) 經過Handler建立一個Messenger對象
//serviceMessenger是Service自身的Messenger,其內部指向了ServiceHandler的實例
//client可以經過IBinder構建Service端的Messenger,從而向Service發送消息,
//並由ServiceHandler接收並處理來自於client的消息
private Messenger serviceMessenger = new Messenger(new ServiceHandler());
3) 在Service的onBind中經過Messenger.getBinder()返回底層的Binder對象。
@Override
public IBinder onBind(Intent intent) {
Log.i("DemoLog", "MyServivce -> onBind");
//獲取Service自身Messenger所對應的IBinder。並將其發送共享給所有client
return serviceMessenger.getBinder();
}
4) 註冊服務,讓其執行在單獨進程中
<service
android:name=".MyService"
android:enabled="true"
android:process=":remote" >
</service>
(2) client進程
1) 綁定服務端的Service
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
//client與Service創建鏈接
Log.i("DemoLog", "client onServiceConnected");
//咱們可以經過從Service的onBind方法中返回的IBinder初始化一個指向Service端的Messenger
serviceMessenger = new Messenger(binder);
isBound = true;
Message msg = Message.obtain();
msg.what = SEND_MESSAGE_CODE;
//此處跨進程Message通訊不能將msg.obj設置爲non-Parcelable的對象,應該使用Bundle
//msg.obj = "你好。MyService,我是client";
Bundle data = new Bundle();
data.putString("msg", "你好,MyService,我是client");
msg.setData(data);
//需要將Message的replyTo設置爲client的clientMessenger,
//以便Service可以經過它向client發送消息
msg.replyTo = clientMessenger;
try {
Log.i("DemoLog", "client向service發送信息");
serviceMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
Log.i("DemoLog", "client向service發送消息失敗: " + e.getMessage());
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
//client與Service失去鏈接
serviceMessenger = null;
isBound = false;
Log.i("DemoLog", "client onServiceDisconnected");
}
};
Intent intent = new Intent();
ComponentName componentName = new ComponentName(packageName, serviceNmae);
intent.setComponent(componentName);
try{
Log.i("DemoLog", "client調用bindService方法");
bindService(intent, conn, BIND_AUTO_CREATE);
}catch(Exception e){
e.printStackTrace();
Log.e("DemoLog", e.getMessage());
}
}
綁定成功後用服務端返回的IBinder對象建立一個Messenger,經過它向服務端發送Message消息。
假設需要服務端給client發送消息。需要在Handler的handleMessage方法裏,依據client發送過來的Message.replyTo獲取到client的Messenger對象,就可以向client發送消息了。同一時候client需要在服務鏈接的onServiceConnected方法中,將client的Messenger對象經過Message.replyTo給收到服務端發送過來的Message對象。這樣就實現兩方通訊。
(1)先新建一個Book.java實現Parcelable接口,表示一個圖書的信息類;
import android.os.Parcel;
import android.os.Parcelable;
/** * Book.java */
public class Book implements Parcelable{
private String name;
private int price;
public Book(){}
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;
}
public 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];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(price);
}
/** * 參數是一個Parcel,用它來存儲與數據傳輸 * @param dest */
public void readFromParcel(Parcel dest) {
//注意。此處的讀值順序應當是和writeToParcel()方法中一致的
name = dest.readString();
price = dest.readInt();
}
//方便打印數據
@Override
public String toString() {
return "name : " + name + " , price : " + price;
}
}
(2) 新建一個Book.aidl。表示Book類在AIDL中的聲明;
// Book.aidl
//這個文件的做用是引入了一個序列化對象 Book 供其它的AIDL文件使用
//注意:Book.aidl與Book.java的包名應當是同樣的
//注意parcelable是小寫
parcelable Book;
(3) 新建一個IBookManger.aidl接口,裏面包括對應的方法;
// IBookManger.aidl
//導入所需要使用的非默認支持數據類型的包
import com.lypeer.ipcclient.Book;
interface IBookManger {
//所有的返回值前都不需要加不論什麼東西。不管是什麼數據類型
List<Book> getBooks();
Book getBook();
}
該方法保存後,會在gen文件夾下生成一個IBookManger.java類,它是系統爲BookManger.aidl生成的Binder類,繼承android.os.IInterface。它本身也仍是個接口。
它包括如下內容:
1) 定義了一個String型的DESCRIPTOR。Binder的惟一標識;
2) asInterface(android.os.IBinder obj)方法,將服務端的Binder對象轉換成client所需的AIDL接口類型對象,當client和服務端處於同一進程,直接返回服務端的Stub對象自己。不然返回系統封裝後的Stub.proxy對象。
3) asBinder方法:返回當前Binder對象。
4) onTransact方法:執行在服務端的Binder線程池中,當client發起遠程請求,遠程請求會經過系統封裝後交由此方法處理,該方法原型爲public Boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),服務端經過code可以肯定client請求的是哪個方法,從data中取出目標方法所需的參數,執行對應方法後,將返回值寫入reply中。假設此方法返回false,client會請求失敗。
5) IBookManger.aidl接口中聲明的方法的代理實現,此方法執行在client,當client調用此方法時,首先需要建立此方法需要的輸入類型Parcel對象_data, 輸出類型Parcel對象_reply和返回值對象。接着將參數信息寫入_data中(如有參數的話),再調用transact方法發起RPC(遠程過程調用)。當前線程掛起,服務端的onTransact方法會被調用,直到RPC過程返回後,當前線程繼續執行。並從_reply中取出RPC過程的返回結果;最後返回_reply中的數據。
6) 聲明瞭IBookManger.aidl中聲明的方法。
7) 聲明瞭對應的整型id分別用於標識聲明的方法。該id用於標識在transact過程當中推斷client請求的到底是哪一個方法。
8) 聲明瞭一個類部類Stub,繼承android.os.Binder實現IBookManager.aidl中類接口。當client和服務端處於同一進程,方法調用不會走跨進程的transact過程。若位於不一樣進程。則由Stub的內部代理Proxy,調用跨進程transact過程。
需要注意的是:client發起請求時。當前線程會掛起直至服務端返回數據,若方法是一個耗時操做,不能在UI線程中發起此遠程請求。服務端的Binder方法執行在Binder線程池中。因此不管操做是否耗時,都應採用同步的方法去實現。
(4)linkToDeath unlinkToDeath
因爲Binder執行在服務端進程中。假設服務端進程因爲某種緣由終止。這時client服務端的Binder鏈接斷裂,會致使遠程調用失敗。但client很是可能不知道,解決此問題的辦法,利用Binder提供的兩個配對方法linkToDeath和unlinkToDeath,經過linkToDeath可以給Binder設置一個死亡代理,當Binder被終止時。咱們會接收到通知。這時可經過又一次發起請求恢復鏈接。
實現方法:
1) 先聲明一個DeathRecipient對象,它是一個接口。內部僅僅有一個方法binderDied,咱們需要實現該方法,當Binder死亡時,系統回調binderDied方法,咱們可以移出以前binder代理並又一次創建遠程服務。
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mBookManager == null) {
return;
}
mBookManger.asBinder().unlinkToDeath(mDeathRecipient, 0);
mBookManger = null;
//又一次綁定遠程服務
}
};
2) 在client綁定遠程服務成功後,給binder設置死亡代理;
mService = IBookManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient, 0);
(1)Serivce創建
咱們需要新建一個Service,稱爲AIDLService,代碼例如如下:
/** 1. 服務端的AIDLService.java */
public class AIDLService extends Service {
public final String TAG = this.getClass().getSimpleName();
//包括Book對象的list
private List<Book> mBooks = new ArrayList<>();
//由AIDL文件生成的IBookManager
private final IBookManager.Stub mBookManager = new IBookManager.Stub() {
@Override
public List<Book> getBooks() throws RemoteException {
synchronized (this) {
Log.e(TAG, "invoking getBooks() method , now the list is : " + mBooks.toString());
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) {
Log.e(TAG, "Book is null in In");
book = new Book();
}
//嘗試改動book的參數,主要是爲了觀察其到client的反饋
book.setPrice(2333);
if (!mBooks.contains(book)) {
mBooks.add(book);
}
//打印mBooks列表,觀察client傳過來的值
Log.e(TAG, "invoking addBooks() method , now the list is : " + mBooks.toString());
}
}
};
@Override
public void onCreate() {
super.onCreate();
Book book = new Book();
book.setName("Android開發藝術探索");
book.setPrice(28);
mBooks.add(book);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(getClass().getSimpleName(), String.format("on bind,intent = %s", intent.toString()));
return mBookManager;//在onBinder中返回服務端的Binder對象
}
}
(2)註冊服務
<service android:name="aidl.AIDLService"
android:process=":remote">
</service>
/** 2. client的AIDLActivity.java */
public class AIDLActivity extends AppCompatActivity {
//由AIDL文件生成的Java類
private IBookManager mBookManager = null;
//標誌當前與服務端鏈接情況的布爾值,false爲未鏈接。true爲鏈接中
private boolean mBound = false;
//包括Book對象的list
private List<Book> mBooks;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
}
/** * button的點擊事件。點擊以後調用服務端的addBookIn方法 * * @param view */
public void addBook(View view) {
//假設與服務端的鏈接處於未鏈接狀態,則嘗試鏈接
if (!mBound) {
attemptToBindService();
Toast.makeText(this, "當前與服務端處於未鏈接狀態,正在嘗試重連,請稍後再試", Toast.LENGTH_SHORT).show();
return;
}
if (mBookManager == null) return;
Book book = new Book();
book.setName("APP研發錄In");
book.setPrice(30);
try {
mBookManager.addBook(book);//經過服務端的Binder對象調服務端方法
Log.e(getLocalClassName(), book.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
/** * 嘗試與服務端創建鏈接 */
private void attemptToBindService() {
Intent intent = new Intent();
intent.setAction("aidl.AIDLService");
intent.setPackage("com.xx.packagename");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);//綁定服務
}
@Override
protected void onStart() {
super.onStart();
if (!mBound) {
attemptToBindService();
}
}
@Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mServiceConnection);
mBound = false;
}
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(getLocalClassName(), "service connected");
mBookManager = IBookManager.Stub.asInterface(service);
mBound = true;
if (mBookManager != null) {
try {
mBooks = mBookManager.getBooks();//調用服務端的getBooks方法
Log.e(getLocalClassName(), mBooks.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(getLocalClassName(), "service disconnected");
mBound = false;
}
};
}
經過獲取服務端的Binder對象,就可以和服務端進行通訊了。
(1)跨進程listener接口
假設在服務端定聲明瞭接口,client進行註冊和反註冊。會拋出異常,沒法反註冊。因爲在多進程中,Binder會把client傳過來的對象又一次轉化成一個新對象,這樣儘管client註冊和反註冊使用的是同一個對象,但是經過Binder傳遞到服務端卻變成兩個對象。對象的跨進程傳輸本質都是反序列化過程。
可以使用RemoteCallbackList,它是系統專門用來刪除跨進程listener接口。它實現原理利用底層Binder對象是同一個,僅僅要遍歷服務端所有listener,找出和解註冊listener具備一樣Binder對象的服務端listener,並把它刪除掉。
同一時候RemoteCallbackList還有一個做用,當client進程終止。它會移除client所註冊的所有listener.
(2)權限驗證
在註冊服務時加入permission權限驗證,或是在onTransact方法中進行權限驗證;
(3) AIDL訪問流程總結:
先建立一個Service和一個AIDL接口。再建立一個類繼承自AIDL接口中的Stub類並實現Stub中的抽象方法;在Service的onBind方法中返回這個對象,再在client就可以綁定服務端service。創建鏈接後就可以訪問遠程服務端的方法了。
和Messenger同樣,ContentProvider底層一樣是Binder.系統預置了很是多ContentProvider,如通信錄、日程表等。僅僅需經過ContentResolver的query/update/insert/delete就可以跨進程訪問這些信息。詳細實現過程例如如下:
1) 新建一個繼承自系統ContentProvider的Provider,並重寫onCreate, query, getType, insert, delete, update六個方法。
這六個進程執行在ContentProvider的進程中,除了onCreate由系統回調執行在主線程中,其它五個方法執行在Binder線程池中。
2) 註冊Provider
<provider android:name=".provider.XXProvider"
android:authorities="com.xx.xx.provider"
android:permission="com.xx.PROVIDER"
android:process=":provider">
</provider>
3) 在還有一個進程中訪問咱們定義的ContentProvider
Uri uri = Uri.parse("content://com.xx.xx.provider");
getContentResolver().query(uri, null, null, null, null);
getContentResolver().query(uri,)
4) ContentProvider數據源發生變化時。可經過ContentResolver的notifyChange方法來通知外界數據發生改變。外界可經過ContentResolver的registerContentObserver方法來註冊觀察者。經過unregisterContentObserver方法來解除觀察者。
Socket分爲流式套接字和用戶數據報套接字。各自是對應於網絡的傳輸控制層中的TCP/UDP
TCP:面向鏈接的協議,穩定的雙向通訊功能,鏈接需要「三次握手」。提供了超時重傳機制,具備很是高的穩定性;
UDP:面向無鏈接協議。不穩定的單向通訊功能,也可提供雙向通訊功能。效率高,不能保證數據必定能正確傳輸。
實現步驟:
1)服務端在新建一個Service,並創建TCP服務
@Override
public void onCreate() {
new Thread(new TcpServer()).start();//堵塞監聽client鏈接
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
throw null;
}
private class TcpServer implements Runnable {
@Override
public void run() {
ServerSocket serverSocket;
try {
//監聽8688端口
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
return;
}
while (!isServiceDestroyed) {
try {
// 接受client請求。並且堵塞直到接收到消息
final Socket client = serverSocket.accept();
new Thread() {
@Override
public void run() {
try {
//有消息接收到。新開一個線程進行處理消息
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void responseClient(Socket client) throws IOException {
// 用於接收client消息,將client的二進制數據流格式轉成文本格式
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
// 用於向client發送消息
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
out.println("您好。我是服務端");
while (!isServiceDestroyed) {
String str = in.readLine();//經過數據流的readLine。可直接變成文本格式
Log.i("moon", "收到client發來的信息" + str);
if (TextUtils.isEmpty(str)) {
//client斷開了鏈接
Log.i("moon", "client斷開鏈接");
break;
}
String message = "收到了client的信息爲:" + str;
// 從client收到的消息加工再發送給client
out.println(message);
}
out.close();//關閉流
in.close();
client.close();
}
2)註冊服務
<service
android:name=".SocketServerService"
android:process=":remote" />
3) client先創建鏈接服務端socket
Socket socket = null;
while (socket == null) {
try {
//選擇和服務器一樣的端口8688
socket = new Socket("localhost", 8688);
mClientSocket = socket;
mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
} catch (IOException e) {
SystemClock.sleep(1000);
}
}
4) client和服務端通訊。經過while循環不斷去讀取服務器發送過來的消息
// 接收服務器端的消息
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!isFinishing()) {
final String msg = br.readLine();
if (msg != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_message.setText(tv_message.getText() + "\n" + "服務端:" + msg);
}
}
);
}
}
5) client在onCreate中單開線程啓動鏈接服務
Intent service = new Intent(this, SocketServerService.class);
startService(service);
new Thread() {
@Override
public void run() {
connectSocketServer();
}
}.start();