Android Studio之NDK开发

一、 前言
NDK全称是Native Development Kit,是Android提供的一个开发工具包,能够快速将开发的C,C++的动态库,协议软件包,以及优秀的软件工具,用so和应用打包成APK,自由地在Android上运行。而NDK开发工具,就是将C/C++程序,编译成为Android环境可运行的程序,再者,通过NDK实现Android中实现JNI编程。那么JNI又是啥呢?JNI的全称是Java Native Interface,即Java的本地接口,JNI可以实现Java与C/C++语言进行交互,实现JAVA与C/C++之间的贯通,实现JAVA层的穿透,实现JAVA不法完成的一些底层的功能。这么一来,通过NDK和JNI结合,就可以很方便的在Android的开发环境中使用C/C++技术互通,方便地使用C/C++所提供的开源库,实现更加广泛的应用功能。
由此,我们不难看出,NDK开发工具在Android应用中有两个使用点,一是对C/C++开源库的编译,获得在Android环境下可运行的SO文件或Lib库;二是JNI程序的编译,完美实现JAVA与C/C++程序的结合。一般情况下我们使用了Android与C/C++的结合,必定需要JNI技术,也就必定需要使用NDK开发工具。所以,我们应掌握NDK编译,掌握Android之NDK开发。

二、 NDK安装
NDK开发工具的安装,可分C/C++开源库等编译环境的NDK开发工具的安装和Android Studio编程环境的安装。前者是基于OS环境交叉编译,制作在Android环境下可运行的SO文件或Lib文件,后者是编译JNI设计技术的C/C++文件,也可以编译Android Studio环境编写的Android下运行的C/C++工程,两个安装方法是不同的,我们在这里分别进行描述。

2.1、Ubuntu环境下安装NDK开发工具

2.1.1、下载NDK安装包
进入官网下载:android-ndk-r16d-linux-x86_64.bin
我们应当注意的问题,在Android NDK版本17以后,不再提供编译功能,我们下载的版本需满足我们能的需求。

2.1.2、安装

#cd /usr/local/
#chmod +x android-ndk-r16d-linux-x86_64.bin
#./android-ndk-r16d-linux-x86_64.bin

2.1.3、环境配置

#cd /root
#vim .bashrc

在文件的最后行添加代码:

export NDK_HOME=/usr/local/android-ndk-16r/
export PATH=$PATH:$NDK_HOME

环境配置完成后,运行下面命令,表示配置环境生效。

#source .bashrc

2.2、Android Studio环境安装NDK开发工具

2.2.1、安装
运行Android studio程序,进入主界面,在主界面工具条上选择SDK manager,点击进入。

选择SDK tool

选择NDK和CMake两项工具安装,点击“ok”按钮,将自动安装NDK。

2.2.2、项目配置NDK编译环境
点击程序主界面菜单的“File”项,选择“project structure…”,进入编译环境配置。

在“NDK version”栏选择安装的版本,点击”OK“按钮,即编译化境配置完成。再次提醒注意的问题,版本17以后的版本,不在提供编译功能,我们需要下载版本17或以前的版本。

三、 Ubuntu环境下NDK编译
在这里需要说明的一点,在linux环境下编译,搭建环境比较简单,如果在windows环境下,往往需要安装cygwin,既费资源,有难找到三方提供(可能不支持cygwin),不如直接在linux环境下操作。
Linux环境下实现编译,主要是NDK运行环境配置和解决configure的配参问题。

3.1、NDK运行环境配置

export NDK=/home/ndk_build/android-ndk-r14b
export SYSROOT=$NDK/platforms/android-9/arch-arm/
export TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
export CPU=arm
export PREFIX=$(pwd)/android/$CPU
export ADDI_CFLAGS="-marm"

3.2、configure配参

--target-os=linux
--arch=arm
CC=$NDK_HOME/bin/ arm-linux-androideabi-gcc
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi-

3.3、具体的实例
编辑android_build.sh文件

#!/bin/bash
make clean
export NDK=/home/ndk_build/android-ndk-r14b
export SYSROOT=$NDK/platforms/android-9/arch-arm/
export TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
export CPU=arm
export PREFIX=$(pwd)/android/$CPU
export ADDI_CFLAGS="-marm"
./configure --target-os=linux \
--prefix=$PREFIX --arch=arm \
--disable-doc \
--enable-shared \
--disable-static \
--disable-yasm \
--disable-symver \
--enable-gpl \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-symver \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $ADDI_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make
make install

只要注意了NDK配置和configure配参问题,解决了NDK编译的主要问题。

四、 CMakelists.txt编译
CMakelists.txt编译的讨论,主要是为了在Android Studio环境下解决编译问题。
我们知道makefile是在Linux编译c或者c++代码的时候的一种脚本文件,但是每一个功能都要写一个makefile文件,这样如果这个工程很大,而且相关性比较强的话,makefile的书写就会变得相对繁琐,更要命的是如果以后需要添加新的功能或者是新人需要修改功能的话,看起来就会特别麻烦;因为介于此,cmake的出现就是为了解决这样的问题,cmake的入门相当容易,而且管理也特别方便简单,那我们开始吧。
cmake的所有语句都写在一个CMakeLists.txt的文件中,CMakeLists.txt文件确定后,直接使用cmake命令进行运行,但是这个命令要指向CMakeLists.txt所在的目录,cmake之后就会产生我们想要的makefile文件,然后再直接make就可以编译出我们需要的结果了。更简单的解释就是cmake是为了生成makefile而存在,这样我们就不需要再去写makefile了,只需要写简单的CMakeLists.txt即可。
cmake的执行流程很简单,我们的重点是如何编写CMakeLists.txt文件呢,我们通过例子来学习cmake的语法。
例子从这篇文章中学习http://blog.csdn.net/dbzhang800/article/details/6314073,大致如下:

4.1、一个单文件的简单的例子
文件名字为main.c 内容如下:

#include <stdio.h>
int main()
{printf("Hello World Test!\n");return 0;
}

编写CMakeLists.txt文件内容如下:

project(hello_jelly)
set(APP_SRC main.c)
add_executable(${PROJECT_NAME} main.c)
#print message
message(${PROJECT_SOURCE_DIR})

解释代码:
第一个行project不是强制性的,最好加上,这会引入两个变量:
HELLO_BINARY_DIR, HELLO_SOURCE_DIR
同时也会定义两个等价的变量:
PROJECT_BINARY_DIR, PROJECT_SOURCE_DIR
外部编译要时刻区分这两个变量对应的目录
可以通过message进行输出
message(${PROJECT_SOURCE_DIR})
set 命令用来设置变量
add_exectuable 告诉工程生成一个可执行文件。
add_library 则告诉生成一个库文件。
CMakeList.txt 文件中,命令名字是不区分大小写的,而参数和变量是大小写相关的。
然后将以上两个文件放在统一目录下面,注意编译产生时候分为两种,一种是直接在当前源码目录执行cmake命令#cmake ./,但是这样会在当前目录下产生很多临时文件和目录,另一种方式就是在当前目录新建一个build目录,然后我门进入到build目录,执行命令cmake …/,这样产生的所有临时文件都会生成在build目录下,而不影响源码目录的代码,此处我们采用第二种方法。我们进入到build目录,执行命令#cmake …/,然后在当前目录可以看到文件如下

drwxrwxr-x 3 zqq zqq 4096 9月  28 17:12 CMakeFiles
-rw-rw-r-- 1 zqq zqq  993 9月  28 17:12 cmake_install.cmake
-rw-rw-r-- 1 zqq zqq 5479 9月  28 17:12 Makefile

最后再在此目录执行make即可生成相应的可执行程序。

4.2、多个源文件的操作
hello.h头文件内容如下

#ifndef JELLYHELLO
#define JELLYHELLO
void hello(const char* name);
#endif

hello.c文件内容

#include <stdio.h>
#include "hello.h"
void hello(const char* name)
{printf("Hello my name is %s\n",name);
}

main.c文件内容如下

#include <stdio.h>
#include "hello.h"
int main()
{printf("Hello World Test!\n");hello("jelly");return 0;
}

然后是编写CMakeLists.txt文件

project(hello_jelly)
set(APP_SRC main.c hello.c)
add_executable(${PROJECT_NAME} ${SRC_LIST})
#print message
message(${PROJECT_SOURCE_DIR})

然后保存使用上面的方法进行cmake和make,就可以生成需要的可执行文件。

4.3、将hello.c生成一个库来调用
如果将hello生成成一个库来调用的话只需要在2的基础上修改一下CMakeLists.txt文件再进行编译即可
修改的CMakeLists.txt如下:

project(hello_jelly)
set(LIB_SRC hello.c)
set(APP_SRC main.c)
add_library(hello ${LIB_SRC})
add_executable(${PROJECT_NAME} ${APP_SRC})
target_link_libraries(${PROJECT_NAME} hello)
#print message
message(${PROJECT_NAME})

相比之下,我们只是添加了一个新的目标hello库,并将其链接到我们的demo程序
然后同样的方法进行cmake和make进行编译。

4.4、工程分类文件夹编译
在前面,我们成功的使用了库,但是源代码都是在同一个路径下面,这样如果到时候代码量比较大的话,可能就会分类,形成多个文件夹,这样我们需要把代码分开放,此时我们需要些三个CMakeLists.txt文件,目录结构如下

drwxrwxr-x 2 zqq zqq 4096 9月  28 17:32 app
drwxrwxr-x 5 zqq zqq 4096 9月  28 17:12 build
-rw-rw-r-- 1 zqq zqq  487 9月  27 14:42 CMakeLists.txt
drwxrwxr-x 2 zqq zqq 4096 9月  28 17:19 libso

我们将main.c程序放在app目录下面,hello.c hello.h放在libso文件夹下面,然后该文件夹有一个CMakeLists.txt文件,app和libso文件夹下面也有CMakeLists.txt文件,这样就有三个CMakeLists.txt文件了,那么我们接下来来编辑这个三个文件吧。
首先是app文件夹的CMakeLists.txt

project(hello_jelly)
include_directories(${PROJECT_SOURCE_DIR}/../libso)set(APP_SRC main.c)
add_executable(${PROJECT_NAME} main.c)
target_link_libraries(${PROJECT_NAME} helloso)message(${PROJECT_SOURCE_DIR})

然后是libso文件夹的CMakeLists.txt,其中SHARED 表示是生成的动态库,如果把SHARED去掉的话就是生成静态库

project(helloso)
set(LIB_SRC hello.c)
add_library(${PROJECT_NAME} SHARED ${LIB_SRC})

最后是外面那个和app在同一目录下的CMakeLists.txt

cmake_minimum_required (VERSION 3.2)
project(jelly_cmake)
add_subdirectory(./app)
add_subdirectory(./libso)

其表示我们要到./app和./libso文件夹下面去寻找Cmake文件然后进行编译。
最后我们在build目录下面去执行上面的命令编译即可编译出我们需要的可执行文件。

#cmake ../
#make

4.5、Cmake的install简单使用
我的理解cmake中的install其实就是一个将编译好的可执行文件或者是生成的库文件将它放到系统对应的位置,比如说可执行文件直接要放到bin目录下面,so库文件要放在对应的lib目录下面,我在上面的例子的基础上修改CMakeLists.txt文件,编辑完成后编译的步骤如下,就是多了个install步骤,这样我们就可以在Linux上面使用该执行文件,执行文件会去调用so库。

#cmake ../
#make
#make install

app目录修改的CMakeLists.txt如下:只是在之前的基础上加了最后install一行

project(hello_jelly)
include_directories(${PROJECT_SOURCE_DIR}/../libso)set(APP_SRC main.c)
add_executable(${PROJECT_NAME} main.c)
target_link_libraries(${PROJECT_NAME} helloso)message(${PROJECT_SOURCE_DIR})install(TARGETS ${PROJECT_NAME} DESTINATION bin)

libso目录修改的CMakeLists.txt如下:只是在之前的基础上加了最后install一行

project(helloso)
set(LIB_SRC hello.c)
add_library(${PROJECT_NAME} SHARED ${LIB_SRC})install(TARGETS ${PROJECT_NAME} DESTINATION ../lib)

在此需要解释下这个路径问题,install(TARGETS PROJECTNAMEDESTINATIONbin)这句话的意思是安装TARGERShellojelly这个可执行文件到{PROJECT_NAME} DESTINATION bin)这句话的意思是安装TARGERS hello_jelly这个可执行文件到PROJECTN​AMEDESTINATIONbin)这句话的意思是安装TARGERShelloj​elly这个可执行文件到{CMAKE_INSTALL_PREFIX}/bin目录下面,我测试打印我的CMAKEINSTALLPREFIX路径是/usr/local路径,bin前面不能有/,否则会是绝对路径,它不再会去获取{CMAKE_INSTALL_PREFIX}路径是/usr/local路径,bin前面不能有/,否则会是绝对路径,它不再会去获取CMAKEI​NSTALLP​REFIX路径是/usr/local路径,bin前面不能有/,否则会是绝对路径,它不再会去获取{CMAKE_INSTALL_PREFIX}路径,
综上所述,可执行文件安装的路径是:
/usr/local/bin/
so库文件的安装路径是:
/usr/local/…/lib/
最后执行那三个命令就完了,此时你可以在你的Linux系统里面的任何目录执行./hello_jelly
注:如果执行make install的时候出现错误,可以加上sudo再次执行试试。

4.6、给出一个实际的例子

cmake_minimum_required(VERSION 3.4.1)set(APP_SRCsrc/main/cpp/native-lib.cpp)add_library( native-libSHARED${SRC_LIST})find_library( log-liblog )include_directories(libs/include)
set(DIR ../../../../libs)
add_library(avcodec-56SHAREDIMPORTED)
set_target_properties(avcodec-56PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libavcodec-56.so)add_library(avdevice-56SHAREDIMPORTED)
set_target_properties(avdevice-56PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libavdevice-56.so)
add_library(avformat-56SHAREDIMPORTED)
set_target_properties(avformat-56PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libavformat-56.so)
add_library(avutil-54SHAREDIMPORTED)
set_target_properties(avutil-54PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libavutil-54.so)
add_library(postproc-53SHAREDIMPORTED)
set_target_properties(postproc-53PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libpostproc-53.so)
add_library(swresample-1SHAREDIMPORTED)
set_target_properties(swresample-1PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libswresample-1.so)add_library(swscale-3SHAREDIMPORTED)set_target_properties(swscale-3PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libswscale-3.so)add_library(avfilter-5SHAREDIMPORTED)set_target_properties(avfilter-5PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libavfilter-5.so)
target_link_libraries( native-libavfilter-5avcodec-56avdevice-56avformat-56avutil-54postproc-53swresample-1swscale-3${log-lib}android)

五、 JNI编程技术
我们为更好地掌握Android Studio JNI编程技术,我们首先介绍JAVA平台下JNI实现方法,了解JNI实现的基本要素。

5.1、编写Native的JAVA接口
一个NetworkUtils.java程序, 文件路径:d:\jni\com\example\ administrator\testfirst\

package com.example.administrator.testfirst;public class NetworkUtils {public native String GetMACAddressByIP(String ipAddr);static {System.loadLibrary("libnetutils");}public static void main(String[] args) {System.out.printf("Jni libnetutils");}
}

接口函数:
public native String GetMACAddressByIP(String ipAddr);

实现接口函数调用的C/C++封装的SO库:
System.loadLibrary(“libnetutils”);

Main函数是解决JAVA编译可能出现没有主函数引起的错误。

5.2、编写JAVA编译文件
编写Java编译的build.sh文件,文件路径:d:\jni\com\example\ administrator\testfirst\

#! /bin/bash
javac NetworkUtils.java

5.3、编写生成H头文件sh文件
编写Build_c.sh文件:文件路径:d:\jni\

#! /bin/bash
Javah -o jni_example.h -classpath D:\jni;D\Android- studio\platforms\android-28\android.jar -jni com.example.administrator.testfirst.NetworkUtils

根据生成的H头文件,建立C/C++文件。

5.4、编写C/C++生成SO文件
编写MakeFile文件,文件路径:d:\jni\

CC = arm-linux-gnueabi-gcc
CFLAGS = -Wall -g -O -fPIC
CXXFLAGS =
INCLUDE  = -I ./inc -I ../comm/inc -I/usr/include -I/usr/lib/jvm/java-7-openjdk-armel/include
TARGET   = libnetutils.so
LIBPATH  = ./libsvpath %.h ./incOBJS = com_example_administrator_testfirst_GetMACAddressByIP.o
SRCS = com_example_administrator_testfirst_GetMACAddressByIP.call:$(OBJS)$(OBJS):$(SRCS)$(CC) $(CFLAGS) $(INCLUDE) -c $^$(CC) -shared -fPIC -o $(TARGET) $(OBJS)
#   mv $(TARGET) $(LIBPATH)clean:rm -rf $(TARGET) $(OBJS)

5.5、编写jar包生成文件
编写build_jar.sh文件,文件路径:D:\jni\

jar cvf libjni_example.jar -C .

六、 Android studio之NDK开发
我们在前面,已经讨论了NDK安装,CMaklists.txt的编写,JNI编程技术,这里编写JNI编程技术,主要是针对Android Studio环境下JNI编程,完整地介绍使用Android Studio实现JNI编程。
Android Studio JNI编程主要由:1)NDK安装;2)NDK配置;3)配置C/C++环境;4)配置CMakelists.txt编译;5)编写JNI接口程序;6)生成H头文件;7)编写C/C++程序。
前面我已介绍了NDK安装,以及NDK配置,这里,我们开始介绍如何编译JNI程序。

6.1、导入C/C++的SO及H文件
建立armeable和include两个目录,将SO文件拷贝到armeable目录里,将H头文件拷贝到include目录里。

6.2、建立C/C++编译环境
修改app级的build.gradle文件,插入:

externalNativeBuild {cmake {cppFlags "-frtti -fexceptions"abiFilters 'armeabi'}}}externalNativeBuild {cmake {path "CMakeLists.txt"}}

我们只要看到图中三个点存在,表示JNI编译环境已经建立起来了。其布局位置:

abiFilters 完整表达格式是:

abiFilters "armeabi", "armeabi-v7a" , "arm64-v8a", "x86", "x86_64", "mips", "mips64"

具体表达描述请查阅abiFilters相关资料。
对了,还有一个值得注意的是:

6.3、编写CMakelista.txt程序

cmake_minimum_required(VERSION 3.4.1)set(APP_SRCsrc/main/cpp/native-lib.cpp)add_library( native-libSHARED${SRC_LIST})find_library( log-liblog )include_directories(libs/include)
set(DIR ../../../../libs)
add_library(avcodec-56SHAREDIMPORTED)
set_target_properties(avcodec-56PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libavcodec-56.so)add_library(avdevice-56SHAREDIMPORTED)
set_target_properties(avdevice-56PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libavdevice-56.so)
add_library(avformat-56SHAREDIMPORTED)
set_target_properties(avformat-56PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libavformat-56.so)
add_library(avutil-54SHAREDIMPORTED)
set_target_properties(avutil-54PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libavutil-54.so)
add_library(postproc-53SHAREDIMPORTED)
set_target_properties(postproc-53PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libpostproc-53.so)
add_library(swresample-1SHAREDIMPORTED)
set_target_properties(swresample-1PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libswresample-1.so)add_library(swscale-3SHAREDIMPORTED)set_target_properties(swscale-3PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libswscale-3.so)add_library(avfilter-5SHAREDIMPORTED)set_target_properties(avfilter-5PROPERTIES IMPORTED_LOCATION${DIR}/armeabi/libavfilter-5.so)
target_link_libraries( native-libavfilter-5avcodec-56avdevice-56avformat-56avutil-54postproc-53swresample-1swscale-3${log-lib}android)

将CMakelists.txt文件拷贝到app目录下。我们需要注意点,add_library( native-lib SHARED ${SRC_LIST})语句,是由SRC_LIST所有文件编译为native_lib.so文件。

6.4、编写JNI的JAVA程序

package com.example.ffmpegplay;import android.view.Surface;import java.util.Map;public class FFmpegPlayer {public native int initNative();public native void stopNative();native void renderFrameStart();native void renderFrameStop();private native void seekNative(long positionUs) throws NotPlayingException;private native long getVideoDurationNative();public native void render(Surface surface);private native void deallocNative();private native int setDataSourceNative(String url, Map<String, String> dictionary, int videoStreamNo,int audioStreamNo, int subtitleStreamNo);private native void pauseNative() throws NotPlayingException;private native void resumeNative() throws NotPlayingException;static {System.loadLibrary("avcodec-56");System.loadLibrary("avdevice-56");System.loadLibrary("avfilter-5");System.loadLibrary("avformat-56");System.loadLibrary("avutil-54");System.loadLibrary("postproc-53");System.loadLibrary("swresample-1");System.loadLibrary("swscale-3");System.loadLibrary("native-lib");};//public static void main(String[] args) {//    System.out.printf("FFmpegPlayer\n");//}
}

文件路径按照工程路径,不需另造目录。

6.5、编写生成H文件的build.bat文件

javah -o FFmpegPlayer.h -classpath G:\VideoViewer\app\build\intermediates\javac\debug\classes;G:\Android-studio\platforms\android-28\android.jar -jni com.example.ffmpegplay.FFmpegPlayer  com.example.ffmpegplay.NotPlayingException

build.bat文件放在目录G:\VideoViewer\app\build\intermediates\javac\debug\classes下。一旦我们的工程编译成功,在该目录下会生成对应的class文件,我们就可以执行build.bat文件,生成H头文件了。运行build.bat文件:

注意:该文件最好备份一个到其它地方,防止丢失。

6.6、建C/C++工程,编写C\C++文件。
创建一个CPP目录于app/scrc/main/目录下,并将H头文件拷贝到CPP目录下,接下来就可以开启C/C++编程之旅了。

七、 Android studuio实例描述
八、

Android Studio之NDK开发相关推荐

  1. 使用Android Studio 进行NDK开发和调试

    2019独角兽企业重金招聘Python工程师标准>>> 尽管Android Studio已经越来越流行了,但很多人还是习惯于Eclipse或源码环境下开发JNI应用.个人认为使用An ...

  2. Android studio中NDK开发(四)——使用addr2line分析Crash日志 backtrace

    文章目录 一.前言 二.分析 1.先提取backtrace部分 2.提取对应so库的信息 3.提取错误地址 三.使用addr2line对地址进行转换 1.addr2line工具的路径放在 2.Term ...

  3. android studio下NDK开发

    2019独角兽企业重金招聘Python工程师标准>>> 1.NDK 的下载 和 配置 NDK 下载地址 : https://developer.android.com/ndk/ind ...

  4. 26.Android Studio下Ndk开发(ffmpeg导入Android studio以cmake方式编译的过程)

    Android studio 2.2开始支持cmake的方式进行jni开发,我在另一篇10.Eclipse下Ndk开发(ffmpeg解码)中写过Eclipse下编译ffmpeg的过程,但是现在几乎没有 ...

  5. Android Studio集成NDK开发环境

    这几天需要使用C语言在底层编译,所以就打算在Android studio中打造可以编译C的环境,毕竟使用Android studio久了,就不怎么想用Eclipse开发了 废话不多说,直接来看一波集成 ...

  6. android studio ndk-builld方式开发

    之前都是在Ubuntu开发,项目也是老的,自然也就顺理成章的用eclipse做各种android的开发.最近想在android studio 切换下,有点不习惯.android studio 为ndk ...

  7. android Studio 配置LUA 开发环境

    android Studio 配置 LUA 开发环境 关于Android LUA资料 android如何调用lua? Android lua 教程 Lua官网 lua语言解释 Lua 5.1 参考手册 ...

  8. android studio lua插件,android Studio 配置LUA 开发环境

    android Studio 配置 LUA开发环境 关于Android LUA资料 引诉大牛的原话: Android 调用 Lua /Lua 调用 Android 代码 在Android项目中使用Lu ...

  9. android studio lua插件,[置顶] android Studio 配置LUA 开发环境

    android Studio 配置 LUA 开发环境 关于Android LUA资料 android如何调用lua? Android lua 教程 Lua官网 lua语言解释 Lua 5.1 参考手册 ...

  10. 【Android NDK 开发】Android Studio 的 NDK 配置 ( 源码编译配置 | 构建脚本配置 | 打包配置 | CMake 配置 | ndkBuild 配置 )

    文章目录 I . 源码编译配置 II . 构建脚本配置 III . NDK 函数库打包配置 IV . Java 与 C 代码示例 V . CMake 配置 ( CMakeLists.txt ) VI ...

最新文章

  1. zabbix 源码编译安装
  2. [译] CSS 变量实现炫酷鼠标悬浮效果
  3. Linux-目录结构说明
  4. .NET(C#) Internals: 以一个数组填充的例子初步了解.NET 4.0中的并行(一)
  5. (持续更新)webstorm快捷键及术语翻译
  6. icmp基于tcp还是udp_面试爱考、编程必会基础!TCP/IP必知必会10大问题……
  7. Python程序每日一练习
  8. stringstream字符串流的妙用
  9. db2 sql执行历史_5 个免费的在线 SQL 数据库环境,比Navicat 香!
  10. Linux C Serial串口编程
  11. 批量文件转换:PDG转PDF并合并
  12. Mac终端没有命令失效
  13. 威斯康星麦迪逊计算机专业排名,威斯康星大学麦迪逊分校计算机工程学科排名...
  14. ffmpeg翻译文档
  15. 【万人围观】20位著名作家,100句惊艳了时光的名句
  16. P3320 [SDOI2015]寻宝游戏 题解
  17. 二维空间的抛物型偏微分方程基本解法——ADI与紧ADI方法
  18. 论语 灵公篇(笔记)
  19. Lambada表达式常用案例(技能题)
  20. 第五节、AHK对话框详解

热门文章

  1. PX4的软件仿真(SITL)环境配置过程
  2. 无线网络技术导论笔记(第五讲)
  3. 旅游B2B2C系统解决方案
  4. 封装SIP和SOIC有什么区别
  5. 古体字与简体字对照表_简体字与繁体字对照表
  6. 无法阻止的电竞热潮-用电竞连接世界
  7. 1x pcie 速度_usb 3.0 速度 pciex1 pci 速度 各总线的速度
  8. 专门用来保存图片生成图片链接的工具博文
  9. ARQ协议与滑动窗口协议
  10. MATLAB经纬度转化