动态图片演示,源码直析:手把手讲解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")

(未完待续......)

关注我,还有更多技术干货分享~

相关文章
相关标签/搜索