編輯:關於Android編程
IPC(Inter-Process Communication) 進程間通信,是指兩個不同進程之間數據交換的過程。
在明確其之前,需要先搞懂幾個概念:
線程:CPU可調度的最小單位,是程序執行流的最小單元;線程是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程自己不擁有系統資源,只擁有一點兒在運行中必不可少的資源,但它可與同屬一個進程的其它線程共享進程所擁有的全部資源。 進程: 一個執行單元,在PC 和移動設備上一般指一個程序或者應用,一個進程可以包含多個線程。每一個進程都有它自己的地址空間,一般情況下,包括文本區域(text region)、數據區域(data region)和堆棧(stack region)。在Android程序中,一般情況下一個程序就是一個進程(在無特別的代碼實現下),UI線程即主線程。如果有耗時操作,則會導致主線程堵死。而在Android中主線程負責UI,和用戶交互,如果堵死肯定影響用戶的使用度。所以Android要求不能再主線程中有耗時操作,這時就要將耗時操作放在子線程中。
在Android 中,每一個應用可使用的內存大小有限制,早起的一些版本在16M左右,不同的設備有不同的大小。可以通過多進程獲取多份內存空間。
Android中開啟多進程只有一種方法,便是給四大組件(Activity
,Receiver
,ContentProvider
,Service
)指定android
屬性,初次之外沒有其他方法。請注意,不能指定某一個線程或者實體類指定其所運行的進程
:process
通過jni調用底層去開啟多進程也是一種方法,但屬於特殊情況,不進行考慮;
首先編寫三個Activity
,並在清單文件中做如下注冊:
對MainActivity
不進行指定,則默認為當前進程。
對SecondActivity
指定屬性android:process=":remote"
。
對ThirdActivity
指定屬性android:process=".remote"
。
注意SencodActivity
和ThirdActivity
的進程參數不同。
把三個頁面都打開,通過DDMS可以看到三個進程的開啟
啟動了三個進程:分別是
com.example.ipc
:默認的程序進程。和包名相同。 com.example.ipc:remote
:SecondActivity
所在的進程。 .remote
:ThirdActivity
所在的進程。
那麼2和3 ,進程名不同有什麼區別嗎;
如果進程名以:開始,表示是要在當前的進程名前附加上當前的包名,表示該進程是本應用的私有進程,其他應用不可以和其跑在同一個進程。 如果進程名不以:開始,表示不需附加包名信息,是一個完全的命名。同時該進程是全局進程,其他應用可以通過ShareUID
和其跑在同一個進程中。
通過如上方式,很簡單的變開啟了多進程,但是,如果僅僅這樣的話,會有大問題。
看下面一個例子:
添加一個公有的類,添加靜態字段:
/**
* 類似平常開發中的工具類等
* @author MH
*
*/
public class PublicContant {
public static int m = 1;
}
在MainActivity
中Log一下並修改字段
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("info", PublicContant.m+"");
PublicContant.m++;
}
在SecondActivity
中Log一下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Log.i("info", PublicContant.m+"");
}
根據上面的邏輯,Log信息應該是1,和2 。但是呢,不是這樣的。
兩個都是1,我靠。。先不問原因,看結果就知道是錯的。多進程這麼不靠譜,腫麼回事??
Android 為每一個進程都分配一個獨立的虛擬機,不同的虛擬機在內存分配上有不同的地址空間,這就導致在不同虛擬機中訪問同一個類對象會產生多個副本。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPrbU09q1scewwLTLtaOsvfizzDxjb2RlPmNvbS5leGFtcGxlLmlwYzwvY29kZT66zTxjb2RlPmNvbS5leGFtcGxlLmlwYzpyZW1vdGU8L2NvZGU+try05tTa0ru49jxjb2RlPlB1YmxpY0NvbnRhbnQ8L2NvZGU+wOCjrLKix9LV4sG9uPbA4MrHz+C7pbK7uMnIxbXEo6zSu7j2vfizzNbQ0N64xMHLuMPWtbXEttTP86OsttTG5Mv7vfizzNbQtcS4w9a1sru74dTss8nIzrrO07DP7KGjPC9wPg0KPGJsb2NrcXVvdGU+DQoJPHA+1MvQ0NTazazSu7j2vfizzNbQtcTX6bz+ysfK9NPazazSu7j20OnE4rv6us3NrNK7uPY8Y29kZT5BcHBsaWNhdGlvbjwvY29kZT61xKGjzazA7aOs1MvQ0NTasrvNrL34s8zW0LXE1+m8/srHyvTT2sG9uPayu82stcTQ6cTiu/q6zTxjb2RlPkFwcGxpY2F0aW9uPC9jb2RlPrXEoaM8L3A+DQo8L2Jsb2NrcXVvdGU+DQo8cD64+b7dyOfJz8v5yvajrLbgvfizzMv51OyzybXEzsrM4rfWzqrI58/CvLi49re9w+ajujwvcD4NCr6yzKyzydSxus21pcD9xKPKvc3qyKvKp9CnPGJyIC8+DQrI58nPt9bO9qOstLS9qMHLsrvNrLXExNq05qOstuC49rbUz/OjrLWxyLu1pcD9yrLDtLXEtrzO3tCnwcuhoyDP37PMzayyvbv61sbN6siryqfQpzxiciAvPg0KsrvKx9K7v+nE2rTmx/jT8qOsz9+zzMv4tbHIu87e0KfBy6GjIDxjb2RlPlNoYXJlZFByZWZlcmVuY2U8L2NvZGU+tcS/yb+/0NTPwr21PGJyIC8+DQo8Y29kZT5zaGFyZWRQcmVmZXJlbmNlPC9jb2RlPrXEtdey48q1z9bKx82ouf22wdC0WE1MzsS8/qOswb249r34s8zIpbbB0LSjrLKit6LP1Mi7yse/ycTcs/bP1s7KzOK1xKGjIDxjb2RlPkFwcGxpY2F0aW9uPC9jb2RlPrvhtuC0zrS0vagNCjxoMyBpZD0="序列化和反序列化">序列化和反序列化
在了解多進程通信之前,我們需要了解兩個基礎的概念,序列化和反序列化。
序列化:將對象轉化為可保存的字節序列。(注意,是對象)。 反序列:將字節序列恢復為對象的過程。序列化和反序列的用途:
以某種存儲形式使自定義對象序列化。 將對象從一個地方傳遞到另一個地方。 通過序列化在進程間傳遞對象。在Android
中實現序列化的方式有兩種,Serializable
和Parcelable
。
Serializable
是Java提供的一個序列化接口,他是一個空接口,是類實現該接口即可實現序列化。
/**
* Serializable 序列化對象
* @author MH
*
*/
public class Book implements Serializable {
/**
* 序列化和反序列的關鍵
*/
private static final long serialVersionUID = 1L;
public int bookId;
public String bookName;
}
在實現Serializable
時候,編譯器會提示,讓我們添加serialVersionUID
字段,該字段是一個關鍵的字段,後面會說。
相應的實現好了,那麼如何寫入和讀取呢?
寫入
public void writeSerializable() {
try {
// 構造對象
Book book = new Book();
// 構造序列化輸出字節流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("xxx.txt"));
// 序列化對象
oos.writeObject(book);
// 關閉流
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
讀取
public void readSerializable() {
try {
// 創建序列化讀取字節流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
"xxx.txt"));
// 反序列化(讀取)對象
Book book = (Book) ois.readObject();
// 關閉流
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
}
在序列化時,如果我們序列化對象之後,改變了我們的類結構(添加或改變字段),甚至是修改了字段的類型,修改了類名,那麼我們能反序列化成功嗎。
那麼關鍵就在於serialVersionUID
字段。
如果我們不指定的話。在序列化時,會計算當前類結構的hash值並將該值賦給serialVersionUID
,當反序列時,會比對該值是否相同,如果不相同,則無法序列化成功。
我們也可以手動指定,手動指定的好處是在類結構發生變化時,能夠最大程度的反序列,當然前提是只是刪除或添加了字段,如果是變量類型發生了變化,則依然無法反序列成功。
serialVersionUID 的工作機制:序列化時系統會把當前類的
serialVersionUID
寫入序列化文件中,當反序列化時候系統會去檢測文件中的serialVersionUID
,看它是否和當前類的serialVersionUID
一致,如果一致說明序列化類的版本和當前類的版本是相同的,這個時候可以成功反序列化,否則就說明當前類和序列化的類相比發生了某些變化。所以,我們最好指定serialVersionUID
,避免他自定生成。
Parcelable
是Android中特有的一種序列化方式,在intent
傳值時,通常使用該方式。
該方式實現序列化,依然實現Parcelable
,然後實現一些該接口的方法。
/**
* Parcelable 對象的使用方式
* @author MH
*
*/
public class Book implements Parcelable {
public int bookId;
public String bookName;
@Override
public int describeContents() {
// 返回當前對象的內容描述。幾乎所有情況下都是返回0
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// 將當前對象寫入到序列化結構中
dest.writeInt(bookId);
dest.writeString(bookName);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
@Override
public Book createFromParcel(Parcel source) {
//從序列化後的對象中創建原始的值
Book book = new Book();
book.bookId = source.readInt();
book.bookName = source.readString();
return book;
}
@Override
public Book[] newArray(int size) {
//創建指定長度的原始對象數組
return new Book[size];
}
};
}
Parcelable
實現兩個方法,創建一個字段:
describeContents()
:返回當前對象的內容描述。幾乎所有情況下都是返回0。 實現public void writeToParcel(Parcel dest, int flags)
:// 將當前對象寫入到序列化結構中 構造Parcelable.Creator
字段,該對象需要實現兩個方法:public Book createFromParcel(Parcel source)
:從序列化後的對象中創建原始的值。 public Book[] newArray(int size)
:創建指定長度的原始對象數組
Serializable
是Java中的序列化接口,其使用起來簡單但是開銷較大,序列化和反序列化需要大量的I/O操作。 Parcelable
是Android中的序列化方式,更適用於Android的平台上,他的缺點是使用起來稍微麻煩,但是效率很高。 Parcelable
適合進程間的通信,運行期。Serializable
適合文件存儲即網絡傳輸。
Android中的四大組件中,其中有三大組件(Activity
,Service
,Receiver
)都支持Intent
中傳遞Bundle
數據,如果看其源碼,會發現其也是實現了Parcelable
接口,所以其能夠在不同進程中傳輸。
當然在傳輸的過程中,其所傳輸的數據必須支持序列化。比如基本數據類型,字符串,Parcelable
的實現類,Serializable
的實現類。由於該方法非常常用,不在多說。
文件共享: 將對象序列化之後保存到文件中,在通過反序列,將對象從文件中讀取。
在MainActvity
中寫寫入對象
/**
* 寫入序列化對象
*/
public void wirte() {
Book book = new Book();
book.bookId = 1;
book.bookName = "si";
try {
// 構造序列化輸出字節流
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(PATH));
// 序列化對象
oos.writeObject(book);
// 關閉流
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(book);
}
在SecondActivity
中,讀取文件(反序列化)
public void read() {
Book book = null;
try {
// 創建序列化讀取字節流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
MainActivity.PATH));
// 反序列化(讀取)對象
book = (Book) ois.readObject();
// 關閉流
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(book);
}
LOG 結果
06-28 09:20:47.916: com.example.ipc(進程名) I/System.out(12399): Book [bookId=1, bookName=si]
06-28 09:20:53.376: com.example.ipc:remote(進程名) I/System.out(12866): Book [bookId=1, bookName=si]
分屬不同的進程成功的獲取到了共享的數據。
通過共享文件這種方式來共享數據對文件的格式是沒有具體的要求的。比如可以是文件,也可以是Xml,JSON 等。只要讀寫雙方約定一定的格式即可。
同文件共享方式也存在著很大的局限性。即並發讀/ 寫的問題。讀/寫會造成數據不是最新。同寫很明顯會出現錯誤。
文件共享適合在對數據同步要求不高的進程之間進行通信。並且要妥善處理並發讀寫的問題。
SharedPreference 底層文件的方式。不適合在多進程中共享數據。
Messenger
可以翻譯為信使,通過該對象,可以在不同的進程中傳遞Message
對象。注意,兩個單詞不同。
下面就通過服務端(Service)和客戶端(Activity)的方式進行演示。
客戶端向服務端發送消息,可分為以下幾步。
服務端
創建Service
構造Handler
對象,實現handlerMessage
方法。 通過Handler
對象構造Messenger
信使對象。 通過Service
的onBind()
返回信使中的Binder
對象。
客戶端
創建Actvity
綁定服務 創建ServiceConnection
,監聽綁定服務的回調。 通過onServiceConnected()
方法的參數,構造客戶端Messenger
對象 通過Messenger
向服務端發送消息。
實現服務端
/**
* Messenger 的使用 服務端
* @author MH
*
*/
public class MessengerService extends Service {
/**
* 構建handler 對象
*/
public static Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
// 接受客戶端發送的消息
String msgClient = msg.getData().getString("msg");
Log.i("messenger","接收到客戶端的消息--"+msgClient);
};
};
// 通過handler 構建Mesenger 對象
private final Messenger messenger = new Messenger(handler);
@Override
public IBinder onBind(Intent intent) {
// 返回binder 對象
return messenger.getBinder();
}
}
注冊服務別忘了 ,同時對服務修改其進程。
實現客戶端
/**
* 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 對象
// 通過服務端返回的Binder 對象 構造Messenger
mService = new Messenger(service);
Log.i("messenger", "客戶端以獲取服務端Messenger對象");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
// 啟動服務
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
}
/**
* 布局文件中添加了一個按鈕,點擊該按鈕的處理方法
* @param view
*/
public void send(View view) {
try {
// 向服務端發送消息
Message message = Message.obtain();
Bundle data = new Bundle();
data.putString("msg", "lalala");
message.setData(data);
// 發送消息
mService.send(message);
Log.i("messenger","向服務端發送了消息");
} catch (Exception e) {
e.printStackTrace();
}
}
}
看一下結果:
注釋很清楚,不在多說,按照流程實現即可。 其中有一點需要注意:
我們是通過Message
作為媒介去攜帶數據的。但是,Message
的obj 並沒有實現序列化(實現Serializable
或Parcelable
),也就是其不能保存數據。必須使用message.setData()
方法去傳入一個Bundle
對象,Bundle
中保存需要傳入的數據。
傳遞時使用的是Messenger.send(Message)
方法。
服務端向客戶端發送了消息,那麼服務端向客戶端發送消息也類似:
關鍵點: 客戶端向服務端發送消息是,通過msg.replyTo
將客戶端Messenger
對象傳給服務端。
客戶端代碼進行修改:
創建客戶端Handler
和Messenger
對象。 修改send()
方法。
/**
* 構建handler 對象
*/
public static Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
// 接受服務端發送的消息
String msgService = msg.getData().getString("msg");
Log.i("messenger","接收到服務端的消息--"+msgService);
};
};
// 通過handler 構建Mesenger 對象
private final Messenger messengerClient = new Messenger(handler);
/**
* 布局文件中添加了一個按鈕,點擊該按鈕的處理方法
* @param view
*/
public void send(View view) {
try {
// 向服務端發送消息
Message message = Message.obtain();
Bundle data = new Bundle();
data.putString("msg", "lalala");
message.setData(data);
// ----- 傳入Messenger 對象
message.replyTo = messengerClient;
// 發送消息
mService.send(message);
Log.i("messenger","向服務端發送了消息");
} catch (Exception e) {
e.printStackTrace();
}
}
服務端代碼修改
/**
* 構建handler 對象
*/
public static Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
// 接受客戶端發送的消息
String msgClient = msg.getData().getString("msg");
Log.i("messenger", "接收到客戶端的消息--" + msgClient);
// 獲取客戶端Messenger 對象
Messenger messengetClient = msg.replyTo;
// 向客戶端發送消息
Message message = Message.obtain();
Bundle data = new Bundle();
data.putString("msg", "ccccc");
message.setData(data);
try {
// 發送消息
messengetClient.send(message);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
};
結果不在演示了。
AIDL是一種接口定義語言,用於約束兩個進程間的通訊規則,供編譯器生成代碼,實現Android設備上的兩個進程間通信(IPC)。
進程之間的通信信息,首先會被轉換成AIDL協議消息,然後發送給對方,對方收到AIDL協議消息後再轉換成相應的對象。
AIDL
的關鍵便是Binder
,關於Binder
,後面的博客會分析。在這裡之將如何使用它。
因為需要服務端和客戶端共用aidl
文件,所以最好單獨建一個包,適合拷貝到客戶端。
服務端:
添加如下包名:com.example.ipc.aidl
創建BookAidl.java
,該對象需要作為傳輸。所以需要實現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 CREATOR = new Creator() {
@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
對象,所以需要先聲明。
創建BookAidl.aidl
文件,並手動添加
package com.example.ipc.aidl;
parcelable BookAidl;
創建IBookManager.aidl
文件,接口文件,面向客戶端調用
package com.example.ipc.aidl;
import com.example.ipc.aidl.BookAidl;
interface IBookManager{
List getBookList();
void addBook(in BookAidl book);
}
寫完之後clean
一下工程,之後會在gen
目錄下生成對應的java文件。此java中的具體含義後面會解釋,在此不做多述。
Service
類。
public class BookService extends Service {
/**
* 支持線程同步,因為其存在多個客戶端同時連接的情況
*/
private CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
/**
* 構造 aidl中聲明的接口的Stub對象,並實現所聲明的方法
*/
private Binder mBinder = new IBookManager.Stub() {
@Override
public List getBookList() throws RemoteException {
return list;
}
@Override
public void addBook(BookAidl book) throws RemoteException {
list.add(book);
Log.i("aidl", "服務端添加了一本書"+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) {
// 返回給客戶端的Binder對象
return mBinder;
}
}
在Service
中,主要干了兩件事情:
Binder
對象通過onBinder
返回給客戶端。
為了省事,在這裡不在另起一個工程了,直接將Service
在另一個進程中運行。
開始編寫客戶端
因為在同一個工程中,不需要拷貝aidl
包中的文件。如果不在同一個工程,需要拷貝。
public class BookActivity extends AppCompatActivity{
/**
* 接口對象
*/
private IBookManager mService;
/**
* 綁定服務的回調
*/
private ServiceConnection conn = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 獲取到書籍管理的對象
mService = IBookManager.Stub.asInterface(service);
Log.i("aidl", "連接到服務端,獲取IBookManager的對象");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book);
// 啟動服務
Intent intent = new Intent(this,BookService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
}
/**
* 獲取服務端書籍列表
* @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 {
// 調用服務端添加書籍
mService.addBook(new BookAidl(3,"ios"));
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
客戶端的代碼和之前的Messenger
很類似:
IBinder service
通過IBookManager.Stub.asInterface()
轉化為借口對象。 調用借口對象的方法。
效果
總結來說可分為如下幾步
服務端:
服務端創建.aidl文件和聲明接口 創建類,繼承Service,並實現onBind方法 在Service類中定義aidl中聲明接口的Stub對象,並實現aidl接口中聲明的方法 在onBind方法中返回Stub對象 在AndroidManifest.xml中注冊Service並聲明其Action客戶端
使用服務端提供的aidl文件 在Activity定義aidl接口對象 定義ServiceConnection對象,監聽綁定服務的回調 回調中通過方法獲取借口對象作為android 四大組件之一,雖然用的地方不是太多。但是其確實是多進程通信的一種方式。例如,獲取通訊錄信息,這明顯跨應用了,肯定是多進程通信啊。
其底層實現和Messenger
一樣,都是通過Binder
,後面會專門分析Binder
對象。
ContentProvider
很多介紹,在這不在多提。
Socket
也稱為“套接字”,是網絡通信中的概念,它分為流式套接字和用戶數據報套接字,分別對應於網絡傳輸中的傳輸控制層的TCP和UDP。
該方面使用的是JAVA 方面的知識。該舉例只是說明一個思路。不做細致的實現。
服務端
public class SocketService extends Service {
/**
* 連接的狀態
*/
private boolean isConnState = true;
@Override
public void onCreate() {
super.onCreate();
// 啟動TCP 服務
new Thread(new TCPServer()).start();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
// 結束TCP 服務
isConnState = false;
super.onDestroy();
}
/**
* 服務端TCP 服務,相當於服務器,接受Socket 連接
* @author MH
*
*/
class TCPServer implements Runnable{
@Override
public void run() {
try {
// 監聽本地的12345 端口
ServerSocket ss = new ServerSocket(12345);
while(isConnState){
// 獲取客戶端的Socket 對象
Socket socket = ss.accept();
// 獲取輸入流 ---
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 通過輸入流讀取客戶端的消息
//String line = br.readLine();
// 輸出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
// 通過輸出流向客戶端發送消息
//bw.write("....");
// 關閉連接
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
服務啟動時,在onCreate
方法中啟動了TCPServer
,該線程時刻接受客戶端的請求。
客戶端
public void conn(){
try {
// 指定ip和端口
Socket s = new Socket("localhost", 12345);
// ----- 和服務端類似
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//String line = br.readLine();
// 輸出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//bw.write("....");
// 關閉連接
s.close();
} catch (Exception e) {
e.printStackTrace();
}
}
關於Socket
,在此只是一個簡單的示范,具體的使用博大精深。知道能實現即可。
Bundle
:四大組件間的進程間通信方式,簡單易用,但傳輸的數據類型受限。 文件共享: 不適合高並發場景,並且無法做到進程間的及時通信。 Messenger
: 數據通過Message
傳輸,只能傳輸Bundle
支持的類型 ContentProvider
:android 系統提供的。簡單易用。但使用受限,只能根據特定規則訪問數據。 AIDL
:功能強大,支持實時通信,但使用稍微復雜。 Socket
:網絡數據交換的常用方式。不推薦使用。
在實現多進程通信時,其中Messenger
,ContentProvider
,AIDL
的底層實現都是Binder
,很有必要對其進行繼續分析。
該博客中的代碼以共享到github。 https://github.com/AlexSmille/Android-IPC-Example
1. 備忘錄設計模式介紹在不破壞封閉的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態,這樣,以後就可將該對象恢復到原先保存的狀態。2. 備忘錄設計模式使用場景
本文實例分析了Android模擬器接收UDP數據包的若干問題。分享給大家供大家參考,具體如下:android模擬器無法接收UDP數據包代碼如下:DatagramPacke
源碼下載 之前用zxing做開發,各種奇葩問題,橫屏修等等,而且性能也不搞。被測試批了,沒辦法後來換了zbar。性能好多了。 直接上圖,看看效果 2.界面上的
1.使用Service2.綁定Service3.Service的生命周期--------------------------------------------1.使用S