一、概述
IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。那么什么是进程,什么是线程,进程和线程是两个截然不同的概念。在操作系统中,线程是CPU调度的最小单元,同时线程是一种有限的系统资源。而进程指的一个执行单元,在PC和移动设备上指的是一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含被包含的关系,最简单情况下,一个进程可以只有一个线程,即主线程,在Android里面也叫UI线程,在UI线程里才能操作界面元素。
AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来生成用于IPC的代码。从某种意义上说AIDL其实是一个模板,因为在使用过程中,实际起作用的并不是AIDL文件,而是据此而生成的一个IInterface的实例代码,AIDL其实是为了避免我们重复编写代码而出现的一个模板
设计AIDL这门语言的目的就是为了实现进程间通信。在Android系统中,每个进程都运行在一块独立的内存中,在其中完成自己的各项活动,与其他进程都分隔开来。可是有时候我们又有应用间进行互动的需求,比较传递数据或者任务委托等,AIDL就是为了满足这种需求而诞生的。通过AIDL,可以在一个进程中获取另一个进程的数据和调用其暴露出来的方法,从而满足进程间通信的需求
通常,暴露方法给其他应用进行调用的应用称为服务端,调用其他应用的方法的应用称为客户端,客户端通过绑定服务端的Service来进行交互
二、Demo功能和介绍
体验功能请安装demo目录下apk文件,一个为Server端,一个是client端。实现client从server端获取用户信息、重置用户信息和修改用户信息的功能。
三、服务器端实现
1、新建User.java实体类(java目录下com.ljd.aidl.entity.User):
/**
* 用户实体类
*/
public class User implements Parcelable {
private String uid;
private String uname;
protected User(Parcel in) {
uid = in.readString();
uname = in.readString();
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(uid);
dest.writeString(uname);
}
/**
* 自己定义的readFromParcel方法, 这样才支持 定向tag out
*
* @param dest
*/
public void readFromParcel(Parcel dest) {
//注意,此处的读值顺序应当是和writeToParcel()方法中一致的
uid = dest.readString();
uname = dest.readString();
}
@Override
public String toString() {
return String.format("[uid:%s, uname:%s]", uid, uname);
}
}
2、生成User.aidl文件(aidl目录下com.ljd.aidl.entity)
package com.ljd.aidl.entity;
//aidl的user声明java中的user对象
parcelable User;
3、生成IUserManager.aidl文件(aidl目录下com.ljd.aidl)
package com.ljd.aidl;
//导入所需要使用的非默认支持数据类型的包, 和java类似
import com.ljd.aidl.entity.User;
//接口定义语言, 当然要定义接口啦, 和java接口类似
interface IUserManager {
//所有的返回值前都不需要加任何东西,不管是什么数据类型
User getUser();
//传参时除了Java基本类型以及String,CharSequence之外的类型
//都需要在前面加上定向tag,具体加什么量需而定
void setUser(in User user);
//修改用户的姓名
void setUserName(String name);
}
4、生成UserService.java文件(java目录下)
package com.ljd.aidl.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import com.ljd.aidl.IUserManager;
import com.ljd.aidl.entity.User;
public class UserService extends Service {
private User mUser;
/**
* 根据AIDL文件 编译器为我们生成的BookManager
*/
private final IUserManager.Stub mUserManager = new IUserManager.Stub() {
@Override
public User getUser() throws RemoteException {
return mUser;
}
@Override
public void setUser(User user) throws RemoteException {
mUser = user;
}
@Override
public void setUserName(String name) throws RemoteException {
if (mUser != null) {
mUser.setUname(name);
}
}
};
@Override
public void onCreate() {
super.onCreate();
User user = new User();
user.setUid("10000");
user.setUname("测试10000");
try {
if (mUserManager != null) {
mUserManager.setUser(user);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mUserManager;
}
}
四、客户端实现
1、拷贝aidl路径下的所有aidl文件到client目录下(保持包名不变)。
2、拷贝java路径下的用到Entity实体类到client目录下(保持包名不变)。
3、编写调用代码:
package com.ljd.aidl.activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.ljd.aidl.IUserManager;
import com.ljd.aidl.client.R;
import com.ljd.aidl.entity.Book;
import com.ljd.aidl.entity.User;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class Demo5Activity extends AppCompatActivity {
private final String TAG = "DEMO5";
private boolean mIsBindService;
private IUserManager mIUserManager;
private TextView TvContent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo5);
TvContent = (TextView) findViewById(R.id.TvContent);
ButterKnife.bind(this);
}
@Override
protected void onDestroy() {
unbindService();
ButterKnife.unbind(this);
super.onDestroy();
}
@OnClick({R.id.bind_demo4_btn, R.id.unbind_demo4_btn, R.id.get_btn, R.id.set_btn,R.id.update_btn})
public void onClickButton(View v) {
switch (v.getId()) {
case R.id.bind_demo4_btn:
bindService();
TvContent.setText("已绑定");
break;
case R.id.unbind_demo4_btn:
Toast.makeText(this, "unbind service success", Toast.LENGTH_SHORT).show();
unbindService();
TvContent.setText("未绑定");
break;
case R.id.get_btn:
getData();
break;
case R.id.set_btn:
User suser = new User();
suser.setUname("uname from client");
suser.setUid("20000");
try {
mIUserManager.setUser(suser);
TvContent.setText("设置数据成功:"+suser.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.update_btn:
try {
mIUserManager.setUserName("is update name");
TvContent.setText("修改用名称为:is update name");
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
private void getData() {
runOnUiThread(new Runnable() {
@Override
public void run() {
User user = null;
try {
user = mIUserManager.getUser();
} catch (RemoteException e) {
e.printStackTrace();
}
TvContent.setText(user.toString());
}
});
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "bind success");
Toast.makeText(Demo5Activity.this, "bind service success", Toast.LENGTH_SHORT).show();
mIUserManager = IUserManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
//重新绑定Service防止系统将服务进程杀死而产生的调用错误。
bindService();
}
};
private void bindService() {
Intent intent = new Intent();
intent.setAction("com.ljd.aidl.action.USER_SERVICE");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
mIsBindService = true;
}
private void unbindService() {
if (mIsBindService) {
mIsBindService = false;
unbindService(mConnection);
}
}
}
五、优化方案以及封装成sdk
通过上面的编码可以实现不同的apk的进程间通信问题,但是使用中还需要有统一的标准,否则就会导致重复的工作和一些未知的Bug。我建议的优化方案是,由Server端封装代码,把两端都封装成lib库,当有别的apk需要用的时候,直接调用lib里面的方法,这样就可以很好的维护代码和节省人力成本。
网友评论