1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > android之手机截屏小程序

android之手机截屏小程序

时间:2024-02-22 08:21:21

相关推荐

android之手机截屏小程序

Android手机截屏程序

一:程序运行的大致思路

1、运行程序,显示一个Activity界面,点击按钮,显示一个浮窗。这里用到一个显示浮窗的小技术。

2、在显示浮窗的同时,会启动一个server服务,这个服务很重要,因为在这里会建立java端和android底层(即c语言端)的通信机制。这个地方比较抽象。以后再来解释,你就记住它是一个通信机制,相当于客户端和服务器端的关系

3、点击一下浮窗,就会向android底层发送消息,开始截屏,这里的向android底层发送消息,采用了android源代码里面的通信机制,我直接就把android源码拿来用了。

4、开始截屏,这里的截屏程序也是用的android自带的截屏程序,也是android源码,但是我在这里做了很多的工作。因为截屏出来的图片,我需要保存为png格式。

5、在保存png格式的图片的时候,我又使用了第三方的一个png库。

6、最后的运行效果相当于点击浮窗,开始截屏,再点击浮窗,停止截屏,图片会自动保存到/sdcard/DCIM/这个目录下面。

二:改程序在做的过程中使用到的一些技术

1、要用到android的应用程序开发的基本知识,这里就不多说了

2、用到了ndk开发技术

3、既然使用ndk开发,那C/C++的技术就不得不用了

4、因为做ndk开发,我觉的就相当于在Linux系统下面做c语言的开发,那Linux开发中使用的一些东西也就需要了。

5、最后一点,你的手机需要root。因为截屏的基本思路就读取屏幕像素在内存中的映射,所有需要直接读取内存中的内容,root是必须的。

三:程序开发的具体过程

1、从现在起,我会一步一步的把这个程序再重新做一遍,目的就是希望能把之前学习到的东西再回顾一遍,温故而知新。

2、先建立一个截屏项目工程,名字随便吧,我的叫screenshot如图

3、在Activity_main.xml文件中添加一个Button控件,如图

在MainActivity.java这个类里面做一些简单的初始化,为Button按钮添加点击响应事件,显示浮窗。因为显示浮窗,程序会直接跳到手机的桌面,所以这里有一个小技术,从应用程序直接跳到手机桌面,代码如下。

/**

*返回到主桌面类似按下Home按钮

*

*@paramcontext

*/

publicstaticvoidbackToHomePage(Contextcontext){

Intenti=newIntent(Intent.ACTION_MAIN);

i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

i.addCategory(Intent.CATEGORY_HOME);

context.startActivity(i);

}

4、要新建一个ScreenCaptureServer类,这个类就非常重要了,它主要有这几个作用,首先要创建一个浮窗,其次就是要建立与android底层的连接,然后要维护与android底层的通信。最后也是最重要的,建立截屏程序的独立线程。首先做一个浮窗,很简单,这里就不多说了,具体可以看我的另一篇博客-----android之浮窗篇。

5、实现android的java端与底层的c端通信。因为这里是调用的系统的通信代码,好多我也没有搞清楚。不过现在到是能用,在该程序里面,java端充当的是服务器端,它随service的启动开始运行,不停的监听来自客户端(c端的连接)。下面是服务器线程的具体代码。

/**

*服务器端的线程

*

*@authorjeck

*

*/

privateclassServerSocketThreadextendsThread{

/**

*线程运行的标示

*/

privatebooleankeeprunning=true;

/**

*本地服务socket

*/

privateLocalServerSocketlocalServerSocket;

@Override

publicvoidrun(){

try{

//创建一个本地socket

localServerSocket=newLocalServerSocket("screen_shot_socket");

}catch(Exceptione){

e.printStackTrace();

keeprunning=false;

}

//通过while循环,轮训从客户端发过来的连接请求

while(keeprunning){

Log.d(TAG,"Waittingforclienttoconnect!!");

try{

//监听客户端的连接

LocalSocketinteractClientSocket=localServerSocket

.accept();

//因为有可能在等待客户端连接的时候,accept阻塞了。Activity被finish掉,所以有必要在检测一次

if(keeprunning){

Log.d(TAG,"nowclientcoming!!!");

//开始为客户端服务

newInteractClientSocketThread(interactClientSocket)

.start();

}

}catch(Exceptione){

e.printStackTrace();

keeprunning=false;

}

}

//如果断开连接,那就关闭服务

if(localServerSocket!=null){

try{

localServerSocket.close();

}catch(Exceptione){

e.printStackTrace();

}

}

}

///**

//*停止线程

//*/

//publicvoidstopRunning(){

//

//keeprunning=false;

//}

}

6、接下来的是service里面的第三个功能,就是维护与客户端(c端)的通信了。这里无非就是监听连接,连接到了就发送消息。因为要配合我的主要功能,截屏,所有我这里的逻辑是启动service,开启服务器监听,如果有客户端连接,会向客户端发送一个数字2,如果客户端收到2,会向服务器端发送2222表示连接成功,然后如果你点击了我们的小浮窗,会向客户端发送数字1,表示开始截屏,客户端收到消息会想服务器端发送1111表示我客户端已经收到消息了。然后你再点击一次小浮窗,服务器端会发送0,表示停止截屏,那客户端收到0会停止截屏,并向服务器端反馈0000.这就是我的通信机制。下面我把代码粘出来。

/**

*服务器与客户端直接的通信线程

*

*@authorjeck

*

*/

privateclassInteractClientSocketThreadextendsThread{

/**

*本地socket

*/

privateLocalSocketinteractClientSocket;

/**

*输入流

*/

privateInputStreaminputStream=null;

/**

*输出流

*/

privateOutputStreamoutputStream=null;

/**

*从客户端发来的消息

*/

privateStringBuilderreceiveFromClientString=newStringBuilder();

/**

*输入缓存区

*/

privatechar[]readBuffer=newchar[4096];

/**

*输入字节数

*/

privateintreadBytes=-1;

/**

*构造函数

*/

publicInteractClientSocketThread(LocalSocketinteractClientSocket){

this.interactClientSocket=interactClientSocket;

}

/**

*从客户端读取数据

*

*@return

*/

privatebooleanreadDataFromClient(){

booleanreadResult=false;

//从本地连接中获取输入流

try{

inputStream=interactClientSocket.getInputStream();

//读数据

InputStreamReaderinputStreamReader=newInputStreamReader(

inputStream);

//从输入流中读取数据

while((readBytes=inputStreamReader.read(readBuffer))!=-1){

StringtmpStr=newString(readBuffer,0,readBytes);

receiveFromClientString.append(tmpStr).append("\n");

}

if(receiveFromClientString.toString()!=null){

if(receiveFromClientString.toString().startsWith("0000")

||receiveFromClientString.toString().startsWith(

"1111")){

captureState=2;

}

//显示client发送的消息

Log.d(TAG,receiveFromClientString.toString());

//读取时间成功

readResult=true;

}

}catch(IOExceptione){

e.printStackTrace();

}

returnreadResult;

}

/**

*向客户端写数据

*

*@return

*/

privatebooleanwriteDataToClient(StringwriteContent){

booleanwriteResult=false;

try{

outputStream=interactClientSocket.getOutputStream();

//如果点击了开始录屏,则发送消息

if(writeContent!=null&&!"".equals(writeContent)){

outputStream.write(writeContent.getBytes());

}

writeResult=true;

}catch(IOExceptione){

e.printStackTrace();

writeResult=false;

}

returnwriteResult;

}

@Override

publicvoidrun(){

try{

switch(captureState){

case0:

//停止截屏

writeDataToClient(STOP_CAPTURE_SCREEN);

break;

case1:

//开始录屏

writeDataToClient(START_CAPTURE_SCREEN);

break;

case2:

//等待连接

writeDataToClient(KEEP_CONNECTION);

break;

}

readDataFromClient();

}catch(Exceptione){

e.printStackTrace();

Log.d(TAG,"receivedatafailed!!");

}finally{

if(outputStream!=null){

try{

outputStream.close();

}catch(Exceptione){

e.printStackTrace();

}

}

if(inputStream!=null){

try{

inputStream.close();

}catch(Exceptione){

e.printStackTrace();

}

}

}

}

}

7、接下来是最关键的部分,呵呵,其实那个地方都很关键,缺少了任何一个地方,这个程序也跑不起来。废话不多说了,现在简绍截屏进程了。这进程的工作原理是这样的。1需要手机root权限,在获取手机root权限之后,通过ndk的混合编译器,编译一个exe文件,在root权限下面,通过代码执行这个exe程序。换句话说就是相当于在java端,执行exe文件。因为android系统是基于Linux操作系统的,所以你的exe想要直接执行,必须获取一点的权限,就算你在Linux系统下面直接写代码,那你执行./xxxx程序的时候,也是需要权限的。2就是要创建一个进程了(process)。你执行了exe程序,就相当于你创建了一个进程,所以要把这个进程获取出来,以便操作。最后这些东西都是在一个单独的线程中运行的,来代码。

/**

*响应点击截屏按钮

*/

privatebooleanscreenCapture(){

booleanresult=false;

try{

//创建log对象

screenLog=newStringBuilder();

//创建一个进程

logcatProcess=RuntimeHelper.getLogcatProcess(this);

//创建一个缓冲

bufferedReader=newBufferedReader(newInputStreamReader(

logcatProcess.getInputStream()),8192);

Stringline;

while((line=bufferedReader.readLine())!=null){

Log.d(TAG,line);

screenLog.append(line).append("\n");

if(line.startsWith("Success")){

result=true;

}

}

}catch(Exceptione){

e.printStackTrace();

result=false;

}

returnresult;

}

/**

*使用异步线程执行截屏操作

*

*@authorjeck

*

*/

privateclassScreenCaptureTaskextendsAsyncTask<Void,Void,Boolean>{

@Override

protectedBooleandoInBackground(Void...params){

returnscreenCapture();

}

@Override

protectedvoidonPostExecute(Booleanresult){

if(result){

//将日志保存在SD卡上

//try{

////Utils.saveCaptureLog(screenLog.toString());

//}catch(Exceptione){

//e.printStackTrace();

//}

Toast.makeText(getApplicationContext(),"截屏成功",

Toast.LENGTH_LONG).show();

}

}

}

8、以上就是我们这个截屏程序的java端的所有代码了,这只是一个好的开始。

在java端的功能就是显示一个浮窗,然后点击浮窗,会开始和C端进行交互。

C端涉及的东西就都是C语言的了,这里面都是ndk的知识了,这里首先要编写一个.cpp文件,就是我们的通信程序,因为这里用的是android源代码,所以这个程序目前只适合android4.1系统的。其他版本暂时没有测试。下面这是我的exe程序的主要代码。

#include<stdio.h>

#include<sys/system_properties.h>

#include<dlfcn.h>

#include<android/log.h>

#include<sys/socket.h>

#include<cutils/sockets.h>

#include<sys/un.h>

#include<unistd.h>

#include<stddef.h>

#include<pthread.h>

#include<string.h>

#include"screen_capture_image.h"

#defineTAG"--screen_capture-->"

intsocketID;

/*

*获取当前的系统版本

*/

intgetCurrentSDKVersion(){

intsdk;

charc[PROP_VALUE_MAX];

if(__system_property_get("ro.build.version.sdk",c)>0){

sscanf(c,"%d",&sdk);

}else{

sdk=8;

}

returnsdk;

}

/*

*Class:com_rdtd_jni_SendMessageFromClientJNI

*Method:startHeartBead

*Signature:()I

*/

intconnection_to_server(){

charpath[]="screen_shot_socket";

socketID=socket_local_client(path,ANDROID_SOCKET_NAMESPACE_ABSTRACT,

SOCK_STREAM);

//如果连接失败

if(socketID<0){

returnsocketID;

}else{

return1;

}

}

voidclose_connection(){

if(socketID){

close(socketID);

}

}

intread_data_from_server(){

//0表示停止录屏,1表示开始录屏,2表示连接中,-1表示读取数据出错

intresult;

//读取的字数

intread_result;

charreadBuffer[2];

memset(readBuffer,0,2);

read_result=read(socketID,readBuffer,2);

if(read_result){

result=atoi(readBuffer);

//printf("---->readdatasuccess:%d\n",result);

}else{

result=-1;

}

returnresult;

}

intwrite_data_to_server(constchar*str){

intwrite_result;

write_result=write(socketID,str,strlen(str));

if(write_result){

close_connection();

return1;

}else{

printf("writedatafailed!\n");

close_connection();

return0;

}

}

void*begin_capture(void*){

longresult=1;

//设置截屏开始的标示

set_capture_flag_png(1);

//截屏并保存成png图片,现在在联想的机器上是不行的。一直报的是找不到libpng。so文件

screen_capture_png();

return(void*)result;

}

/*

*截屏程序的入口,截屏参数都放在这里面。

*argv[1]表示宽度,argv[2]表示高度

*默认帧率15帧每秒,宽度和高度是手机屏幕的宽高

*/

intmain(intargc,char*argv[]){

intcount=0;

pthread_tthread_id;

void*thread_result;

//循环读写数据

while(connection_to_server()){

//先读取数据

intread_result=read_data_from_server();

printf("---->receivemessage=%d\n",read_result);

__android_log_print(ANDROID_LOG_DEBUG,TAG,"receivemessage=%d",

read_result);

switch(read_result){

case0:

//停止录制

write_data_to_server("0000---------->");

set_capture_flag_png(0);

if(pthread_join(thread_id,&thread_result)==-1){

printf("waitingthreadfailed!\n");

}else{

if((long)thread_result==0){

printf("screen_capreturnfailed\n");

}else{

printf("Success------------------>\n");

//这个地方必须返回,否则保存的图片都是黑屏图片,不知道是为什么,呵呵,应该是没有关闭文件,只有函数返回了,系统自动关闭文件

exit(0);

}

}

break;

case1:

//开始录制

write_data_to_server("1111---------->");

if(pthread_create(&thread_id,NULL,begin_capture,NULL)==-1){

printf("createthreadfailed!\n");

}

break;

case2:

//连接中

write_data_to_server("2222---------->");

break;

case-1:

//读取数据出错

printf("---->readdatafailed!\n");

gotoexit;

}

sleep(3);

}

exit:close_connection();

return0;

}

这个cpp文件就会被ndk编译成我们在上面提到的exe文件,就是Linux系统下面的可执行文件。只有这个文件,我们能做的就是和java端发个信息而已,还是不能截屏的,需要截屏的程序在下面,也是用的android源代码,不过基本让我改的没有源代码的味道了。

在这里,我使用了第三方的png库,我把它编译成静态库,然后链接到我的cpp文件中,最后把cpp文件编译成exe文件,再在代码中执行exe文件。下面是截屏的主要代码。

/*

*Copyright(C)TheAndroidOpenSourceProject

*

*LicensedundertheApacheLicense,Version2.0(the"License");

*youmaynotusethisfileexceptincompliancewiththeLicense.

*YoumayobtainacopyoftheLicenseat

*

*/licenses/LICENSE-2.0

*

*Unlessrequiredbyapplicablelaworagreedtoinwriting,software

*distributedundertheLicenseisdistributedonan"ASIS"BASIS,

*WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied.

*SeetheLicenseforthespecificlanguagegoverningpermissionsand

*limitationsundertheLicense.

*/

#include<errno.h>

#include<unistd.h>

#include<stdio.h>

#include<fcntl.h>

#include<ui/PixelFormat.h>

#include<zlib.h>

#include<libpng/png.h>

#include<time.h>

#include<stdlib.h>

#include<malloc.h>

#include<pthread.h>

#include<linux/fb.h>

#include<sys/ioctl.h>

#include<sys/mman.h>

#include<gui/SurfaceComposerClient.h>

#include"screen_capture_image.h"

#include<android/log.h>

//#ifdefANDROID_KK//4.4

//#include<binder/ProcessState.h>

//#include<gui/ISurfaceComposer.h>

//#else

#include<binder/IMemory.h>

//#endif

usingnamespaceandroid;

#defineTAG"--screen_capture-->"

//截屏标识

staticintcapture_flag=0;

//录音标识

staticintrecord_flag=0;

intPushTime=0;

//获取当前时间(microsecond)

int64_tgetCurrentTime(){

structtimevaltv;

gettimeofday(&tv,NULL);

return(1000000LL*tv.tv_sec)+tv.tv_usec;

}

//#ifdefANDROID_KK

//staticuint32_tDEFAULT_DISPLAY_ID=ISurfaceComposer::eDisplayIdMain;

//#endif

//显示错误信息

voiderror(constchar*msg){

fprintf(stderr,"%s:%s:\n",msg,strerror(errno));

exit(1);

}

typedefstructparam{

void*fb_in;

FILE*fb_out;

intwidth;

intheight;

int64_tusedTime;

size_tbuffer_size;

param*next;

}image_info;

//获取当前日期,以秒为单位,现在我一秒之内可以截取3张图片,他的名字当然一样了

char*getLocalTime(){

charcurrentTime[128];

memset(currentTime,0,128);

int64_ttime=getCurrentTime();

sprintf(currentTime,"%lld",time);

returncurrentTime;

}

//创建视频路径

voidcreate_video_path(char*path){

memset(path,0,256);

strcpy(path,"/sdcard/DCIM/Record_");

strcat(path,getLocalTime());

strcat(path,".mp4");

printf("---->path=%s\n",path);

}

//创建视频路径

voidcreate_image_path(char*path){

memset(path,0,256);

strcpy(path,"/sdcard/DCIM/capture_");

strcat(path,getLocalTime());

strcat(path,".png");

printf("---->path=%s\n",path);

}

//创建截屏图片信息的节点

image_info*create_image_info_node(constvoid*in,FILE*out,intw,inth,

size_tsize,int64_tut){

image_info*newInfo=(image_info*)malloc(sizeof(image_info));

//分配内存

newInfo->fb_in=malloc(size);

//拷贝内存

memcpy(newInfo->fb_in,in,size);

newInfo->fb_out=out;

newInfo->buffer_size=size;

newInfo->width=w;

newInfo->height=h;

newInfo->usedTime=ut;

newInfo->next=NULL;

printf("---->createanewimagenode\n");

returnnewInfo;

}

//释放内存

voidrelease_image_node(image_info*node){

if(node!=NULL){

free(node->fb_in);

node->fb_in=NULL;

free(node);

node=NULL;

printf("---->successtoreleaseaimagenode\n");

}

}

/*

*fb_base屏幕左上角的第一个像素的内存地址

*fb_out生成的png保存的地址

*w屏幕的宽度

*h屏幕的高度

*fpng的文件格式

*/

//voidtake_screenshot(char*fb_base,FILE*fb_out,intw,inth,intf){

inttake_screenshot(image_info*argu){

longresult=0;

//printf("take_screenshotisrunning!\n");

//image_info*argu=(image_info*)param;

//png结构

png_structppng;

//pnginfo

png_infopinfo;

structfb_var_screeninfovinfo;

//因为是一行一行的扫描屏幕,r是行数

unsignedintr;

//每一行的长度,是右屏幕的宽度(像素)*每一个像素的大小(32位4个字节)

unsignedintrowlen;

//每一个像素所占的大小,4个字节

unsignedintbytespp=4;

//创建一个png结构

png=png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);

//printf("1--->png_create_write_structisruned\n");

if(png==NULL){

ALOGE("failedpng_create_write_struct\n");

}

png_init_io(png,argu->fb_out);

//printf("2--->png_init_ioisruned\n");

info=png_create_info_struct(png);

//printf("3--->png_create_info_structisruned\n");

if(info==NULL){

ALOGE("failedpng_create_info_struct\n");

png_destroy_write_struct(&png,NULL);

}

if(setjmp(png_jmpbuf(png))){

ALOGE("failedpngsetjmp\n");

png_destroy_write_struct(&png,NULL);

}

//设置png的各种信息

png_set_IHDR(png,info,argu->width,argu->height,8,

PNG_COLOR_TYPE_RGB_ALPHA,PNG_INTERLACE_NONE,

PNG_COMPRESSION_TYPE_BASE,PNG_FILTER_TYPE_BASE);

//printf("4--->png_set_IHDRisruned\n");

png_write_info(png,info);

//printf("5--->png_write_infoisruned\n");

//计算每一行的长度

rowlen=argu->width*bytespp;

//新建一个临时变量,保存图片的内存块地址

png_byteptemp=(png_bytep)argu->fb_in;

//循环扫描屏幕,一行一行的读取数据

for(r=0;r<argu->height;r++){

//将第r行写到png结构中

png_write_row(png,temp);

//计算下一行的起始位置

temp+=rowlen;

}

//写png的信息

png_write_end(png,info);

//printf("6--->png_write_endisruned\n");

png_destroy_write_struct(&png,NULL);

//printf("7--->png_destroy_write_structisruned\n");

//保存完图片,就把文件关闭掉

if(fclose(argu->fb_out)==-1){

error("closefilefailed!");

}else{

//printf("---->successtosaveaimage\n");

temp=NULL;

result=1;

}

returnresult;

}

//保存图片,返回保存成功的图片数量,如果不等于24,则说明保存失败,成功返回1,失败,返回0

void*save_image(void*start){

longresult=0;

image_info*p=(image_info*)start;

image_info*next=NULL;

while(p!=NULL){

//先将下一个节点的指针保存在next里面

next=p->next;

//直接保存,如果成功,则释放该节点所占的内存

if(take_screenshot(p)==1){

release_image_node(p);

}else{

error("saveimagefailed");

}

//最后再将next节点复制给p,继续操作

p=next;

}

result=1;

return(void*)result;

}

/*截屏程序*/

image_info*screen_shot(){

staticint64_tbase_time=getCurrentTime();

//截屏的开始时间和结束时间

int64_tbeginTime=0,usedTime=0;

constvoid*base;

size_tsize;

uint32_twidth,height;

FILE*fb_out=NULL;

//#ifdefANDROID_KK

//ProcessState::self()->startThreadPool();

//#endif

//

//#ifdefANDROID_KK

//int32_tdisplayId=DEFAULT_DISPLAY_ID;

//#endif

ScreenshotClientscreenshot;

beginTime=getCurrentTime();

//#ifdefANDROID_KK

//sp<IBinder>display=SurfaceComposerClient::getBuiltInDisplay(displayId);

//if(display!=NULL&&screenshot.update(display)==NO_ERROR){

//#else

if(screenshot.update()==NO_ERROR){

//#endif

base=screenshot.getPixels();

size=screenshot.getSize();

width=screenshot.getWidth();

height=screenshot.getHeight();

//计算截图时间

usedTime=getCurrentTime()-beginTime;

printf("---->Image:width=%d,height=%d,usedtime=%lld\n",

screenshot.getWidth(),screenshot.getHeight(),usedTime);

charimage_path[256];

create_image_path(image_path);

fb_out=fopen(image_path,"w");

returncreate_image_info_node(base,fb_out,width,height,size,

usedTime);

}

returnNULL;

}

//截屏并生成视频链表

void*capture_and_link(void*){

image_info*start=NULL;

image_info*newNode=NULL;

image_info*tail=NULL;

//开始截屏并生成截屏链表

while(capture_flag){

//保存视频流

newNode=screen_shot();

if(newNode!=NULL){

if(start==NULL){

start=newNode;

}

if(tail!=NULL){

tail->next=newNode;

}

tail=newNode;

}else{

printf("---->screen_shotreturnNULL!\n");

returnNULL;

}

//睡3秒,不然录制的太快

sleep(3);

}

return(void*)start;

}

#ifdef__cplusplus

extern"C"{

#endif

//设置截屏标志

voidset_capture_flag_png(intflag){

capture_flag=flag;

record_flag=flag;

}

//截屏并保存为图片

voidscreen_capture_png(){

//截屏线程,只负责截屏

pthread_tcapture_thread;

if(pthread_create(&capture_thread,NULL,capture_and_link,NULL)==-1){

error("createcapture_threadfailed!");

}

//截屏之后返回的结果,是一个包含截屏图片信息的链表,这个链表保存的东西关系到整个截屏程序的成败

void*capture_result;

if(pthread_join(capture_thread,&capture_result)==-1){

error("waitingcapture_threadfailed!");

}

//截屏完了,开始保存图片

pthread_tsave_thread;

if(pthread_create(&save_thread,NULL,save_image,capture_result)==-1){

error("createsave_threadfailed!");

}

//等待保存图片的线程

void*save_result;

if(pthread_join(save_thread,&save_result)==-1){

error("waitingsave_threadfailed!");

}

if((long)save_result==1){

printf("---->successtosaveimage!\n");

}

}

#ifdef__cplusplus

}

#endif

到此为止,这个截屏程序的主要代码是都将完了,虽然说将的很简单,但是做起来一点都不简单,里面涉及的东西还是很多的。这需要你对ndk编程很熟悉才可能把这个程序顺利的运行起来

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。