Android进程间通信方式
Posted 小图包
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android进程间通信方式相关的知识,希望对你有一定的参考价值。
目录
定义多进程
android应用中使用多进程只有一个办法(用NDK的fork来做除外),就是在AndroidManifest.xml中声明组件时,用android:process属性来指定。
不知定process属性,则默认运行在主进程中,主进程名字为包名。
android:process = package:remote,将运行在package:remote进程中,属于全局进程,其他具有相同shareUID与签名的APP可以跑在这个进程中。
android:process = :remote ,将运行在默认包名:remote进程中,而且是APP的私有进程,不允许其他APP的组件来访问。
多进程引发的问题
静态成员和单例失效:每个进程保持各自的静态成员和单例,相互独立。
线程同步机制失效:每个进程有自己的线程锁。
SharedPreferences可靠性下降:不支持并发写,会出现脏数据。
Application多次创建:不同进程跑在不同虚拟机,每个虚拟机启动会创建自己的Application,自定义Application时生命周期会混乱。
综上,不同进程拥有各自独立的虚拟机,Application,内存空间,由此引发一系列问题。
一 Intent
Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle 实现了 Parcelable 接口,可以在不同的进程间进行传输。
Bundle bundle = new Bundle();
bundle.putString("test", "来自A");
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
ComponentName cn = new ComponentName("com.test",
"com.test.MainActivity");//要启动的目标包名和包名+类名
intent.setComponent(cn);
intent.putExtras(bundle);
startActivity(intent);
Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678" );
startActivity(callIntent);
二 Content Provider
Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回),当然,也可以对其他应用程序的数据进行增、删、改操 作;
Content Provider返回的数据是二维表的形式
三 使用文件共享
- Windows 上,一个文件如果被加了排斥锁会导致其他线程无法对其进行访问,包括读和写;而 Android 系统基于 Linux ,使得其并发读取文件没有限制地进行,甚至允许两个线程同时对一个文件进行读写操作,尽管这样可能会出问题。
- 可以在一个进程中序列化一个对象到文件系统中,在另一个进程中反序列化恢复这个对象(注意:并不是同一个对象,只是内容相同。)。
- SharedPreferences 是个特例,系统对它的读 / 写有一定的缓存策略,即内存中会有一份 ShardPreferences 文件的缓存,系统对他的读 / 写就变得不可靠,当面对高并发的读写访问,SharedPreferences 有很多大的几率丢失数据。因此,IPC 不建议采用 SharedPreferences。
四 使用 Messenger
Messenger 是一种轻量级的 IPC 方案,它的底层实现是 AIDL ,可以在不同进程中传递 Message 对象
1 创建一个服务端的进程
public class TestMessengerService extends Service {
private static final String TAG = TestMessengerService.class.getSimpleName();
private class MessengerHandler extends Handler {
/**
* @param msg
*/
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 100000 :
Toast.makeText(TestMessengerService.this, "receive msg from client: msg = [" + msg.getData().getString("msg_key_client") + "]", Toast.LENGTH_SHORT).show();
Messenger client = msg.replyTo;
Message replyMsg = Message.obtain(null, 100001);
Bundle bundle = new Bundle();
bundle.putString("msg_key_service", "我已经收到你的消息,稍后回复你!");
replyMsg.setData(bundle);
try {
client.send(replyMsg);
} catch (Exception e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
private Messenger mMessenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
2 创建客户端进程
客户端进程:首先绑定服务端 Service ,绑定成功之后用服务端的 IBinder 对象创建一个 Messenger ,通过这个 Messenger 就可以向服务端发送消息了
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent mIntent = new Intent(this, TestMessengerService .class);
bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
Message msg = Message.obtain(null,100000);
Bundle data = new Bundle();
data.putString("msg_key_client", "Hello! This is client.");
msg.setData(data);
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
private Messenger mGetReplyMessenger = new Messenger(new MessageHandler());
private Messenger mService;
private class MessageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 100001:
Toast.makeText(MainActivity.this, "received msg form service: msg = [" + msg.getData().getString("msg_key_service") + "]", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
/**
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = new Messenger(service);
Message msg = Message.obtain(null,100000);
Bundle data = new Bundle();
data.putString("msg_key_client", "Hello! This is client.");
msg.setData(data);
//
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
}
五 AIDL
在Java层,想利用Binder进行夸进程的通信,那就得通过AIDL(Android 接口定义语言)了,AIDL是客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口,只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL.
1 创建aidl文件 并定义一个getString的方法
interface MyAidlService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
String getString ();
}
2 创建需要一个Service配合,所以我们在服务端还要声明一个Service
public class MyService extends Service {
class Mybind extends MyAidlService.Stub {
@Override
public String getString() throws RemoteException {
String string = "我是从服务器返回的";
return string;
}
}
Mybind mybind = new Mybind();
@Override
public IBinder onBind(Intent intent) {
return mybind;
}
@Override
public void onCreate() {
super.onCreate();
}
}
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
</service>
3 创建客户端
private MyAidlService myAidlService;
private ServiceConnection serviceConnection = new ServiceConnection() {//创建服务连接对象
@Override
public void onServiceConnected(ComponentName name, IBinder service) {//当成功绑定服务时回调该方法
myAidlService = MyAidlService.Stub.asInterface(service);//向下转型得到服务器端Mybind类的实例
try {
String str = myAidlService.getString();//调用实例的getString方法
Log.d("TestServiceConnection", str);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {//解除服务绑定时回调该方法
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_activity);
bindService();
}
private void bindService() {
Intent intent = new Intent();
intent.setClassName("服务端的包名", "服务端AIDL的Service的Class");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
六 Socket的方式
Socket也是实现进程间通信的一种方式,Socket也称为“套接字”(网络通信中概念),通过Socket也可以实现跨进程通信,Socaket主要还是应用在网络通信中。
1 服务端 我们创建一个Service做为服务端
<service
android:name="com.lvhj.ipc.SocketService"
android:enabled="true"
android:exported="true"
android:process=":remote" />
public class SocketService extends Service {
private static final String TAG = "SocketService";
private boolean isDestroyed = false;
private String[] messages = new String[]{
"测试1",
"测试2",
"测试3",
"测试4",
"测试5"
};
public SocketService() {
}
@Override
public void onCreate() {
super.onCreate();
new Thread(new TCPServer()).start();
}
@Override
public void onDestroy() {
isDestroyed = true;
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private class TCPServer implements Runnable {
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
e.printStackTrace();
}
while (!isDestroyed) {
try {
final Socket client = serverSocket.accept();
Log.d(TAG, "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);
Log.d(TAG, "欢迎来到聊天室!");
out.println("欢迎来到聊天室!");
while (!isDestroyed) {
String line = in.readLine();
Log.d(TAG, "message from Client: " + line);
if (line == null) break;
int i = new Random().nextInt(messages.length);
String message = messages[i];
out.println(message);
Log.d(TAG, "response to Client: " + message);
}
out.close();
in.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端建立连接后,新建 Socket 客户端,接收消息并作出响应。
2、客户端
客户端部分首先启动 Socket 服务,并且在连接失败后会不断重新尝试连接。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "TestSocket";
private PrintWriter mPrintWriter;
private Socket mClientSocket;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
Intent service = new Intent(this, SocketService.class);
startService(service);
new Thread() {
@Override
public void run() {
connectSocketServer();
}
}.start();
}
private void initView() {
button = findViewById(R.id.btn_send_msg);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
mPrintWriter.println("客户端发送的消息");
}
}).start();
}
});
}
private void connectSocketServer() {
Socket socket = null;
while (socket == null) {
try {
//选择和服务器相同的端口8688
socket = new Socket("localhost", 8688);
mClientSocket = socket;
mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
} catch (IOException e) {
SystemClock.sleep(1000);
}
}
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() {
Log.d("TestSocket", "n" + "服务端:" + msg);
}
}
);
}
}
mPrintWriter.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
}
打印日志如下
IPC 适用的场景及优缺点
Demo地址
以上是关于Android进程间通信方式的主要内容,如果未能解决你的问题,请参考以下文章