Android NDK开发初步探索

2019.12 在了解了Android权限的原理和运行机制后,突然想起之前写过的这篇博文,只能说,当时自己的想法,真是 too young too naive。

#Android NDK开发初步探索

在了解Anroid安全、权限与Linux进程的安全、权限之间的关联关系的时候突发奇想:是否使用socket连接来作为http客户端,以之请求网络就不需要在AndroidManifest.xml中申请Internet访问权限。为验证该想,遂作如下试验:

  1. 试验结果:NDK下无论是调用本地方法进行socket连接,还是调用本地方法读写SD卡上的文件,均需要在AndroidManifest.xml中作相应的权限请求配置;
  2. 试验环境:AndroidStudio2.2.3 + android-ndk-r13b + Gradle2.14.1
  3. 比对环境:Ubuntu14(win10虚拟机)+ codeblock13.12

####测试步骤:

  1. 在codeblock中编写可执行的socket实现的http客户端文件httpGet.cpp文件;
  2. 在AndroidStudio中新建NDK项目:勾选include C++ Support,使用frtti和fexceptions;
  3. 将httpGet.cpp代码移植到Android项目的cpp文件中,建立本地方法;
  4. 对比,分别测试在AndroidManifext.xml文件中配置Internet访问权限和不配置的运行结果。

####核心代码:
1.httpGet.cpp

#include<stdio.h>  
#include<unistd.h>  
#include<sys/types.h>  
#include<sys/socket.h>  
#include<sys/stat.h>  
#include<fcntl.h>  
#include<string.h>  
#include<arpa/inet.h>  
#include<netdb.h>  
#include<stdlib.h>  
#include<netinet/in.h>  

#define BUFSIZE 0xF000
void geturl(char* url)
{
    int cfd;
    struct sockaddr_in cadd;
    struct hostent* pURL = NULL;
    char myurl[BUFSIZE];
    char* pHost = 0;
    char host[BUFSIZE], GET[BUFSIZE];
    char request[BUFSIZE];
    static char text[BUFSIZE];
    int i, j;

    //分离主机中的主机地址和相对路径
    memset(myurl, 0, BUFSIZE);
    memset(host, 0, BUFSIZE);
    strcpy(myurl, url);
    for(pHost = myurl; *pHost != '/' &&  *pHost != '\0'; ++pHost);

    //获取相对路径保存到GET中
    memset(GET, 0, BUFSIZE);
    if((int)(pHost - myurl) == strlen(myurl))
    {
        strcpy(GET, "/");//即url中没有给出相对路径,需要自己手动的在url尾部加上/
    }
    else
    {
        	trcpy(GET, pHost); //地址段pHost到strlen(myurl)保存的是相对路径
    }

    //将主机信息保存到host中
    //此处将它置0, 即它只想的内容里面已经分离出了相对路径,剩下的为host信息(从myurl到pHost地址段存放的是HOST)
    *pHost = '\0';
    strcpy(host, myurl);

    //设置socket参数
    if(-1 == (cfd = socket(AF_INET, SOCK_STREAM, 0)))
    {
        printf("create socket failed of client!\n");
	    exit(-1);
    }
    printf("create socket success of client!\n");

    pURL = gethostbyname(host); //讲上面获得的主机信息通过域名解析函数获得域名信息
	
    //设置IP地址结构
    bzero(&cadd, sizeof(struct sockaddr_in));
    cadd.sin_family = AF_INET;
    cadd.sin_addr.s_addr = *((unsigned long*)pURL->h_addr_list[0]);
    cadd.sin_port = htons(80);

    //向WEB服务器发送URL信息
    memset(request, 0, BUFSIZE);
    strcat(request, "GET ");
    strcat(request, GET);
    strcat(request," HTTP/1.1\r\n"); //设置http请求行的信息

    strcat(request, "Accept: text/html, image/jpeg, image/png, vedio/x-mng, text/*, image/*, */*\r\n");
    strcat(request, "Accept-Language: zh-cn\r\n");
    strcat(request, "Accept-Encoding: x-gzip, x-deflate, gzip, deflat\r\n");
    strcat(request, "Accpet-charset: utf-8, utf-8; q=0.5, *; q=0.5\r\n");
    strcat(request, "HOST: ");
    strcat(request, host);
    strcat(request, "\r\n");
    //strcat(request, "Content-Type: text/html\r\n");
    //strcat(request, "Content-Length: 1024\r\n");
    strcat(request, "Connection: Keep-Alive\r\n");
    strcat(request, "Cache-Control: no-cache\r\n\r\n");//设置http请求头

    //链接服务器
    int cc;
    if(-1 == (cc = connect(cfd, (struct sockaddr*)&cadd, (socklen_t)sizeof(cadd))))
    {
    	   printf("connect failed of client!\n");
    	   exit(1);
    }
    printf("connect success!\n");

    //向服务器发送url请求的request、
    int cs;
    if(-1 == (cs = send(cfd, request, strlen(request), 0)))
    {
    	   printf("send request failed!\n");
    	   exit(1);
    }
    printf("send reqeust success, and send length = %d\n", cs);

    //将http请求信息写入文件hPage
    int chfd;
    if(-1 == (chfd = open("hPage", O_RDWR|O_CREAT|O_TRUNC, 0766)))
    {
    	   printf("open or create hPage failed!\n");
    	   exit(1);
    }
    printf("open or create hPage success!\n");

    int chf;
    if(-1 == (chf = write(chfd, request, strlen(request))))
    {
    	   printf("write to hPage failed!\n");
    	   exit(1);
    }
    else if(chf == 0)
    {
    	   printf("the length of written to hPage is 0 byte!\n");
    }
    else
    {
    	   printf("the length of written to hPage is %d bytes!\n", chf);
    }

    //客户端接受服务器的返回信息
    memset(text, 0, BUFSIZE);
    int cr;
    if(-1 == (cr = recv(cfd, text, BUFSIZE, 0)))
    {
    	   printf("receive failed!\n");
    	   exit(1);
    }
    else
    {
    	   printf("receive success!\n");
    	   int cofd;
    	   for(;;)
    	   {
    	       //创建一个文件mPage用于保存网页的主要信息
    	       if(-1 == (cofd = open("mPage.html", O_RDWR|O_CREAT|O_TRUNC, 0766)))
    	       {
    	           perror("open or create mPage failed!\n");
    	           exit(1);
    	       }

    	       //将网页的主要内容text写入mPage中
    	        nt cf;
    	       if(-1 == (cf = write(cofd, text, strlen(text))))
    	       {
    	           perror("write to mPage failed!\n");
    	           break;
    	        }
    	        else if(cf == 0)
    	        {
    	            printf("the length of written to mPage is 0 byte!\n");
    	            break;
    	        }
    	        else
    	        {
    	            printf("the length of written to mPage is %d bytes", cf);
    	            break;
    	        }
	
    	        //将text清空,方便接受其他网页信息
    	        memset(text, 0, BUFSIZE);
	
    	        //关闭文件描述符
    	        //close(chfd);
    	        close(cofd);
    	    }
    	}

    	//free(BUFSIZE);
    	close(cfd);
	}	

2.native-lib.cpp(AndroidStudio jni)

#include"httpGet.cpp"  
#define OK  1  
#define ERROR  0  
...
extern "C"
jint
Java_com_xxx_ndk_MainActivity_urlFromJNI(JNIEnv* env, jobject thiz){
	char address[] = "www.baidu.com";
	char* pAdd = address;
	return geturl(pAdd);
}	

说明:linux下的httpGet.cpp移植到AndroidStudio下的native-lib.cpp文件需要作一些调整:1.需要修改open()方法里文件的创建位置(/sdcard/xxx.xx); 2.为方便起见,本地方法调用返回值为int类型,对于exit()方法,需要替换为return OK或者return ERROR。


编译.so文件
1.编译环境:Ubuntu14 + gcc;
2.编译步骤:

  1. 首先, 编译mylib.c:
    $gcc -c -fPIC -o mylib.o mylib.c
    -c表示只编译(compile),而不是链接。-o选项用于说明输出(output)文件名。gcc将生成一个目标(object)文件mylib.o。注意-fPIC选项。PIC指Position Independent Code。共享库要求有此选项,以便实现动态链接(dynamic linking)。
  2. 生成共享库:
    $gcc -shared -o mylib.so mylib.o
    库文件以lib开始。共享库文件以.so为后缀。-shared表示生成一个共享库。注意,由于该文件并不包含main()函数,因此,生词.so文件必须分两步,先编译,后链接。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值