Messenger、Socket实现IPC

Messenger

Messenger是一种轻量级的IPC方案,其底层实现依旧是AIDL,不过Messenger只能处理较为简单但线程交流的情况。对于这一点,我们可以从它的构造方法也可以看出来。

1
2
3
public Messenger(IBinder target){
mTarget = IMessenger.Stub.asInterface(target);
}

服务端实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class AddLifeServiceMe extends Service {
public final static int REQUEST_FROM_CLIENT = 1;
public final static int ANSWER_FROM_SERVER = 2;
public static int liftLength = 0;
private static class MessageHandler extends Handler{
@Override
public void handleMessage(Message message){
switch (message.what){
case REQUEST_FROM_CLIENT:{
try {
liftLength++;
message.replyTo.send(Message.obtain(null, ANSWER_FROM_SERVER, liftLength, 0));
}catch (RemoteException re){
re.printStackTrace();
}
}
default:
super.handleMessage(message);
}
}
}
private final Messenger messenger = new Messenger(new MessageHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}

在服务端需要做的事情包括:

  1. 创建一个绑定了处理消息方法的Handler的Messenger;
  2. 通过调用messenger的getBinder方法,在onBind里面返回(这一点可以通过AIDL的使用方法看出来);
  3. 如果想要向客户端发送返回信息,只需要通过发来的message中的replyTo中的messengr发送信息即可。

客户端实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private final class MessageHandler extends Handler{
@Override
public void handleMessage(Message msg){
switch (msg.what){
case AddLifeServiceMe.ANSWER_FROM_SERVER:{
Log.d(TAG, Integer.toString(msg.arg1));
}
default:
super.handleMessage(msg);
}
}
}
getAnswer = new Messenger(new MessageHandler());
@OnClick(R.id.add_life_button)
void addLife(){
try {
Message message = Message.obtain(null, AddLifeServiceMe.REQUEST_FROM_CLIENT);
message.replyTo = getAnswer;
addLife.send(message);
}catch (RemoteException re){
re.printStackTrace();
}
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d(TAG, "Success!");
addLife = new Messenger(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d(TAG, "Fail!");
addLife = null;
}
};

在客户端中,我们要实现:

  1. 写另一个Messenger和Handler,以处理服务端返回的消息,并且把这个Messenger放到发送的message的replyTo之中,供服务端调用;
  2. 从onServiceConnected的参数iBinder以获得Messenger,然后通过这个Messenger发送信息到客户端中。

实现效果

效果如图

Socket

socket通常是在计算机网络中用作“大门”的一种“工具”,在这里我们通过它与自己本地IP的某个端口的TCP协议发送数据,以实现IPC的效果。

权限

虽然说我们并没有访问到网络中,但是由于还是在本地端口之间传来传去,还是需要申请权限之后才能够使用。

1
2
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Override
public void onCreate(){
super.onCreate();
new Thread(new Runnable() {
@Override
public void run() {
try {
serverSocket = new ServerSocket(30000);
Log.d(TAG, "Server ready!");
socket = serverSocket.accept();
Log.d(TAG, "Accept!");
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
while(true){
Log.d(TAG, reader.readLine());
writer.println(lifeLength++);
writer.flush();
if(lifeLength == 100){
writer.println("Enough!");
writer.flush();
break;
}
}
reader.close();
writer.close();
socket.close();
serverSocket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}).start();
}

由于socket是java自己的东西,和安卓的sdk没有任何关系,所以我们只需要在onCreate中写入相应方法即可,其中:

  1. Socket的缓冲区机制一下说不清楚……链接
  2. flush函数能够保证缓冲区内的东西都发送出去,但是这样的坏处还不清楚。。

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@OnClick(R.id.add_life_button)
void addLife(){
new Thread(new Runnable() {
@Override
public void run() {
try {
Socket socket = new Socket("localhost", 30000);
Log.d(TAG, "Socket ready!");
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
int i = 0;
while(!socket.isClosed()) {
writer.println("hello client!" + i++);
writer.flush();
Log.d(TAG, reader.readLine());
}
reader.close();
writer.close();
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}).start();
}

这段代码是显而易见的,和服务端基本上区别不是很大,只需要Socket发送给localhost就可以轻易解决问题了。(但是不知道为什么还是不能正常关闭socket。。)

实现效果

效果

总结

到这里,IPC的学习就暂时告一段落了,虽然还有无数的细节以及源码没有学习。但是这也是今后继续学习的方向和动力,希望以后这些总结能帮助我能更好的实现业务效果!

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2018 Alex's Blog All Rights Reserved.

Yifeng Tang hält Urheberrechtsansprüche.