動態圖片演示,源碼直析:手把手講解IPC框架(1)

做者:享學課堂終身VIP週週java

轉載請聲明出處!android

前言

手把手講解系列文章,是我寫給各位看官,也是寫給我本身的。文章可能過度詳細,可是這是爲了幫助到儘可能多的人,畢竟工做5,6年,不能老吸血,也到了回饋開源的時候. 這個系列的文章:git

一、用通俗易懂的講解方式,講解一門技術的實用價值github

二、詳細書寫源碼的追蹤,源碼截圖,繪製類的結構圖,儘可能詳細地解釋原理的探索過程小程序

三、提供Github 的 可運行的Demo工程,可是我所提供代碼,更可能是提供思路,拋磚引玉,請酌情cvbash

四、集合整理原理探索過程當中的一些坑,或者demo的運行過程當中的注意事項服務器

五、用gif圖,最直觀地展現demo運行效果微信

若是以爲細節太細,直接跳過看結論便可。本人能力有限,如若發現描述不當之處,歡迎留言批評指正。app

學到老活到老,路漫漫其修遠兮。與衆君共勉 !框架

正文大綱

1、 概念QA以及前置技能

2、 傳統方式IPC通訊寫法 與 使用IPC框架進行RPC通訊的對比

3、Demo展現

4、框架核心思想講解

5、 寫在最後的話

正文

1、 概念QA以及前置技能

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使用方法, android AIDL通訊機制, java註解,java反射,java 泛型

2、傳統方式IPC通訊寫法使用IPC框架進行RPC通訊 的對比

見github : github.com/18598925736… , 運行 aidl_clientaidl_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,團隊成員的水平頗有可能 良莠不齊,那麼如何保證項目開發中 出錯機率最小 ------- 使用框架, 用框架來簡化團隊成員的 編碼量編碼難度,讓他們 傻瓜式地寫代碼.

3、Demo展現

github地址github.com/18598925736…

以上Demo,模擬的場景是:

服務端:開啓一個 登陸服務,啓動服務以後,保存一個 能夠登陸的用戶名和密碼

客戶端1RPC調用 登陸服務,用戶名和密碼 和服務端的 同樣,能夠登陸成功

客戶端2RPC調用 登陸服務,用戶名和密碼 和服務端的 不同,登陸失敗

Demo工程代碼結構圖

客戶端和服務端必須同時依賴框架層module implementation project(":ipc")

(未完待續......)

關注我,還有更多技術乾貨分享~

相關文章
相關標籤/搜索