IPC全称为Inter-Process Communication,含义为进程间通信,指的是两个进程之间进行数据交换的过程。
方式一:Bundle实现 用于android四大组件直接的进程间通信
应用一的Activity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.one_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent();
Bundle bundle=new Bundle();
bundle.putString("sendMessage","嘿!我正在给你发消息!");
ComponentName componentName=new ComponentName("com.example.ipcdemoapplication","com.example.ipcdemoapplication.MainActivity");
intent.setComponent(componentName);
intent.putExtras(bundle);
startActivity(intent);
}
});
}
}
应用二代码
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView =(TextView)findViewById(R.id.text);
Bundle bundle=getIntent().getExtras();
if(bundle!=null){
textView.setText(bundle.getString("sendMessage"));
}
}
}
点击应用一的按钮之后,跳转到应用二并输出“嘿!我正在给你发消息!”
方式二:使用文件共享
比如本地目录下有一个文件,都可以通过这个文件来进行数据分享,具体操作(https://www.jianshu.com/p/55eae30d133c)
还有一种方式是通过SharedPreferences 通过共享xml来实现本地文件共享,但是多进程还是有一些问题的,具体参照(https://www.jianshu.com/p/4984f66f9a4b)
方式三:使用Messenger
Messenger可以在不同进程中传递Message对象,在Message中加入我们想要传递的数据就可以在进程间进行数据传递了。Messenger是一种轻量级的IPC方案并对AIDL进行了封装,它实现起来比较容易,首先我们先写服务器(MessengerServce.java),在onBind方法中创建Messenger,关联接受消息的Handler调用getBinder来获取Binder对象,在handleMessage方法中接受客户端发来的信息。
public class MessengerService extends Service {
private static final String TAG = "WANZIKAIFA";
public static final int MSG_FORMCLIENT = 1000;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case MSG_FORMCLIENT:
Log.e(TAG,"收到客户端信息----"+msg.getData().get("msg"));
//得到客户端传来的Messenger对象
Messenger mMessenger = msg.replyTo;
Message message = Message.obtain(null,MessengerService.MSG_FORMCLIENT);
Bundle mBundle = new Bundle();
mBundle.putString("rep","这里是服务端,我们收到信息了");
message.setData(mBundle);
try {
mMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new Messenger(mHandler).getBinder();
}
}
注册服务时要另开启一个进程
<service android:name=".MessengerService" android:process=":remoute"/>
接下来创建客户端(MainActivity.java),绑定另一个进程服务,绑定成功后根据服务端返回的Binder对象创建Messenger,并用Messenger向服务端发送信息。
public class MainActivity extends AppCompatActivity {
private Messenger mMessenger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.one_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("WANZIKAIFA", "点击按钮 ");
Intent intent = new Intent(MainActivity.this,MessengerService.class);
bindService(intent,mServiceConnon, Context.BIND_AUTO_CREATE);
}
});
}
private ServiceConnection mServiceConnon = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d("WANZIKAIFA", "这里是客户端,服务端收到了吗 ");
mMessenger = new Messenger(iBinder);
Message mMessage = Message.obtain(null,MessengerService.MSG_FORMCLIENT);
Bundle mBundle = new Bundle();
mBundle.putString("msg","这里是客户端,服务端收到了吗");
mMessage.setData(mBundle);
//将Messenger传递给服务端
mMessage.replyTo = new Messenger(mHandler);
try {
mMessenger.send(mMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private Handler mHandler= new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case MessengerService.MSG_FORMCLIENT:
Log.d("WANZIKAIFA", "收到服务端信息: "+msg.getData().get("rep"));
break;
}
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnon);
}
}
在handleMessage回调中收到客户端信息时,调用Message.replyTo得到客户端传递过来的Messenger对象,创建消息并通过Messenger发送给客户端。然后客户端需要创建一个Handler来接受服务端的信息。当绑定完服务之后需要关联定义的Handler。
打印的log
01-01 03:02:51.601 12156 12156 D WANZIKAIFA: 点击按钮
01-01 03:02:52.011 12156 12156 D WANZIKAIFA: 这里是客户端,服务端收到了吗
01-01 03:02:52.012 12565 12565 E WANZIKAIFA: 收到客户端信息----这里是客户端,服务端收到了吗
01-01 03:02:52.013 12156 12156 D WANZIKAIFA: 收到服务端信息: 这里是服务端,我们收到信息了
方式四:通过AIDL来进行IPC通信
之前发过的一篇AIDL通信(https://blog.csdn.net/qq_27647919/article/details/107204389)
方式五:ContentProvider方式 实现对另一个应用进程开放provider数据的查询
第一步创建数据库并创建表名为“game_provider.db” 里面有两个字段name,describe
public class DBOpenHelper extends SQLiteOpenHelper {
public static final String DB_NAME = "game_provider.db";
public static final String GAME_TABLE_NAME = "game";
public static final int DB_VERSION = 1;
public String CREATE_GAME_TABLE = "create table if not exists " + GAME_TABLE_NAME
+"(_id integer primary key ,"+"name TEXT ,"+"describe TEXT)";
public DBOpenHelper(@Nullable Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(CREATE_GAME_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
第二步对数据库存入一条数据
public class GameProvider extends ContentProvider {
public static final String AUTHORITY = "com.example.myapplication.GameProvider";
public static final Uri GAME_CONTENT_URL = Uri.parse("content://"+AUTHORITY+"/game");
private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private SQLiteDatabase mDB;
private Context mContext;
private String table;
static {
mUriMatcher.addURI(AUTHORITY,"game",0);
}
@Override
public boolean onCreate() {
table = DBOpenHelper.GAME_TABLE_NAME;
mContext = getContext();
initProvider();
return false;
}
private void initProvider() {
Log.d("WANZIKAIFA", "insert into game: ");
mDB = new DBOpenHelper(mContext).getWritableDatabase();
new Thread(new Runnable() {
@Override
public void run() {
mDB.execSQL("delete from "+DBOpenHelper.GAME_TABLE_NAME);
mDB.execSQL("insert into game values(1,'插入第一条','我是第一条');");
}
}).start();
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
String table = DBOpenHelper.GAME_TABLE_NAME;
Cursor mCursor = mDB.query(table,
projection,
selection,
selectionArgs,
null,
sortOrder,
null);
return mCursor;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
mDB.insert(table,null,contentValues);
mContext.getContentResolver().notifyChange(uri,null);
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
return 0;
}
}
为了测试是跨进程通信,将provdier写入另一个进程
<provider
android:authorities="com.example.myapplication.GameProvider"
android:name=".GameProvider"
android:process=":provider"/>
在Activity中插入另一条数据
public class ContentProviderActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contentprovier_activity);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("content://com.example.myapplication.GameProvider");
ContentValues mContentValues = new ContentValues();
mContentValues.put("_id",2);
mContentValues.put("name","插入第二条");
mContentValues.put("describe","我是第二条");
getContentResolver().insert(uri,mContentValues);
Cursor gameCursor = getContentResolver().query(uri,new String[]{"name","describe"},null,null,null);
while (gameCursor.moveToNext()){
Game mGame = new Game(gameCursor.getString(0),gameCursor.getString(1));
Log.d("WANZIKAIFA",mGame.gameName+"---"+mGame.gameDescribe);
}
}
});
}
}
game.java
public class Game implements Parcelable {
public String gameName;
public String gameDescribe;
public Game(String gameName,String gameDescribe){
this.gameName = gameName;
this.gameDescribe = gameDescribe;
}
protected Game(Parcel in) {
gameName = in.readString();
gameDescribe=in.readString();
}
public static final Creator<Game> CREATOR = new Creator<Game>() {
@Override
public Game createFromParcel(Parcel in) {
return new Game(in);
}
@Override
public Game[] newArray(int size) {
return new Game[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(gameName);
parcel.writeString(gameDescribe);
}
}
点击按钮之后,输出log为
01-01 00:10:46.764 8779 8779 D WANZIKAIFA: insert into game:
01-01 00:10:46.842 8736 8736 D WANZIKAIFA: 插入第一条---我是第一条
01-01 00:10:46.842 8736 8736 D WANZIKAIFA: 插入第二条---我是第二条
由log可以看出进程号pid8779 pid8736 第二条跨进程插入成功
方式六用Socket实现跨进程聊天程序
Socket是位于应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口,供应用层调用以实现进程在网络中通信。Socket分为流式套接字和数据包套接字,分别对应网络传输控制层的TCP协议和UDP协议。TCP协议是一种面向连接的,可靠的,基于字节流的传输层通信协议,它使用三次握手协议建立连接,并且提供了超时重传机制,具有很高的稳定性。UDP协议则是一种无连接的协议,且不对传送数据包进行可靠性保证,它适合一次传输少量数据,UDP传输的可靠性是由应用层负责。在网络质量令人十分不满意的环境下,UDP协议数据包丢失会比较严重。但是由于UDP协议的特性:他不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频,视频和普通数据在传送时使用UDP协议比较多。
第一步添加权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
第二步实现服务端
实际流程是在线程中创建TCP服务,监听8688端口,等待客户端连接,当客户端连接时就会生成Socket。通过每次创建的Socket就可以和不同的客户端通信了。当客户端断开连接时,服务端也会跟着关闭Socket并结束通话线程。服务端首先会向客户端发送一条消息:“您好,我是服务端”,并接受客户端发来的消息,将受到的消息进行加工再返回给客户端
public class SocketServerService extends Service {
private boolean isServiceDestoryed = false;
@Override
public void onCreate() {
new Thread(new TcpServer()).start();
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private class TcpServer implements Runnable {
@Override
public void run() {
ServerSocket serverSocket = null;
try {
//监听8688窗口
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
e.printStackTrace();
}
while (!isServiceDestoryed){
try {
//接受客户端请求,并且阻塞直接接受消息
final Socket client = serverSocket.accept();
new Thread(){
@Override
public void run() {
responseClient(client);
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void responseClient(Socket client) {
//用户接受客户端消息
try {
BufferedReader in = new BufferedReader((new InputStreamReader(client.getInputStream())));
//用于向客户端发送消息
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())),true);
out.println("您好呀!我是服务端!");
while (!isServiceDestoryed){
String str = in.readLine();
Log.d("WANZIKAIFA", "收到客户端的信息: "+str);
if (TextUtils.isEmpty(str)){
//客户端断开了连接
Log.d("WANZIKAIFA","客户端断开连接");
break;
}
String message = "收到了客户端的信息为"+ str;
//从客户端收到的消息加工再发给客户端
out.println(message);
}
out.close();
in.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onDestroy() {
isServiceDestoryed = true;
super.onDestroy();
}
}
第三步实现客户端
流程:客户端会启动服务,并开启线程连接服务端Socket。
public class SocketClientActivity extends Activity {
private EditText et_receiver;
private Button bt_send;
private TextView tv_message;
private PrintWriter mPrintWriter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_socket);
initView();
Intent intent = new Intent(this,SocketServerService.class);
startService(intent);
new Thread(){
@Override
public void run() {
connectSocketServer();
}
}.start();
}
private void initView() {
et_receiver = (EditText)findViewById(R.id.et_receiver);
bt_send =(Button)findViewById(R.id.bt_send);
tv_message = (TextView)findViewById(R.id.tv_message);
bt_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final String msg = et_receiver.getText().toString();
//向服务端发送信息
if (!TextUtils.isEmpty(msg)&&null!=mPrintWriter){
new Thread(new Runnable() {
@Override
public void run() {
mPrintWriter.println(msg);
}
}).start();
tv_message.setText(tv_message.getText()+"\n"+"客户端:"+msg);
Log.d("WANZIKAIFA", tv_message.getText()+"\n"+"客户端:"+msg);
et_receiver.setText("");
}
}
});
}
private void connectSocketServer() {
Socket socket = null;
while (socket==null){
//选择和服务器相同的端口8688
try {
socket = new Socket("localhost",8688);
mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
} catch (IOException e) {
e.printStackTrace();
}
}
//接受服务器端的消息
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!isFinishing()){
final String msg = br.readLine();
if (msg!=null){
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_message.setText(tv_message.getText()+"\n"+"服务端:"+msg);
Log.d("WANZIKAIFA", tv_message.getText()+"\n"+"服务端:"+msg);
}
});
}
}
mPrintWriter.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
打印出log为
01-01 02:08:54.459 9771 9771 D WANZIKAIFA: 服务端:您好呀!我是服务端!
01-01 02:09:28.140 9790 9814 D WANZIKAIFA: 收到客户端的信息: 收到
01-01 02:09:28.142 9771 9771 D WANZIKAIFA: 客户端:收到
01-01 02:09:28.190 9771 9771 D WANZIKAIFA: 服务端:收到了客户端的信息为收到
方式七广播
比如开机启动广播
public class BootUpReceiver extends BroadcastReceiver {
private static final String cTag = "BootUpReceiver";
@Override
public void onReceive( final Context context, final Intent intent ) {
LogTool.d(cTag, " BootUpReceiver");
}
}
注册清单:
<receiver android:name=".BootUpReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</receiver>
最后针对这几种IPC通信方式分析一下优缺点:总结转自(https://www.jianshu.com/p/71480c680a65)
1.bundle :
简单易用 但是只能传输Bundle支持的对象 常用于四大组件间进程间通信
2.文件共享:
简单易用 但不适合在高并发的情况下 并且读取文件需要时间 不能即时通信 常用于并发程度不高 并且实时性要求不高的情况
3.AIDL :
功能强大 支持一对多并发通信 支持即时通信 但是使用起来比其他的复杂 需要处理好多线程的同步问题 常用于一对多通信 且有RPC 需求的场合(服务端和客户端通信)
4.Messenger :
功能一般 支持一对多串行通信 支持实时通信 但是不能很好处理高并发情况 只能传输Bundle支持的类型 常用于低并发的无RPC需求一对多的场合
5.ContentProvider :
在数据源访问方面功能强大 支持一对多并发操作 可扩展call方法 可以理解为约束版的AIDL 提供CRUD操作和自定义函数 常用于一对多的数据共享场合
6.Socket :
功能强大 可以通过网络传输字节流 支持一对多并发操作 但是实现起来比较麻烦 不支持直接的RPC 常用于网络数据交换
总结
当仅仅是跨进程的四大组件间的传递数据时 使用Bundle就可以 简单方便
当要共享一个应用程序的内部数据的时候 使用ContentProvider实现比较方便
当并发程度不高 也就是偶尔访问一次那种 进程间通信 用Messenger就可以
当设计网络数据的共享时 使用socket
当需求比较复杂 高并发 并且还要求实时通信 而且有RPC需求时 就得使用AIDL了
文件共享的方法用于一些缓存共享 之类的功能