做者:享學課堂終身VIP週週java
轉載請聲明出處!android
手把手講解系列文章,是我寫給各位看官,也是寫給我本身的。文章可能過度詳細,可是這是爲了幫助到儘可能多的人,畢竟工做5,6年,不能老吸血,也到了回饋開源的時候. 這個系列的文章:git
一、用通俗易懂的講解方式,講解一門技術的實用價值github
二、詳細書寫源碼的追蹤,源碼截圖,繪製類的結構圖,儘可能詳細地解釋原理的探索過程小程序
三、提供Github 的 可運行的Demo工程,可是我所提供代碼,更可能是提供思路,拋磚引玉,請酌情cvbash
四、集合整理原理探索過程當中的一些坑,或者demo的運行過程當中的注意事項服務器
五、用gif圖,最直觀地展現demo運行效果微信
若是以爲細節太細,直接跳過看結論便可。本人能力有限,如若發現描述不當之處,歡迎留言批評指正。app
學到老活到老,路漫漫其修遠兮。與衆君共勉 !框架
1、 概念QA以及前置技能
2、 傳統方式IPC通訊寫法 與 使用IPC框架進行RPC通訊的對比
3、Demo展現
4、框架核心思想講解
5、 寫在最後的話
Q:何時會用到多進程通訊?
A: 常見的多進程
app
通常是大型公司的 app組,像是騰訊系的QQ微信QQ空間,QQ郵箱
等等,有可能 在QQ郵箱
登陸時,能夠直接調用QQ的登陸服務
,另外,騰訊阿里都有小程序,做爲一個第三方開發的小程序應用,在微信客戶端運行
,若是和微信放在同一個進程運行,一旦崩潰
,微信也跟着玩完,明明是小程序開發者的鍋
,硬是讓騰訊給背
了,不合適。而小型公司,emmmmm,連多進程開發都用的不多,就不要說通訊了。可是,若是沒有一顆進大廠的心,就學不到高階技能,有些東西學了,總比一無所知要好。
Q:使用多進程有什麼好處?
A: 1)進程隔離,子
app
崩潰,不會影響其餘進程。2)系統運行期間,對每一個進程的內存劃分是有一個上限的,具體多少,視具體設備而定,利用多進程開發,能夠提升程序的可運行內存限制。
3)若是系統運行期間內存吃緊,能夠殺死子進程,減小系統壓力。殺死進程的方式,每每比優化單個app的內存更加直接有效
Q:什麼叫
RPC
?A:從客戶端上經過參數傳遞的方式調用服務器上的一個函數並獲得返回的結果,隱藏底層的通信細節。在使用形式上像調用
本地函數
同樣去調用遠程函數
。
Q:咱們本身定義一個
RPC
進程間通訊框架,有什麼實際用處? A:定義框架的做用,都是 把髒活,累活,別人不肯意重複乾的活,都放到框架裏面去,讓使用者用最乾淨的方式使用業務接口。定義一個RPC
進程間通訊框架,能夠把C/S兩端那些噁心人的AIDL
編碼都集中放到框架module
中,這是最直觀的好處,另外,客戶端本來還須要手動去bindService
,定義ServiceConnection
,取得Binder
,再去通訊,使用RPC
框架,這些內容均可以放到框架module
中. 而C/S兩端的代碼,就只剩下了S端
的服務註冊,C端
的RPC
接口調用,代碼外觀上很是簡潔(可能這裏文字描述不夠直觀,後面有圖)
要理解本文的核心代碼,仍是須要一些基礎的,大體以下:四大組件之一
Service
使用方法, androidAIDL
通訊機制, java註解,java反射,java 泛型
傳統方式IPC通訊寫法
與 使用IPC框架進行RPC通訊
的對比見github : github.com/18598925736… , 運行 aidl_client
和 aidl_service
圖中的 查找用戶
,是從 服務端
讀取的數據,觀察一下核心代碼:
這是我優化以後的 IPC
項目結構( 若是不優化,那麼客戶端服務端都須要編寫同樣的AIDL代碼,還要有一個包括包名在內神馬都要如出一轍的JavaBean,實在是醜陋):
服務端
核心代碼:
public class ServiceActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this, MyService.class));//服務端,app啓動以後,自動啓動服務
}
}
複製代碼
public class MyService extends Service {
ConcurrentMap<String, UserInfoBean> map;
@Nullable
@Override
public IBinder onBind(Intent intent) {
map = new ConcurrentHashMap<>();
for (int i = 0; i < 100; i++) {
map.put("name" + i, new UserInfoBean("name" + i, "accountNo" + i, i));
}
return new IUserInfo.Stub() {
//數據接收器 Stub
@Override
public UserInfoBean getInfo(String name) {
return map.get(name);
}
}
;
}
@Override
public void onCreate() {
super.onCreate();
Log.e("MyService", "onCreate: success");
}
}
複製代碼
客戶端
核心代碼 :
public class ClientActivity extends AppCompatActivity {
private TextView resultView;
private String TAG = "clientLog";
private int i = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
resultView = findViewById(R.id.resultView);
findViewById(R.id.connect).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bindService();
}
}
);
findViewById(R.id.disconnect).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
unbindService(connection);
resultView.setText("嘗試釋放");
}
catch (IllegalArgumentException e) {
resultView.setText("已經釋放了");
}
}
}
);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (iUserInfo != null) {
try {
((Button) v).setText("查找name爲:name" + ((i++) + 1) + "的UserInfoBean");
UserInfoBean bean = iUserInfo.getInfo("name" + i);
if (bean != null)
resultView.setText(bean.toString()); else
resultView.setText("沒找到呀");
}
catch (RemoteException e) {
e.printStackTrace();
}
} else {
resultView.setText("沒有鏈接上service");
}
}
}
);
}
//做爲IPC的客戶端,咱們須要 創建起和Service的鏈接
private IUserInfo iUserInfo;
//操做句柄,能夠經過它向service發送數據
private void bindService() {
if (iUserInfo == null) {
Intent intent = new Intent();
intent.setComponent(new ComponentName(
"study.hank.com.aidl_service",
"study.hank.com.aidl_service.MyService"));
bindService(intent, connection, Context.BIND_AUTO_CREATE);
resultView.setText("嘗試鏈接");
} else {
resultView.setText("已經鏈接上service" + iUserInfo);
}
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iUserInfo = IUserInfo.Stub.asInterface(service);
resultView.setText("鏈接成功");
Log.d(TAG, "connection:" + "鏈接成功");
}
@Override
public void onServiceDisconnected(ComponentName name) {
iUserInfo = null;
resultView.setText("鏈接 已經斷開");
Log.d(TAG, "connection:" + "已經斷開");
}
}
;
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
}
複製代碼
很容易發現,服務端的代碼量尚可,不是很複雜,可是客戶端這邊,要處理 connection
,要手動去綁定以及解綁 Service
,全部參與通訊的 javabean
還必須實現序列化接口 parcelable Demo
中只有一個客戶端,還不是很明顯,可是若是有 N
個客戶端 Activity
都須要與 service
發生通訊,意味着每個 Activity
都必須寫相似的代碼. 不但 累贅
,並且 醜陋
.
不使用RPC框架時,CS兩端的代碼的結構,已經有了大體的印象,下面是 使用IPC框架時
客戶端、服務端 的核心代碼
以前的
bindService
呢?沒了。客戶端使用此框架來進行 進程通訊,不用去關心AIDL
怎麼寫了,不用關注bindService,ServiceConnection
,省了不少事。
有什麼變化?顯而易見,極大縮減了
客戶端
的編碼量,並且,一勞永逸,除非需求大改,否則這個框架,一次編寫,終身使用。除此以外,使用框架還能夠極大地節省客戶端
代碼量,減小人爲編碼時產生的可能疏漏(好比忘記釋放鏈接形成泄漏等). 試想一下,若是你是一個團隊leader
,團隊成員的水平頗有可能良莠不齊
,那麼如何保證項目開發中出錯機率最小
-------使用框架
, 用框架來簡化團隊成員的編碼量
和編碼難度
,讓他們傻瓜式
地寫代碼.
github地址:github.com/18598925736…
以上Demo,模擬的場景是:
服務端:開啓一個
登陸服務
,啓動服務以後,保存一個能夠登陸
的用戶名和密碼客戶端1:
RPC
調用登陸服務
,用戶名和密碼 和服務端的同樣
,能夠登陸成功客戶端2:
RPC
調用登陸服務
,用戶名和密碼 和服務端的不同
,登陸失敗
Demo
工程代碼結構圖注:客戶端和服務端必須同時依賴框架層module implementation project(":ipc")
(未完待續......)
關注我,還有更多技術乾貨分享~