本篇博客的框架
IPC(Inter-Process Communication) 進(jìn)程間通訊,是指兩個不同進(jìn)程之間數(shù)據(jù)交換的進(jìn)程。
在明確其之前,需要先弄懂幾個概念:
在Android程序中,1般情況下1個程序就是1個進(jìn)程(在無特別的代碼實現(xiàn)下),UI線程即主線程。如果有耗時操作,則會致使主線程堵死。而在Android中主線程負(fù)責(zé)UI,和用戶交互,如果堵死肯定影響用戶的使用度。所以Android要求不能再主線程中有耗時操作,這時候就要將耗時操作放在子線程中。
在Android 中,每個利用可以使用的內(nèi)存大小有限制,早起的1些版本在16M左右,不同的裝備有不同的大小。可以通過量進(jìn)程獲得多分內(nèi)存空間。
Android中開啟多進(jìn)程只有1種方法,便是給4大組件(Activity
,Receiver
,ContentProvider
,Service
)指定android
屬性,初次以外沒有其他方法。請注意,不能指定某1個線程或?qū)嶓w類指定其所運行的進(jìn)程
:process
通過jni調(diào)用底層去開啟多進(jìn)程也是1種方法,但屬于特殊情況,不進(jìn)行斟酌;
首先編寫3個Activity
,并在清單文件中做以下注冊:
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.ipc.SecondActivity"
android:process=":remote" />
<activity
android:name="com.example.ipc.ThirdActivity"
android:process=".remote" />
對MainActivity
不進(jìn)行指定,則默許為當(dāng)前進(jìn)程。
對SecondActivity
指定屬性android:process=":remote"
。
對ThirdActivity
指定屬性android:process=".remote"
。
注意SencodActivity
和ThirdActivity
的進(jìn)程參數(shù)不同。
把3個頁面都打開,通過DDMS可以看到3個進(jìn)程的開啟
啟動了3個進(jìn)程:分別是
com.example.ipc
:默許的程序進(jìn)程。和包名相同。com.example.ipc:remote
:SecondActivity
所在的進(jìn)程。.remote
:ThirdActivity
所在的進(jìn)程。那末2和3 ,進(jìn)程名不同有甚么區(qū)分嗎;
ShareUID
和其跑在同1個進(jìn)程中。通過如上方式,很簡單的變開啟了多進(jìn)程,但是,如果僅僅這樣的話,會有大問題。
看下面1個例子:
添加1個公有的類,添加靜態(tài)字段:
/**
* 類似平常開發(fā)中的工具類等
* @author MH
*
*/
public class PublicContant {
public static int m = 1;
}
在MainActivity
中Log1下并修改字段
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("info", PublicContant.m+"");
PublicContant.m++;
}
在SecondActivity
中Log1下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Log.i("info", PublicContant.m+"");
}
根據(jù)上面的邏輯,Log信息應(yīng)當(dāng)是1,和2 。但是呢,不是這樣的。
兩個都是1,我靠。。先不問緣由,看結(jié)果就知道是錯的。多進(jìn)程這么不靠譜,腫么回事??
Android 為每個進(jìn)程都分配1個獨立的虛擬機(jī),不同的虛擬機(jī)在內(nèi)存分配上有不同的地址空間,這就致使在不同虛擬機(jī)中訪問同1個類對象會產(chǎn)生多個副本。
對當(dāng)前來講,進(jìn)程com.example.ipc
和com.example.ipc:remote
都存在1個PublicContant
類,并且這兩個類是相互不干擾的,1個進(jìn)程中修改了該值的對象,對其他進(jìn)程中的該值不會造成任何影響。
運行在同1個進(jìn)程中的組件是屬于同1個虛擬機(jī)和同1個
Application
的。同理,運行在不同進(jìn)程中的組件是屬于兩個不同的虛擬機(jī)和Application
的。
根據(jù)如上所述,多進(jìn)程所釀成的問題分為以下幾個方面:
SharedPreference
的可靠性降落 sharedPreference
的底層實現(xiàn)是通過讀寫XML文件,兩個進(jìn)程去讀寫,并發(fā)明顯是可能出現(xiàn)問題的。Application
會屢次創(chuàng)建在了解多進(jìn)程通訊之前,我們需要了解兩個基礎(chǔ)的概念,序列化和反序列化。
序列化和反序列的用處:
在Android
中實現(xiàn)序列化的方式有兩種,Serializable
和Parcelable
。
Serializable
是Java提供的1個序列化接口,他是1個空接口,是類實現(xiàn)該接口便可實現(xiàn)序列化。
/**
* Serializable 序列化對象
* @author MH
*
*/
public class Book implements Serializable {
/**
* 序列化和反序列的關(guān)鍵
*/
private static final long serialVersionUID = 1L;
public int bookId;
public String bookName;
}
在實現(xiàn)Serializable
時候,編譯器會提示,讓我們添加serialVersionUID
字段,該字段是1個關(guān)鍵的字段,后面會說。
相應(yīng)的實現(xiàn)好了,那末如何寫入和讀取呢?
public void writeSerializable() {
try {
// 構(gòu)造對象
Book book = new Book();
// 構(gòu)造序列化輸出字節(jié)流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("xxx.txt"));
// 序列化對象
oos.writeObject(book);
// 關(guān)閉流
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void readSerializable() {
try {
// 創(chuàng)建序列化讀取字節(jié)流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
"xxx.txt"));
// 反序列化(讀取)對象
Book book = (Book) ois.readObject();
// 關(guān)閉流
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
}
在序列化時,如果我們序列化對象以后,改變了我們的類結(jié)構(gòu)(添加或改變字段),乃至是修改了字段的類型,修改了類名,那末我們能反序列化成功嗎。
那末關(guān)鍵就在于serialVersionUID
字段。
如果我們不指定的話。在序列化時,會計算當(dāng)前類結(jié)構(gòu)的hash值并將該值賦給serialVersionUID
,當(dāng)反序列時,會比對該值是不是相同,如果不相同,則沒法序列化成功。
我們也能夠手動指定,手動指定的好處是在類結(jié)構(gòu)產(chǎn)生變化時,能夠最大程度的反序列,固然條件是只是刪除或添加了字段,如果是變量類型產(chǎn)生了變化,則仍然沒法反序列成功。
serialVersionUID 的工作機(jī)制:序列化時系統(tǒng)會把當(dāng)前類的
serialVersionUID
寫入序列化文件中,當(dāng)反序列化時候系統(tǒng)會去檢測文件中的serialVersionUID
,看它是不是和當(dāng)前類的serialVersionUID
1致,如果1致說明序列化類的版本和當(dāng)前類的版本是相同的,這個時候可以成功反序列化,否則就說明當(dāng)前類和序列化的類相比產(chǎn)生了某些變化。所以,我們最好指定serialVersionUID
,避免他自定生成。
Parcelable
是Android中獨有的1種序列化方式,在intent
傳值時,通常使用該方式。
該方式實現(xiàn)序列化,仍然實現(xiàn)Parcelable
,然后實現(xiàn)1些該接口的方法。
/**
* Parcelable 對象的使用方式
* @author MH
*
*/
public class Book implements Parcelable {
public int bookId;
public String bookName;
@Override
public int describeContents() {
// 返回當(dāng)前對象的內(nèi)容描寫。幾近所有情況下都是返回0
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// 將當(dāng)前對象寫入到序列化結(jié)構(gòu)中
dest.writeInt(bookId);
dest.writeString(bookName);
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
@Override
public Book createFromParcel(Parcel source) {
//從序列化后的對象中創(chuàng)建原始的值
Book book = new Book();
book.bookId = source.readInt();
book.bookName = source.readString();
return book;
}
@Override
public Book[] newArray(int size) {
//創(chuàng)建指定長度的原始對象數(shù)組
return new Book[size];
}
};
}
Parcelable
實現(xiàn)兩個方法,創(chuàng)建1個字段:
describeContents()
:返回當(dāng)前對象的內(nèi)容描寫。幾近所有情況下都是返回0。public void writeToParcel(Parcel dest, int flags)
:// 將當(dāng)前對象寫入到序列化結(jié)構(gòu)中Parcelable.Creator
字段,該對象需要實現(xiàn)兩個方法: public Book createFromParcel(Parcel source)
:從序列化后的對象中創(chuàng)建原始的值。public Book[] newArray(int size)
:創(chuàng)建指定長度的原始對象數(shù)組Serializable
是Java中的序列化接口,其使用起來簡單但是開消較大,序列化和反序列化需要大量的I/O操作。Parcelable
是Android中的序列化方式,更適用于Android的平臺上,他的缺點是使用起來略微麻煩,但是效力很高。Parcelable
合適進(jìn)程間的通訊,運行期。Serializable
合適文件存儲即網(wǎng)絡(luò)傳輸。Android中的4大組件中,其中有3大組件(Activity
,Service
,Receiver
)都支持Intent
中傳遞Bundle
數(shù)據(jù),如果看其源碼,會發(fā)現(xiàn)其也是實現(xiàn)了Parcelable
接口,所以其能夠在不同進(jìn)程中傳輸。
固然在傳輸?shù)倪M(jìn)程中,其所傳輸?shù)臄?shù)據(jù)必須支持序列化。比如基本數(shù)據(jù)類型,字符串,Parcelable
的實現(xiàn)類,Serializable
的實現(xiàn)類。由于該方法非常經(jīng)常使用,不在多說。
文件同享: 將對象序列化以后保存到文件中,在通過反序列,將對象從文件中讀取。
在MainActvity
中寫寫入對象
/**
* 寫入序列化對象
*/
public void wirte() {
Book book = new Book();
book.bookId = 1;
book.bookName = "si";
try {
// 構(gòu)造序列化輸出字節(jié)流
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(PATH));
// 序列化對象
oos.writeObject(book);
// 關(guān)閉流
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(book);
}
在SecondActivity
中,讀取文件(反序列化)
public void read() {
Book book = null;
try {
// 創(chuàng)建序列化讀取字節(jié)流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
MainActivity.PATH));
// 反序列化(讀取)對象
book = (Book) ois.readObject();
// 關(guān)閉流
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(book);
}
LOG 結(jié)果
06-28 09:20:47.916: com.example.ipc(進(jìn)程名) I/System.out(12399): Book [bookId=1, bookName=si]
06-28 09:20:53.376: com.example.ipc:remote(進(jìn)程名) I/System.out(12866): Book [bookId=1, bookName=si]
分屬不同的進(jìn)程成功的獲得到了同享的數(shù)據(jù)。
通過同享文件這類方式來同享數(shù)據(jù)對文件的格式是沒有具體的要求的。比如可以是文件,也能夠是Xml,JSON 等。只要讀寫雙方約定1定的格式便可。
同文件同享方式也存在著很大的局限性。即并發(fā)讀/ 寫的問題。讀/寫會造成數(shù)據(jù)不是最新。同寫很明顯會出現(xiàn)毛病。
文件同享合適在對數(shù)據(jù)同步要求不高的進(jìn)程之間進(jìn)行通訊。并且要妥善處理并發(fā)讀寫的問題。
SharedPreference 底層文件的方式。不合適在多進(jìn)程中同享數(shù)據(jù)。
Messenger
可以翻譯為信使,通過該對象,可以在不同的進(jìn)程中傳遞Message
對象。注意,兩個單詞不同。
下面就通過服務(wù)端(Service)和客戶端(Activity)的方式進(jìn)行演示。
客戶端向服務(wù)端發(fā)送消息,可分為以下幾步。
服務(wù)端
Service
Handler
對象,實現(xiàn)handlerMessage
方法。Handler
對象構(gòu)造Messenger
信使對象。Service
的onBind()
返回信使中的Binder
對象。客戶端
Actvity
ServiceConnection
,監(jiān)聽綁定服務(wù)的回調(diào)。onServiceConnected()
方法的參數(shù),構(gòu)造客戶端Messenger
對象Messenger
向服務(wù)端發(fā)送消息。實現(xiàn)服務(wù)端
/**
* Messenger 的使用 服務(wù)端
* @author MH
*
*/
public class MessengerService extends Service {
/**
* 構(gòu)建handler 對象
*/
public static Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
// 接受客戶端發(fā)送的消息
String msgClient = msg.getData().getString("msg");
Log.i("messenger","接收到客戶真?zhèn)€消息--"+msgClient);
};
};
// 通過handler 構(gòu)建Mesenger 對象
private final Messenger messenger = new Messenger(handler);
@Override
public IBinder onBind(Intent intent) {
// 返回binder 對象
return messenger.getBinder();
}
}
注冊服務(wù)別忘了 ,同時對服務(wù)修改其進(jìn)程。
實現(xiàn)客戶端
/**
* Messenger 的使用 客戶端
*
* @author MH
*
*/
public class MessengerActivity extends AppCompatActivity {
/**
* Messenger 對象
*/
private Messenger mService;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// IBinder 對象
// 通過服務(wù)端返回的Binder 對象 構(gòu)造Messenger
mService = new Messenger(service);
Log.i("messenger", "客戶端以獲得服務(wù)端Messenger對象");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
// 啟動服務(wù)
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
}
/**
* 布局文件中添加了1個按鈕,點擊該按鈕的處理方法
* @param view
*/
public void send(View view) {
try {
// 向服務(wù)端發(fā)送消息
Message message = Message.obtain();
Bundle data = new Bundle();
data.putString("msg", "lalala");
message.setData(data);
// 發(fā)送消息
mService.send(message);
Log.i("messenger","向服務(wù)端發(fā)送了消息");
} catch (Exception e) {
e.printStackTrace();
}
}
}
看1下結(jié)果:
注釋很清楚,不在多說,依照流程實現(xiàn)便可。 其中有1點需要注意:
我們是通過Message
作為媒介去攜帶數(shù)據(jù)的。但是,Message
的obj 并沒有實現(xiàn)序列化(實現(xiàn)Serializable
或Parcelable
),也就是其不能保存數(shù)據(jù)。必須使用message.setData()
方法去傳入1個Bundle
對象,Bundle
中保存需要傳入的數(shù)據(jù)。
傳遞時使用的是Messenger.send(Message)
方法。
服務(wù)端向客戶端發(fā)送了消息,那末服務(wù)端向客戶端發(fā)送消息也類似:
關(guān)鍵點: 客戶端向服務(wù)端發(fā)送消息是,通過msg.replyTo
將客戶端Messenger
對象傳給服務(wù)端。
客戶端代碼進(jìn)行修改:
Handler
和Messenger
對象。send()
方法。
/**
* 構(gòu)建handler 對象
*/
public static Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
// 接受服務(wù)端發(fā)送的消息
String msgService = msg.getData().getString("msg");
Log.i("messenger","接收到服務(wù)真?zhèn)€消息--"+msgService);
};
};
// 通過handler 構(gòu)建Mesenger 對象
private final Messenger messengerClient = new Messenger(handler);
/**
* 布局文件中添加了1個按鈕,點擊該按鈕的處理方法
* @param view
*/
public void send(View view) {
try {
// 向服務(wù)端發(fā)送消息
Message message = Message.obtain();
Bundle data = new Bundle();
data.putString("msg", "lalala");
message.setData(data);
// ----- 傳入Messenger 對象
message.replyTo = messengerClient;
// 發(fā)送消息
mService.send(message);
Log.i("messenger","向服務(wù)端發(fā)送了消息");
} catch (Exception e) {
e.printStackTrace();
}
}
服務(wù)端代碼修改
/**
* 構(gòu)建handler 對象
*/
public static Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
// 接受客戶端發(fā)送的消息
String msgClient = msg.getData().getString("msg");
Log.i("messenger", "接收到客戶真?zhèn)€消息--" + msgClient);
// 獲得客戶端Messenger 對象
Messenger messengetClient = msg.replyTo;
// 向客戶端發(fā)送消息
Message message = Message.obtain();
Bundle data = new Bundle();
data.putString("msg", "ccccc");
message.setData(data);
try {
// 發(fā)送消息
messengetClient.send(message);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
};
結(jié)果不在演示了。
AIDL是1種接口定義語言,用于束縛兩個進(jìn)程間的通訊規(guī)則,供編譯器生成代碼,實現(xiàn)Android裝備上的兩個進(jìn)程間通訊(IPC)。
進(jìn)程之間的通訊信息,首先會被轉(zhuǎn)換成AIDL協(xié)議消息,然后發(fā)送給對方,對方收到AIDL協(xié)議消息后再轉(zhuǎn)換成相應(yīng)的對象。
AIDL
的關(guān)鍵便是Binder
,關(guān)于Binder
,后面的博客會分析。在這里之將如何使用它。
由于需要服務(wù)端和客戶端共用aidl
文件,所以最好單獨建1個包,合適拷貝到客戶端。
服務(wù)端:
com.example.ipc.aidl
BookAidl.java
,該對象需要作為傳輸。所以需要實現(xiàn)Parcelable
。public class BookAidl implements Parcelable {
public int bookId;
public String bookName;
public BookAidl() {
super();
}
public BookAidl(int bookId, String bookName) {
super();
this.bookId = bookId;
this.bookName = bookName;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
public static final Parcelable.Creator<BookAidl> CREATOR = new Creator<BookAidl>() {
@Override
public BookAidl[] newArray(int size) {
return new BookAidl[size];
}
@Override
public BookAidl createFromParcel(Parcel source) {
BookAidl book = new BookAidl();
book.bookId = source.readInt();
book.bookName = source.readString();
return book;
}
};
@Override
public String toString() {
return "BookAidl [bookId=" + bookId + ", bookName=" + bookName + "]";
}
}
Parcelable
在前面已說過,不在多說。
.aidl
文件。由于需要用到BookAidl
對象,所以需要先聲明。創(chuàng)建BookAidl.aidl
文件,并手動添加
package com.example.ipc.aidl;
parcelable BookAidl;
創(chuàng)建IBookManager.aidl
文件,接口文件,面向客戶端調(diào)用
package com.example.ipc.aidl;
import com.example.ipc.aidl.BookAidl;
interface IBookManager{
List<BookAidl> getBookList();
void addBook(in BookAidl book);
}
寫完以后clean
1下工程,以后會在gen
目錄下生成對應(yīng)的java文件。此java中的具體含義后面會解釋,在此不做多述。
Service
類。public class BookService extends Service {
/**
* 支持線程同步,由于其存在多個客戶端同時連接的情況
*/
private CopyOnWriteArrayList<BookAidl> list = new CopyOnWriteArrayList<>();
/**
* 構(gòu)造 aidl中聲明的接口的Stub對象,并實現(xiàn)所聲明的方法
*/
private Binder mBinder = new IBookManager.Stub() {
@Override
public List<BookAidl> getBookList() throws RemoteException {
return list;
}
@Override
public void addBook(BookAidl book) throws RemoteException {
list.add(book);
Log.i("aidl", "服務(wù)端添加了1本書"+book.toString());
}
};
@Override
public void onCreate() {
super.onCreate();
//加點書
list.add(new BookAidl(1, "java"));
list.add(new BookAidl(2, "android"));
}
@Override
public IBinder onBind(Intent intent) {
// 返回給客戶真?zhèn)€Binder對象
return mBinder;
}
}
在Service
中,主要干了兩件事情:
Binder
對象通過onBinder
返回給客戶端。為了省事,在這里不在另起1個工程了,直接將Service
在另外一個進(jìn)程中運行。
<service
android:name="com.example.ipc.BookService"
android:process=":remote" />
開始編寫客戶端
由于在同1個工程中,不需要拷貝aidl
包中的文件。如果不在同1個工程,需要拷貝。
public class BookActivity extends AppCompatActivity{
/**
* 接口對象
*/
private IBookManager mService;
/**
* 綁定服務(wù)的回調(diào)
*/
private ServiceConnection conn = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 獲得到書籍管理的對象
mService = IBookManager.Stub.asInterface(service);
Log.i("aidl", "連接到服務(wù)端,獲得IBookManager的對象");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book);
// 啟動服務(wù)
Intent intent = new Intent(this,BookService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
}
/**
* 獲得服務(wù)端書籍列表
* @param view
*/
public void getBookList(View view){
try {
Log.i("aidl","客戶端查詢書籍"+mService.getBookList().toString());
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 添加書籍
*/
public void add(View view){
try {
// 調(diào)用服務(wù)端添加書籍
mService.addBook(new BookAidl(3,"ios"));
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
客戶真?zhèn)€代碼和之前的Messenger
很類似:
IBinder service
通過IBookManager.Stub.asInterface()
轉(zhuǎn)化為借口對象。效果
總結(jié)來講可分為以下幾步
服務(wù)端:
客戶端
作為android 4大組件之1,雖然用的地方不是太多。但是其確切是多進(jìn)程通訊的1種方式。例如,獲得通訊錄信息,這明顯跨利用了,肯定是多進(jìn)程通訊啊。
其底層實現(xiàn)和Messenger
1樣,都是通過Binder
,后面會專門分析Binder
對象。
ContentProvider
很多介紹,在這不在多提。
Socket
也稱為“套接字”,是網(wǎng)絡(luò)通訊中的概念,它分為流式套接字和用戶數(shù)據(jù)報套接字,分別對應(yīng)于網(wǎng)絡(luò)傳輸中的傳輸控制層的TCP和UDP。
該方面使用的是JAVA 方面的知識。該舉例只是說明1個思路。不做細(xì)致的實現(xiàn)。
服務(wù)端
public class SocketService extends Service {
/**
* 連接的狀態(tài)
*/
private boolean isConnState = true;
@Override
public void onCreate() {
super.onCreate();
// 啟動TCP 服務(wù)
new Thread(new TCPServer()).start();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
// 結(jié)束TCP 服務(wù)
isConnState = false;
super.onDestroy();
}
/**
* 服務(wù)端TCP 服務(wù),相當(dāng)于服務(wù)器,接受Socket 連接
* @author MH
*
*/
class TCPServer implements Runnable{
@Override
public void run() {
try {
// 監(jiān)聽本地的12345 端口
ServerSocket ss = new ServerSocket(12345);
while(isConnState){
// 獲得客戶真?zhèn)€Socket 對象
Socket socket = ss.accept();
// 獲得輸入流 ---
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 通過輸入流讀取客戶真?zhèn)€消息
//String line = br.readLine();
// 輸出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
// 通過輸出流向客戶端發(fā)送消息
//bw.write("....");
// 關(guān)閉連接
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
服務(wù)啟動時,在onCreate
方法中啟動了TCPServer
,該線程時刻接受客戶真?zhèn)€要求。
客戶端
public void conn(){
try {
// 指定ip和端口
Socket s = new Socket("localhost", 12345);
// ----- 和服務(wù)端類似
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//String line = br.readLine();
// 輸出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//bw.write("....");
// 關(guān)閉連接
s.close();
} catch (Exception e) {
e.printStackTrace();
}
}
關(guān)于Socket
,在此只是1個簡單的示范,具體的使用博大精深。知道能實現(xiàn)便可。
Bundle
:4大組件間的進(jìn)程間通訊方式,簡單易用,但傳輸?shù)臄?shù)據(jù)類型受限。Messenger
: 數(shù)據(jù)通過Message
傳輸,只能傳輸Bundle
支持的類型ContentProvider
:android 系統(tǒng)提供的。簡單易用。但使用受限,只能根據(jù)特定規(guī)則訪問數(shù)據(jù)。AIDL
:功能強(qiáng)大,支持實時通訊,但使用略微復(fù)雜。Socket
:網(wǎng)絡(luò)數(shù)據(jù)交換的經(jīng)常使用方式。不推薦使用。在實現(xiàn)多進(jìn)程通訊時,其中Messenger
,ContentProvider
,AIDL
的底層實現(xiàn)都是Binder
,很有必要對其進(jìn)行繼續(xù)分析。
該博客中的代碼以同享到github。 https://github.com/AlexSmille/Android-IPC-Example
上一篇 C++中enum的使用
下一篇 部分SWAP 內(nèi)存知識