1. 程式人生 > >Android應用與framework的socket通訊例項

Android應用與framework的socket通訊例項

關於Android應用與Framework的socket通訊,相信關心這個問題的朋友們已經看過《android使用socket使底層和framework通訊》這篇文章,美中不足的是作者只貼出一些關鍵的程式碼片段而並沒有放出原始碼。我這裡還是以一個能實際執行的例子為基礎來講,這樣也方便大家學習。

首先看一下效果,如下圖。我填寫姓名"Potter",選擇性別"Mr"然後點擊發送,底層socket收到訊息後將訊息直接返回給我,我將返回的結果(Mr.Potter)直接顯示在Result。


ok,有了初步瞭解後我們現在來看一下如何一步步實現這個例子。

1、配置init.rc。我在init.rc加入如下配置

service htfskservice /system/bin/htfsk
socket htfsk stream 666 system system
oneshot

這裡配置了一個名為 “htfskservice” 的服務,手機開機後該服務會自啟動並執行/system/bin目錄下的指令碼htfsk(步驟二將提到如何生成這個指令碼)。

同時這裡還配置了一個名為 "htfsk" 的socket,並且只有擁有system許可權的應用才允許連線這個socket,

2、編寫socket服務端程式碼,生成可執行指令碼htfsk。

首先來看下socket服務端程式碼 htfsk.c ,內容如下:

#define SOCKET_NAME "htfsk"

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/un.h>
#include <cutils/sockets.h>
#include <utils/Log.h>
#include <android/log.h>


int main(){
    char log[200]; 

    int connect_number = 6;
    int fdListen = -1, new_fd = -1;
    int ret;
    struct sockaddr_un peeraddr;
    socklen_t socklen = sizeof (peeraddr);
    int numbytes ;
    char buff[256];
    //這一步很關鍵,就是獲取init.rc中配置的名為 "htfsk" 的socket
    fdListen = android_get_control_socket(SOCKET_NAME);
    if (fdListen < 0) {
	sprintf(log,"Failed to get socket '" SOCKET_NAME "' errno:%d", errno);
	__android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log); 
	exit(-1);
    }
    //開始監聽
    ret = listen(fdListen, connect_number);    
    
    sprintf(log,"Listen result %d",ret);
    __android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log); 
    
    if (ret < 0) {
        perror("listen");
        exit(-1);
    }
    //等待Socket客戶端發啟連線請求
    new_fd = accept(fdListen, (struct sockaddr *) &peeraddr, &socklen);
    sprintf(log,"Accept_fd %d",new_fd);
    __android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log); 
    if (new_fd < 0 ) {
        sprintf(log,"%d",errno);
        __android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log); 
        perror("accept error");
        exit(-1);
    }
	
    while(1){
	//迴圈等待Socket客戶端發來訊息
	__android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI","Waiting for receive");
	if((numbytes = recv(new_fd,buff,sizeof(buff),0))==-1){
        sprintf(log,"%d",errno);
        __android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log); 
		perror("recv");
		continue;
	}
	//傳送訊息回執給Socket客戶端
	if(send(new_fd,buff,strlen(buff),0)==-1)
	{
		perror("send");
		close(new_fd);
		exit(0);
	}		
    }
    
    close(new_fd);
    close(fdListen);
    return 0;
}

寫好服務端程式碼後我們要將他編譯成可執行指令碼htfsk,編譯的Android.mk內容如下:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS :=optional
LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
LOCAL_SHARED_LIBRARIES := libcutils liblog
LOCAL_MODULE:= htfsk
LOCAL_SRC_FILES:=htfsk.c
LOCAL_PRELINK_MODULE := false
include $(BUILD_EXECUTABLE)

編譯成功後就會在/system/bin/目錄下找到生成的可執行指令碼htfsk

3、編寫客戶端java程式碼。核心程式碼如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.util.Log;

/**
 * Socket客戶端
 * 
 * @author lai_zs
 * @date:2012-3-17 下午12:15:09
 */
public class SocketClient {
	private final String SOCKET_NAME = "htfsk";
	private LocalSocket client;
	private LocalSocketAddress address;
	private boolean isConnected = false;
	private int connetTime = 1;

	public SocketClient() {
		client = new LocalSocket();
		address = new LocalSocketAddress(SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED);
		new ConnectSocketThread().start();
	}

	/**
	 * 傳送訊息
	 * @param msg
	 * @return 返回Socket服務端的訊息回執
	 */
	public String sendMsg(String msg) {
		if (!isConnected) {
			return "Connect fail";
		}
		try {
			BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
			PrintWriter out = new PrintWriter(client.getOutputStream());
			out.println(msg);
			out.flush();
			return in.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "Nothing return";
	}

	/**
	 * 非同步連線Socket,如果連線不上會嘗試重複連線十次
	 * 
	 * @author Administrator
	 * 
	 */
	private class ConnectSocketThread extends Thread {
		@Override
		public void run() {
			while (!isConnected && connetTime <= 10) {
				try {
					sleep(1000);
					Log.i("SocketClient","Try to connect socket;ConnectTime:"+connetTime);
					client.connect(address);
					isConnected = true;
				} catch (Exception e) {
					connetTime++;
					isConnected = false;
					Log.i("SocketClient","Connect fail");
				}
			}
		}
	}

	/**
	 * 關閉Socket
	 */
	public void closeSocket() {
		try {
			client.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

工程MySocket使用步驟:

1、配置init.rc。

2、將工程MySocket拷貝到android原始碼的/packages/apps目錄下編譯就可以了。

編譯成功後就可以刷機了,手機開機啟動後可以看到 /dev/socket 目錄下服務端Socket已經成功啟動了,如下圖:


再來試一下,微笑