Android Framework层和Native层通过LocalSocket实现通信

 Android Framework层和Native层通过LocalSocket实现通信

   前面篇章Android Framework层和Native层通过原生socket实现通信介绍了使用原生Socket实现Android Framework层和Native层之间的通信方式,这种方式虽然简单,但是效率并不是很高(但是也并不是一无是处,可以实现跨终端跨网络的通信)。所以Android的妈咪谷歌为我们带来了LocalSocket,比Java本身的socket效率要高,没有经过协议栈,是Android自己实现的类似共享内存一样的东东,在传输大量数据的时候就需要用到,比如Rild电话,,在创建应用进程和zygote通信,在应用安装过程中和installd通信等等就不一一枚举了。既然这是Android妈咪推荐使用的,并且在Android项目中也有多处用到的,那么我们有啥理由不来学习一把,撸一发呢。



实际效果演示

   在正式介绍前,先奉上最终效果演示图,正所谓眼见为实耳听为虚,不放个大招,各位读者咋有一鼓作气看下去的勇气呢。

(1) 服务端

root@xxx:/ # ps | grep LocalSocketServer
root      5034  1     3872   936   ffffffff b6ef3328 S /system/bin/LocalSocketServer
λ adb llogcat -s  LocalSocket
--------- beginning of main
--------- beginning of system
E/LocalSocket( 5034): android_get_control_socket success
E/LocalSocket( 5034): listen success
E/LocalSocket( 5034): Accept_fd 4
E/LocalSocket( 5034): Waiting for receive
E/LocalSocket( 5034): receive msg from client : hello  server
E/LocalSocket( 5034): send to client msg : hello  server
E/LocalSocket( 5034): Waiting for receive

(2) Android客户端

130|root@xxx:/ # ps | grep com.xxx.android2native
system    5079  345   981160 38088 ffffffff b6dccf58 S com.xxx.android2native
root@xxx:/ #

在这里插入图片描述
上面的图示为我们演示了Android应用进程PID号5079 和Native C++进程PID号5034 实现了通信。好了演示效果已经OK了,下面我们一步步的来讲解怎么实现这种通信方式。



配置init.rc

   配置init.rc脚本,在init.rc里面添加如下native service服务配置:

service local_server /system/bin/LocalSocketServer
    class main
    socket local_socket_server stream 600 system system

这里配置一个名为"local_server"的服务(注意这里的服务名字符串长度不能超过16,别问我为什么知道,你懂的,),终端在开机后通过解析init.rc然后启动并运行/system/bin目录下的脚本LocalSocketServer,同时这里还配置了一个名为 "local_socket_server " 的socket,并且只有拥有system权限的应用才允许连接这个socket。完成了init.rc的配置,然后就可以编译bootimage,将生成的boot.img烧入终端。



服务端实现

   有了前面的的铺垫,大家应该知道得开始Socket通信经典的C/S模式了,不过这里使用的是Android特别版LoclaSocket,在这里我们将C/C++端实现的Native程序作为服务端,Android端作为客户端。下面直接上服务端代码,如下:

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <android/log.h>
#include <jni.h>
#include <assert.h>
#include <cutils/sockets.h>
#include <utils/Log.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/un.h>


#define SOCKET_NAME "local_socket_server"
#define LOGE(TAG,...) if(1) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);

#define TAG "LocalSocket"


static void* recever_thread_exe(void *arg)
{
	int receive_fd = *(int *)arg;
	int numbytes ;
    char buff[1024] = {0};
	while(1){
		//循环等待Socket客户端发来消息
		//__android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI","Waiting for receive");
		LOGE(TAG,"Waiting for receive\n");
		if((numbytes = recv(receive_fd,buff,sizeof(buff),0))==-1){
			LOGE(TAG,"recv %d\n", errno);
			perror("recv");
			continue;
		}
		LOGE(TAG,"receive msg from client : %s\n", buff);
		//发送消息回执给Socket客户端
		if(send(receive_fd,buff,strlen(buff),0)==-1)
		{
			perror("send\n");
			LOGE(TAG,"send error\n");
			close(receive_fd);
			exit(0);
		}	
		LOGE(TAG,"send to client msg : %s\n", buff);
    }
    
    close(receive_fd);
	return ((void *)0);

}


int main()
{
	int max_connect_number = 6;
	int listen_fd = -1;
	int receive_fd = -1;
	int result;
	
	struct sockaddr addr;
	socklen_t alen;
	alen = sizeof(addr);

	//获取init.rc里面配置的名为local_socket_server的socket
	listen_fd = android_get_control_socket(SOCKET_NAME);
	if(listen_fd < 0)
	{
		LOGE(TAG,"Failed to get socket '" SOCKET_NAME "' errno:%d\n", errno); 
		exit(-1);
	}
	LOGE(TAG,"android_get_control_socket success\n");


	//开始监听local_socket_server,并设置最大监听数
	result = listen(listen_fd, max_connect_number);
	if(result < 0 )
	{
		perror("listen\n");
		LOGE(TAG,"listen error\n");
		exit(-1);
	}
	LOGE(TAG,"listen success\n");
	fcntl(listen_fd, F_SETFD, FD_CLOEXEC);
	for(;;)
	{
		//等待Socket客户端发启连接请求
		receive_fd= accept(listen_fd, &addr, &alen);
		LOGE(TAG,"Accept_fd %d\n",receive_fd);
		if (receive_fd < 0 ) {
			LOGE(TAG,"%d\n",errno);
			perror("accept error");
			exit(-1);
		}

		fcntl(receive_fd, F_SETFD, FD_CLOEXEC);
		pthread_t id;
		//通过pthread库创建线程
	    if(pthread_create(&id, NULL, recever_thread_exe, &receive_fd) )
	    {
	        LOGE(TAG,"rece thread create fail\n");
	    }
	}
	
	close(listen_fd);
	return 0;
}

有了具体的实现代码,还得有相关的编译脚本,编译脚本Android.mk的实现如下:

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS :=optional
LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
LOCAL_SHARED_LIBRARIES := libcutils liblog  libutils libicuuc
LOCAL_LDLIBS	:= -lm -llog 

LOCAL_MODULE:= LocalSocketServer
LOCAL_SRC_FILES:= LocalSocketServer.cpp
LOCAL_PRELINK_MODULE := false
include $(BUILD_EXECUTABLE)

编译成功以后,将LocalSocketServer打包进入system.img,或直接push到终端/system/bin里面也是ok的,重启终端然后查看该Native service是否成功运行,并且是否在 /dev/socket 下面生成了我们配置的socket节点。
在这里插入图片描述



Android客户端的实现

   在前面的章节里面,我们Linux C/C++服务端已经愉快的跑了起来,正在等待着客户端的连接,我们的Android客户端要来连接了,具体核心代码如下:

package com.xxx.android2native;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class LocalSocketClientManager {
    private final String SOCKET_NAME = "local_socket_server";
    private LocalSocket client;
    private LocalSocketAddress address;
    private boolean isConnected = false;
    private int connetTime = 1;
    private static final int HANDMESSAGE = 0;
    private static final String TAG = "LocalSocketClientManager";
    private ConnectListener mListener;

    private static LocalSocketClientManager mLocalSocketClientManager = null;

    public interface ConnectListener {
        void onReceiveData(String data);
    }

    public void setOnConnectListener(ConnectListener listener) {
        this.mListener = listener;
    }

    public static LocalSocketClientManager getInstance() {
        if (mLocalSocketClientManager == null) {
            synchronized (LocalSocketClientManager.class) {
                if (mLocalSocketClientManager == null) {
                    mLocalSocketClientManager = new LocalSocketClientManager();
                }
            }
        }
        return mLocalSocketClientManager;
    }

    public void connectLocalSocketServer() {
        client = new LocalSocket();
        address = new LocalSocketAddress(SOCKET_NAME,
                LocalSocketAddress.Namespace.RESERVED);
        new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                while (!isConnected && connetTime <= 10) {
                    try {
                        Thread.sleep(1000);
                        Log.i(TAG, "Try to connect socket;ConnectTime:"
                                + connetTime);
                        client.connect(address);
                        isConnected = true;
                        if (client.isConnected()) {
                            sendMesssage("Connect Server success\n");
                        }
                    } catch (Exception e) {
                        connetTime++;
                        isConnected = false;
                        Log.i(TAG, "Connect fail");
                    }
                }

            }
        }).start();
    }

    public void send(String data) {
        try {
            OutputStream outputStream = client.getOutputStream();
            outputStream.write(data.getBytes());

            InputStream inputStream;
            inputStream = client.getInputStream();
            if (inputStream == null) {
                Log.e("TAG", "inputStream error");
                return;
            }
            byte[] buffer = new byte[1024];
            int len = -1;
            while ((len = inputStream.read(buffer)) != -1) {
                String recedata = new String(buffer, 0, len);
                sendMesssage(recedata);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
            case HANDMESSAGE:
                if (mListener != null) {
                    mListener.onReceiveData(msg.getData().getString("data"));
                }
                break;
            }
        }
    };

    private void sendMesssage(String msg) {
        Message message = Message.obtain();
        message.what = HANDMESSAGE;
        Bundle bundle = new Bundle();
        bundle.putString("data", msg);
        message.setData(bundle);
        mHandler.sendMessage(message);
    }

    /**
     * 关闭Socket
     */
    public void disconnect() {
        try {
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}



结语

   如上就是使用LocalSocket实现Android和C/C++通信的全部过程,这种方式在Android中使用的非常普遍,譬如Rild,installd,进程启动通知zygote等等都是使用这种机制的。既然这是Android的妈咪谷所推荐的,那么就一定有它的优点,譬如高效,且通信双方可以进行全双工通信,这个要比Android通用的IPC通信Binder有了可取之处。当然这里并不是说BInder不好,术有专攻吗。所以具体选择那种通信方式得根据具体的业务逻辑,和使用场景来确定了。

猜你喜欢

转载自blog.csdn.net/tkwxty/article/details/103086192