做者:享学课堂终身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")
(未完待续......)
关注我,还有更多技术干货分享~