Why your Android NDK breakpoints might fail and how to fix them

Overview

 

First of all let's overview the structure of a typical Android app containing native code:

native1

The app code is stored inside an .apk file that is essentially a ZIP archive containing the classes.dex file (with all the Java code) and one or more native libraries in lib\<EABI name> subdirectory. The typical lifecycle of an application with native code is the following:

  1. The Android OS loads the Java code and starts executing it
  2. The Java code calls System.loadLibrary() to load a native library.
  3. The Java code starts calling functions from the native code.

Each native library exists in 2 versions:

  •  A "full" version debugging information that associates code addresses inside the library with source files and lines. This version resides in obj\local\armeabi under your project directory.
  • A "stripped" version without the debugging information that resides in libs\armeabi and gets packaged into the APK file.

While the Android device runs the smaller "stripped" version of the library, the GDB debugger needs the larger "non-stripped" version to map source files into addresses and vice versa:

native2

Setting breakpoints

 

When you set a breakpoint on a given line of a source file, the GDB debugger needs to perform certain computations before the breakpoint can be physically created and starts triggering. Assume libNative1 is loaded at address 0x10000 and the user is setting a breakpoint in line 24 of c:\main.cpp. GDB will do the following before it can set a breakpoint:

  1. GDB starts searching all loaded libraries for the file called "c:\main.cpp". In this example the Android OS only reports the libNative1.so library.
  2. GDB looks inside obj\local\armeabi for a file called libNative1.so which is the "non-stripped" version of the library containing the debug information. GDB reads the symbol table from it and finds c:\main.cpp inside it.
  3. Based on the symbol table GDB computes that line 24 corresponds to offset +0x2. It adds 0x2 to the load address of libNative1.so (0x10000) and sets the breakpoint at 0x10002.

If any of the 3 steps fail, the breakpoint will not be created, or will be created wrongly and will never be hit. The next section explains how to diagnose and fix the related problems.

Diagnosing the problems

This section provides detailed instructions how to check for the most common problems with breakpoints caused by non-standard configurations or missing files:

A. Ensure that the library is loaded

First thing to do is to determine if the native library has been actually loaded and if GDB knows about it. It is done by running the "info shared" command in GDB:

native-sharedlib

The output from the info shared command should include 3 important lines:

  1. Loaded symbols for "linker"
  2. Loaded symbols for "libc.so"
  3. Loaded symbols for all native libraries you want to debug.

Here's an example of those lines:

If some of the libraries are not present, you can force GDB to manually reload the symbols by running the following command in GDB:


If your .so library (e.g. libMyAndroidApp.so) is listed, but the symbols are not loaded ("Syms read" states "no"), GDB was not able to find a non-stripped version of the library. To fix it please run the "show solib-search-path" command in GDB:

native-solib

The obj/local/armeabi directory of your project should be present among the reported directories and it should contain the .so file with symbols. If not, copy the correct .so file into a directory listed here and rerun the "sharedlibrary" command.

You can alternatively force GDB to search additional directories for the .so file using the set solib-search-path command.

If your .so library is not present in the list, it has not been loaded by the Java code yet. Please ensure that System.loadLibrary() is called from your Java code and that it succeeds without any Java exceptions.

B. Ensure that you are using correct file paths

A common cause of many breakpoint problems is a configuration when the directories using for building and debugging your project are different. E.g. if the library was compiled from c:\projects and then the sources were moved to d:\projects, setting a breakpoint in d:\projects\main.cpp will fail, as GDB will not accept c:\projects\main.cpp as a substitute.

Those problems can be diagnosed by looking into the source file lists and comparing them with the file used to set a breakpoint. First of all, remove your breakpoint, try setting your breakpoint again and watch what GDB command is issued by your IDE:

native-break-insert

The -break-insert command used to set a breakpoint will specify a path to your source file.

Run the "info sources" command to see the source files discovered by GDB:

native-info-sources

Copy the output of the command to the clipboard (Ctrl-A, Ctrl-C), paste it into a text editor and search for the source file you are trying to debug (e.g. MyAndroidApp.c):

native-source-list

If the file is not present in the list, you have loaded a wrong version of the .so file (see previous section). If the file is present, but the path is different from the one used in -break-insert command, you have found the cause of your problem. Rebuild your app using the new path, or move the file back to an old location so that the path used for creating breakpoints matches the path reported by the "info sources" command.

Note that GDB requires the file path to be EXACTLY the same as reported by "info sources", including double slash before "jni".

C. Recheck file versions and do a clean build

If your breakpoints are set, but never hit, there is probably a mismatch between the .so file version loaded into the Android application and the .so file version used by GDB to compute addresses from source lines. The most common cause for it is the bug in the Android loader that loads the armeabi library instead of the armeabi-v7a version. If you suspect this to happen, change your build configuration to build either armeabi, or armeabi-v7a platform, but not both at the same time and rebuild your application.'

引用自  http://www.codeproject.com/Articles/493043/Why-your-Android-NDK-breakpoints-might-fail-and-ho

android之StrictMode介绍

Android 2.3起,新增加了一个新的类,叫StrictMode(android.os.StrictMode)。这个类可以用来帮助开发者改进他们编写的应用,并且提供了各种的策略,这些策略能随时检查报告开发者开发应用中存在的问题,比如可以监视那些本不应该在主线程中完成的工作或者其他的一些不规范和不好的代码。

StrictMode的策略和规则

  目前,有两大类的策略可供使用

一类是关于常用的监控方面的

Disk Reads 磁盘读

Disk Writes 磁盘写

Network access 网络访问

Custom Slow Code 自定义的运行速度慢的代码分析

前面三种的意思读者应该很清楚,就是正如它们的名字所示,分别对磁盘的读和写,网络访问进行监控。而第四种的自定义慢代码分析,是仅当访问调用类的时后才触发的,可以通过这种

方法去监视运行缓慢的代码。当在主线程中调用时,这些验证规则就会起作用去检查你的代码。比如,当你的应用在下载或者解析大量的数据时,你可以触发自定义运行速度慢代码的查询分、

析,作用很大。StrictMode可以用于捕捉发生在应用程序主线程中耗时的磁盘、网络访问或函数调用,可以帮助开发者使其改进程序,使主线程处理UI和动画在磁盘读写和网络操作时变得更平

滑,避免主线程被阻塞的发生。

另一类是关于VM虚拟机等方面的策略

内存泄露的Activity对象

内存泄露的SQLite对象

内存泄露的释放的对象

其中,内存泄露的Activity对象和内存泄露的SQLite对象都比较好理解,而所谓对关闭对象的检查,主要是去监那些本该释放的对象,比如应该调用close()方法的对象

相关的违反情况可以记录在LogCat中或者存储在DropBox中(android.os.DropBox)服务中

  如何使用:

  放在activity的周期onCreate方法中

 

Android 源代码 error: Exited sync due to fetch errors…

希望各位不要出现这个错误,出现这个错误就要折腾一会了

首先继续repo sync,若是一直提示这个错误,那么就按照下面的方法来做吧:

关于这个问题其实google是有说明的http://source.android.com/source/downloading.html,为了防止连接数过多,每个ip都需要认证。。。

第一步:从这里 the password generator 获取用户名和密码,前提是你在之前填写了你的真实姓名和邮箱

第二步:将上面的页面上以machine开头的两行复制到 ~/.netrc文件中

第三步:

多了个“/a”,并且指定 “--config-name”

参数,很多时候会用以前配置过的用户名密码,指定这个参数会重新设置用户名

然后就可以repo sync了

特别注意.netrc文件是在用户的根目录下,root用户就是/目录下,如果没有的话就自己建一个,把权限改为 777 好了

Android 中的FrameLayout 布局

最近在研究 Cocos2dX ,一直没弄明白 GLSurfaceView 是如何工作的,研究代码发现了动态创建了FrameLayout(单帧布局) ,特意研究了一下这个布局,信息,发现果然比较适合 OpenGL ES 之流的东西的绘制。

FrameLayout是五大布局中最简单的一个布局, 在这个布局中,整个界面被当成一块空白备用区域,所有的子元素都不能被指定放置的位置,它们统统放于这块区域的左上角,并且后面的子元素直接覆盖在前面的 子元素之上,将前面的子元素部分和全部遮挡。显示效果如下,第一个TextView被第二个TextView完全遮挡,第三个TextView遮挡了第二 个TextView的部分位置。

image

参考的XML 信息

这样子,就可以保证,前面的Java控件的绘制,不会阻挡了后面的C++ 代码的绘制,加上JNI 的协助,基本上实现了本地代码跟其他Java的近乎实时的无缝交互。

Android查看源代码的版本号

确认Android源代码的版本号:

1. 编译的时候在终端中一开始就会打印出来:

2. 直接去make文件中去看:

import-module的注意事项与NDK_MODULE_PATH的配置

引用 http://blog.sina.com.cn/s/blog_4057ab62010197z8.html

import-module的功能
导入外部模块的.mk文件 ,和 include基本一样。
概念上的区别是include导入的是由我们自己写的.mk。而import-module导入的是外部库、外部模块提供的.mk。
用法上:include的路径是.mk文件的绝对路径。
而import是设置的路径指定到模块的.mk所在目录,是相对于NDK_MODULE_PATH中的路径列表的相对路径。

import-module的使用
$(call import-module,相对路径)

-----------------场景重现---------------------------
比如我的当前模块要调用 cocosdenshion模块。
1\找到模块名字和路径
找到cocosdenshion模块的android.mk的位置。F:\cocos2d-x\CocosDenshion\android\android.mk
打开看到:
LOCAL_MODULE := cocosdenshion_shared
...
include $(BUILD_STATIC_LIBRARY)
那么cocosdenshion模块在我自己的android.mk中引用它是应该叫它cocosdenshion_shared。而且他是个静态库。

2\在Android.mk中引用模块
就像普通代码中引用头文件一样。
在android.mk的最后一行调用
$(call import-module,CocosDenshion/android)
来导入模块。
注意:我的NDK_MODULE_PATH=/cygdrive/f/cocos2d-x 是已经设置好了的。
如果引用的模块里面也有import-module,他的相对路径也要加到NDK_MODULE_PATH中。如果它没被加进去的话。
然后
LOCAL_WHOLE_STATIC_LIBRARIES += cocos_jpeg_static
声明我这模块要引用该静态库模块。
-------------------------------------------------------

import-module的使用注意
1、设置路径时,注意与NDK_MODULE_PATH中的路径相互配合。
1、导入模块的.mk中如果也有import-module,则注意其相对路径也要在NDK_MODULE_PATH中。
2、上面说了import-module和include一样。如果import-module和Include包含了同一个.mk,会报重复包含的错误。

NDK_MODULE_PATH的配置

NDK_MODULE_PATH的作用
NDK_MODULE_PATH是一个很重要的变量,当android.mk中使用了$(call import-module,XXX)函数引入外部库文件时会用到,用以指示该往哪里去找这个文件。
如果NDK_MODULE_PATH 没有设置或者设置不正确。编译时都是报错 Are you sure your NDK_MODULE_PATH variable is properly defined。
NDK_MODULE_PATH的设置与格式
NDK_MODULE_PATH 是一个环境变量,不是android.mk中设置的变量。
NDK_MODULE_PATH多个路径用冒号分割。不是分号!且整个字符串中间不能有空格。格式不正确也会报错上面的错误的。
设置NDK_MODULE_PATH的方法
1、在系统环境里手动添加这个环境变量,
2、在build_native.sh中 运行ndk-build之前使用export命令定义环境变量NDK_MODULE_PATH。
如:export NDK_MODULE_PATH=路径1:路径2:路径3
3、直接将NDK_MODULE_PATH=路径1:路径2 加到 ndk-build命令的参数后面。ndk-build的参数最终会直接传给make.
如:$NDK_ROOT_LOCAL/ndk-build -C $HELLOWORLD_ROOT NDK_MODULE_PATH=路径1:路径2
(命令 make aaa=213 //在编译makefile之前将aaa当作环境变量设置为213.)

4、还可以在android.mk中设置NDK_MODULE_PATH
在import语句之前加入,
$(call import-add-path,$(LOCAL_PATH)/platform/third_party/android/prebuilt)
将一个新的路径加入NDK_MODULE_PATH变量。

Android.mk简介

引用 http://blog.csdn.net/hudashi/article/details/7059006

Android.mk文件是GNU Makefile的一小部分,它用来对Android程序进行编译。
因为所有的编译文件都在同一个 GNU MAKE 执行环境中进行执行,而Android.mk中所有的变量都是全局的。因此,您应尽量少声明变量,不要认为某些变量在解析过程中不会被定义。
一个Android.mk文件可以编译多个模块,每个模块属下列类型之一:
1)APK程序
一般的Android程序,编译打包生成apk文件
2)JAVA库
java类库,编译打包生成jar文件
3)C\C++应用程序
可执行的C\C++应用程序
4)C\C++静态库
编译生成C\C++静态库,并打包成.a文件
5)C\C++共享库
编译生成共享库(动态链接库),并打包成.so文, 有且只有共享库才能被安装/复制到您的应用软件(APK)包中。
可以在每一个Android.mk file 中定义一个或多个模块,你也可以在几个模块中使用同一个
源代码文件。  编译系统为你处理许多细节问题。例如,你不需要在你的 Android.mk 中列出头文件和依
赖文件。编译系统将会为你自动处理这些问题。这也意味着,在升级 NDK 后,你应该
得到新的toolchain/platform支持,而且不需要改变你的 Android.mk 文件。
注意,NDK的Anroid.mk语法同公开发布的Android平台开源代码的Anroid.mk语法很接近,然而编译系统实现他们的
方式却是不同的,这是故意这样设计的,可以让程序开发人员重用外部库的源代码更容易。
在描述语法细节之前,咱们来看一个简单的"hello world"的例子,比如,下面的文件:
sources/helloworld/helloworld.c
sources/helloworld/Android.mk
'helloworld.c'是一个 JNI 共享库,实现返回"hello world"字符串的原生方法。相应的
Android.mk 文件会象下面这样:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= helloworld
LOCAL_SRC_FILES := helloworld.c
include $(BUILD_SHARED_LIBRARY)
解释一下几行代码:
LOCAL_PATH := $(call my-dir)
一个Android.mk file首先必须定义好LOCAL_PATH变量。它表示是当前文件的路径。
在这个例子中, 宏函数‘my-dir’,  由编译系统提供, 用于返回当前路径(即包含Android.mk file
文件的目录)。
include $(CLEAR_VARS)
CLEAR_VARS 由编译系统提供(可以在 android 安装目录下的/build/core/config.mk 文件看到其定义,为 CLEAR_VARS:=$(BUILD_SYSTEM)/clear_vars.mk),指定让GNU MAKEFILE该脚本为你清除许多 LOCAL_XXX 变量 ( 例如 LOCAL_MODULE , LOCAL_SRC_FILES ,LOCAL_STATIC_LIBRARIES,等等…),除 LOCAL_PATH。这是必要的,因为所有的编译文件都在同一个 GNU MAKE 执行环境中,所有的变量都是全局的。所以我们需要先清空这些变量(LOCAL_PATH除外)。又因为LOCAL_PATH总是要求在每个模块中都要进行 设置,所以并需要清空它。
另外注意,该语句的意思就是把CLEAR_VARS变量所指向的脚本文件包含进来。
LOCAL_MODULE := helloworld
LOCAL_MODULE 变量必须定义,以标识你在 Android.mk 文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'foo'的共享库模 块,将会生成'libfoo.so'文件。
注意:如果把库命名为‘libhelloworld’,编译系统将不会添加任何的 lib 前缀,也会生成 libhelloworld.so。
LOCAL_SRC_FILES := helloworld.c
LOCAL_SRC_FILES 变量必须包含将要编译打包进模块中的 C 或 C++源代码文件。不用
在这里列出头文件和包含文件,编译系统将会自动找出依赖型的文件,当然对于包含文件,你包含时指定的路径应该正确。
注意,默认的 C++源码文件的扩展名是‘.cpp’ 。指定一个不同的扩展名也是可能的,只要定义LOCAL_DEFAULT_CPP_EXTENSION 变量,不要忘记开始的小圆点(也就是定义为  ‘.cxx’,而不是‘cxx’)
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY 是编译系统提供的变量,指向一个 GNU Makefile 脚本(应该
就是在 build/core  目录下的 shared_library.mk) ,将根据LOCAL_XXX系列变量中的值,来编译生成共享库(动态链接库)。
如果想生成静态库,则用BUILD_STATIC_LIBRARY
在NDK的sources/samples目录下有更复杂一点的例子,写有注释的 Android.mk 文件。
二、自定义变量
以下是在 Android.mk中依赖或定义的变量列表, 可以定义其他变量为自己使用,但是NDK编译系统保留下列变量名:
-以 LOCAL_开头的名字(例如 LOCAL_MODULE)
-以 PRIVATE_, NDK_ 或 APP_开头的名字(内部使用)
-小写名字(内部使用,例如‘my-dir’)
如果为了方便在 Android.mk 中定义自己的变量,建议使用 MY_前缀,一个小例子:
MY_SOURCES := foo.c
ifneq ($(MY_CONFIG_BAR),)
MY_SOURCES += bar.c
endif
LOCAL_SRC_FILES += $(MY_SOURCES)
注意:‘:=’是赋值的意思;'+='是追加的意思;‘$’表示引用某变量的值。
三、GNU Make系统变量
这些 GNU Make变量在你的 Android.mk 文件解析之前,就由编译系统定义好了。注意在
某些情况下,NDK可能分析 Android.mk 几次,每一次某些变量的定义会有不同。
(1)CLEAR_VARS:  指向一个编译脚本,几乎所有未定义的 LOCAL_XXX 变量都在"Module-description"节中列出。必须在开始一个新模块之前包含这个脚本:include$(CLEAR_VARS),用于重 置除LOCAL_PATH变量外的,所有LOCAL_XXX系列变量。
(2)BUILD_SHARED_LIBRARY:  指向编译脚本,根据所有的在 LOCAL_XXX 变量把列出的源代码文件编译成一个共享库。
注意,必须至少在包含这个文件之前定义 LOCAL_MODULE 和 LOCAL_SRC_FILES。
(3) BUILD_STATIC_LIBRARY:  一个 BUILD_SHARED_LIBRARY 变量用于编译一个静态库。静态库不会复制到的APK包中,但是能够用于编译共享库。
示例:include $(BUILD_STATIC_LIBRARY)
注意,这将会生成一个名为 lib$(LOCAL_MODULE).a 的文件
(4)TARGET_ARCH: 目标 CPU平台的名字,  和 android 开放源码中指定的那样。如果是
arm,表示要生成 ARM 兼容的指令,与 CPU架构的修订版无关。
(5)TARGET_PLATFORM: Android.mk 解析的时候,目标 Android 平台的名字.详情可参考/development/ndk/docs/stable- apis.txt.
android-3 -> Official Android 1.5 system images
android-4 -> Official Android 1.6 system images
android-5 -> Official Android 2.0 system images
(6)TARGET_ARCH_ABI:  暂时只支持两个 value,armeabi 和 armeabi-v7a。在现在的版本中一般把这两个值简单的定义为 arm, 通过 android  平台内部对它重定义来获得更好的匹配。其他的 ABI 将在以后的 NDK 版本中介绍,它们会有不同的名字。注意虽然所有基于
ARM的ABI都会把 'TARGET_ARCH'定义成‘arm’, 但是会有不同的‘TARGET_ARCH_ABI’。
( 7 ) TARGET_ABI:  目标平台和 ABI 的组合,它事实上被定义成$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)  ,在想要在真实的设备中针对一个特别的目标系统进行测试时,会有用。在默认的情况下,它会是'android-3-arm'。
五、模块描述变量
下面的变量用于向编译系统描述你的模块。你应该定义在'include  $(CLEAR_VARS)'和'include $(BUILD_XXXXX)'之间。正如前面描写的那样,$(CLEAR_VARS)是一个脚本,清除所有这些变量。
(1) LOCAL_PATH:  这个变量用于给出当前文件的路径。必须在 Android.mk 的开头定义,可以这样使用:LOCAL_PATH := $(call my-dir)  这个变量不会被$(CLEAR_VARS)清除,因此每个 Android.mk 只需要定义一次(即使在一个文件中定义了几个模块的情况下)。
(2)LOCAL_MODULE: 这是模块的名字,它必须是唯一的,而且不能包含空格。必须在包含任一的$(BUILD_XXXX)脚本之前定义它。模块的名字决定了生成文件的名字。例 如,如果一个一个共享库模块的名字是,那么生成文件的名字就是 lib.so。但是,在的 NDK 生成文
件中(或者 Android.mk 或者 Application.mk),应该只涉及(引用)有正常名字的其他模块。
(3)LOCAL_SRC_FILES:  这是要编译的源代码文件列表。只要列出要传递给编译器的文件,因为编译系统自动计算依赖。注意源代码文件名称都是相对于 LOCAL_PATH的,你可以使用路径部分,例如:
LOCAL_SRC_FILES := foo.c toto/bar.c\
Hello.c
文件之间可以用空格或Tab键进行分割,换行请用"\".如果是追加源代码文件的话,请用LOCAL_SRC_FILES +=
注意:在生成文件中都要使用UNIX风格的斜杠(/).windows风格的反斜杠不会被正确的处理。
注意:可以LOCAL_SRC_FILES := $(call all-subdir-java-files)这种形式来包含local_path目录下的所有java文件。
(4) LOCAL_CPP_EXTENSION:  这是一个可选变量, 用来指定C++代码文件的扩展名,默认是'.cpp',但是可以改变它,比如:
LOCAL_CPP_EXTENSION := .cxx
(5) LOCAL_C_INCLUDES:  可选变量,表示头文件的搜索路径。默认的头文件的搜索路径是LOCAL_PATH目录。
示例:LOCAL_C_INCLUDES := sources/foo或LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
LOCAL_C_INCLUDES需要在任何包含LOCAL_CFLAGS/LOCAL_CPPFLAGS标志之前进行设置。
(6)LOCAL_CFLAGS:  可选的编译器选项,在编译 C 代码文件的时候使用。这可能是有
用的,指定一个附加的包含路径(相对于NDK的顶层目录),宏定义,或者编译选项。
注意:不要在 Android.mk 中改变 optimization/debugging 级别,只要在 Application.mk 中指定合适的信息,就会自动地为你处理这个问题,在调试期间,会让 NDK自动生成有用的数据文件。
(7)LOCAL_CXXFLAGS:  与 LOCAL_CFLAGS同理,针对 C++源文件。
(8)LOCAL_CPPFLAGS:  与 LOCAL_CFLAGS同理,但是对 C 和 C++ source files都适用。
(9)LOCAL_STATIC_LIBRARIES: 表示该模块需要使用哪些静态库,以便在编译时进行链接。
(10)LOCAL_SHARED_LIBRARIES:  表示模块在运行时要依赖的共享库(动态库),在链接时就需要,以便在生成文件时嵌入其相应的信息。注意:它不会附加列出的模块到编译图,也就是仍然需要 在Application.mk 中把它们添加到程序要求的模块中。
(11)LOCAL_LDLIBS:  编译模块时要使用的附加的链接器选项。这对于使用‘-l’前缀传递指定库的名字是有用的。
例如,LOCAL_LDLIBS := -lz表示告诉链接器生成的模块要在加载时刻链接到/system/lib/libz.so
可查看 docs/STABLE-APIS.TXT 获取使用 NDK发行版能链接到的开放的系统库列表。
(12) LOCAL_ALLOW_UNDEFINED_SYMBOLS:  默认情况下, 在试图编译一个共享库时,任何未定义的引用将导致一个“未定义的符号”错误。这对于在源代码文件中捕捉错误会有很大的帮助。然而,如果因为某些原因,需要 不启动这项检查,可把这个变量设为‘true’。
注意相应的共享库可能在运行时加载失败。(这个一般尽量不要去设为 true)。
(13) LOCAL_ARM_MODE: 默认情况下, arm目标二进制会以 thumb 的形式生成(16 位),你可以通过设置这个变量为 arm如果你希望你的 module 是以 32 位指令的形式。
'arm' (32-bit instructions) mode. E.g.:
LOCAL_ARM_MODE := arm
注意:可以在编译的时候告诉系统针对某个源码文件进行特定的类型的编译
比如,LOCAL_SRC_FILES := foo.c bar.c.arm  这样就告诉系统总是将 bar.c 以arm的模式编译。
(14)LOCAL_MODULE_PATH 和 LOCAL_UNSTRIPPED_PATH
在 Android.mk 文件中, 还可以用LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH指定最后的目标安装路径.
不同的文件系统路径用以下的宏进行选择:
TARGET_ROOT_OUT:表示根文件系统。
TARGET_OUT:表示 system文件系统。
TARGET_OUT_DATA:表示 data文件系统。
用法如:LOCAL_MODULE_PATH :=$(TARGET_ROOT_OUT)
至于LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH的区别,暂时还不清楚。
七、GNU Make‘功能’宏
GNU Make‘功能’宏,必须通过使用'$(call  )'来调用,调用他们将返回文本化的信息。
(1)my-dir:返回当前 Android.mk 所在的目录的路径,相对于 NDK 编译系统的顶层。这是有用的,在 Android.mk 文件的开头如此定义:
LOCAL_PATH := $(call my-dir)
(2)all-subdir-makefiles: 返回一个位于当前'my-dir'路径的子目录中的所有Android.mk的列表。
例如,看下面的目录层次:
sources/foo/Android.mk
sources/foo/lib1/Android.mk
sources/foo/lib2/Android.mk
如果 sources/foo/Android.mk 包含一行:
include $(call all-subdir-makefiles)
那么它就会自动包含 sources/foo/lib1/Android.mk 和 sources/foo/lib2/Android.mk。
这项功能用于向编译系统提供深层次嵌套的代码目录层次。
注意,在默认情况下,NDK 将会只搜索在 sources/*/Android.mk 中的文件。
(3)this-makefile:  返回当前Makefile 的路径(即这个函数调用的地方)
(4)parent-makefile:  返回调用树中父 Makefile 路径。即包含当前Makefile的Makefile 路径。
(5)grand-parent-makefile:返回调用树中父Makefile的父Makefile的路径
八、 Android.mk 使用模板
在一个 Android.mk 中可以生成多个APK应用程序,JAVA库,C\C++可执行程序,C\C++动态库和C\C++静态库。
(1)编译APK应用程序模板。
关于编译APK应用程序的模板请参照《Android.mk编译APK范例》
(2)编译JAVA库模板
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Any libraries that this library depends on
LOCAL_JAVA_LIBRARIES := android.test.runner
# The name of the jar file to create
LOCAL_MODULE := sample
# Build a static jar file.
include $(BUILD_STATIC_JAVA_LIBRARY)
注:LOCAL_JAVA_LIBRARIES := android.test.runner表示生成的JAVA库的jar文件名
(3)编译C/C++应用程序模板如下:
LOCAL_PATH := $(call my-dir)
#include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := test_exe
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $(BUILD_EXECUTABLE)
注:‘:=’是赋值的意思,'+='是追加的意思,‘$’表示引用某变量的值
LOCAL_SRC_FILES中加入源文件路径,LOCAL_C_INCLUDES中加入需要的头文件搜索路径
LOCAL_STATIC_LIBRARIES 加入所需要链接的静态库(*.a)的名称,
LOCAL_SHARED_LIBRARIES 中加入所需要链接的动态库(*.so)的名称,
LOCAL_MODULE表示模块最终的名称,BUILD_EXECUTABLE 表示以一个可执行程序的方式进行编译。
(4)编译C\C++静态库
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
helloworld.c
LOCAL_MODULE:= libtest_static
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $(BUILD_STATIC_LIBRARY)
和上面相似,BUILD_STATIC_LIBRARY 表示编译一个静态库。
(5)编译C\C++动态库的模板
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := helloworld.c
LOCAL_MODULE := libtest_shared
TARGET_PRELINK_MODULES := false
#LOCAL_C_INCLUDES :=
#LOCAL_STATIC_LIBRARIES :=
#LOCAL_SHARED_LIBRARIES :=
include $(BUILD_SHARED_LIBRARY)
和上面相似,BUILD_SHARED_LIBRARY 表示编译一个共享库。
以上三者的生成结果分别在如下目录中,generic 依具体 target 会变:
out/target/product/generic/obj/APPS
out/target/product/generic/obj/JAVA_LIBRARIES
out/target/product/generic/obj/EXECUTABLE
out/target/product/generic/obj/STATIC_LIBRARY
out/target/product/generic/obj/SHARED_LIBRARY
每个模块的目标文件夹分别为:
1)APK程序:XXX_intermediates
2)JAVA库程序:XXX_intermediates
这里的XXX
3)C\C++可执行程序:XXX_intermediates
4)C\C++静态库: XXX_static_intermediates
5)C\C++动态库: XXX_shared_intermediates

Android NDK 在小米2 中的调试

近几天一直在捣鼓Eclipse下面直接调试NDK ,以前都是从命令行中直接调试,现在听说现在Google已经发布了新的支持Eclipse直接调试的SDK ,心中甚为欣喜,本以为可以很快解决问题,没想到折腾了许久。

1. Android ADT的BUG

ADT 的Eclipse插件从r20 开始就已经支持通过CDT 直接调试NDK ,但是有一个问题,他只支持CDT 8.01以下的版本,原因在于CDT 升级到8.1的时候,修改了文件索引器部分的逻辑,也就是我们按F3可以来回跳转到制定文件的那个东东,导致Android的ADT不能与CDT进行很好 的配合,于是编译的时候报告一堆的文件找不到。实际上直接编译是没有问题的,问题不是编译器的问题。

关于这个问题的基本描述在 http://tools.android.com/recent 这个链接上面 ,因为经常会被墙,索性截图,如下

image

这个页面中比较详细的描述了怎么使用NDK ,但是请注意从下面的这个 BUG 33788 这个就是讨论了不能在最新的Eclipse 4.2 和CDT8.1上不能正确使用的原因。了解详情的,可以看看这个链接里面的详细讨论。

不过比较欣慰的是,在 ADT 21 Preview 2 中修复了这个问题。但是目前这个版本还没有发布,因此我们要配置ADT到Preview通道。具体的配置信息 链接 http://tools.android.com/preview-channel 同样配置一下截图

image

配置完成之后,最好重建一下工程,基本上提示错误的情况发生的不会太频繁,但是似乎仍旧会发生。也就是这个补丁还不怎么稳定。

一旦还是发生找不到JNI之类的问题,一般可以通过如下图操作来暂时处理掉

image

2. run-as: Package '<PACKAGE_NAME>' has corrupt installation

根据提示信息,貌似软件包没有正确安装造成的,反复安装调试了很多次,结果照旧,于是怀疑是不是某些其他原因。

好在Android是开源的,那么就比较好处理了,从 4.01的源代码中提取出 run-as部分的源代码,然后调整编译,在可能出问题的地方增加输出,看看到底是什么原因造成的,结果还真找到了。分离出来的源代码以及在此下载 run-as.zip

出问题的代码位置如下

这句话,意思是 目录的权限中,others用户是不能有读写权限的,这个目录就是 /data

源 代码的编译是在run-as目录下面执行NDK-Build即可生成的可执行文件run-as在 obj/local/armeabi 目录下面。通过  adb push run-as /system/bin/run-as-log 命令把这个文件放到手机,如果提示没有权限或者只读,请参考这个 小米 2 adb remount failed解决办法

注意 千万不要动系统自带的run-as 否则系统重启之后,往往起不来。

然后执行

来看看他的输出

也就是 run-as 要求 /data目录是不能有others用户的读写权限的。否则就会失败。根本就不是安装错误。

解决方法

权限信息如下:

4、修改权限

5、再次执行

注意,似乎手机重启之后,设置又会还原掉,因此到时候还是需要重新设置。

简 单描述一下图中的 chmod函数的参数,注意 这个 drwxrwxrwx root root  .其中 r = read 定义为 0x004 ,w = write 定义为 0x002 ,x = execute 定义为 0x001 , 那么 rwx 都拥有的话 ,就是 r | w|x = 7

rwxrwxrwx 分为三组用户 依次为,文件拥有者的权限,文件拥有者所在用户组的权限,其他用户的权限

因此 chmod 771 就表示  文件拥有者的权限 有 r|w|x的全部权限,而其他用户只有执行权限。

java中的native关键字

转载 http://www.blogjava.net/shiliqiang/articles/287920.html

JNI 是Java Native Interface的 缩写。从Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计 的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。

使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机实现下。

JNI(Java Native Interface)的书写步骤

·编写带有native声明的方法的java类
·使用javac命令编译所编写的java类
·使用javah ?jni java类名生成扩展名为h的头文件
·使用C/C++(或者其他编程想语言)实现本地方法
·将C/C++编写的文件生成动态连接库

1) 编写java程序:
这里以HelloWorld为例。
代码1:

声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明改方法为native的,并且不能实现。其中方法的参数和返回值在后面讲述。

Load 动态库:System.loadLibrary("hello");加载动态库(我们可以这样理解:我们的方法displayHelloWorld()没 有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是 System.loadLibrary();的参数“hello”是动态库的名字。
main()方法
2) 编译没有什么好说的了

3) 生成扩展名为h的头文件

头文件的内容:

(这 里我们可以这样理解:这个h文件相当于我们在java里面的接口,这里声明了一个Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致)。

4) 编写本地方法
实现和由javah命令生成的头文件里面声明的方法名相同的方法。
代码2:

注 意代码2中的第1行,需要将jni.h(该文件可以在%JAVA_HOME%/include文件夹下面找到)文件引入,因为在程序中的JNIEnv、 jobject等类型都是在该头文件中定义的;另外在第2行需要将HelloWorld.h头文件引入(我是这么理解的:相当于我们在编写java程序的 时候,实现一个接口的话需要声明才可以,这里就是将HelloWorld.h头文件里面声明的方法加以实现。当然不一定是这样)。然后保存为 HelloWorldImpl.c就 ok了。

5) 生成动态库
这里以在Windows中为例,需要生成dll文件。在保存HelloWorldImpl.c文件夹下面,使用VC的编译器cl成。

注 意:生成的dll文件名在选项-Fe后面配置,这里是hello,因为在HelloWorld.java文件中我们loadLibary的时候使用的名字 是hello。当然这里修改之后那里也需要修改。另外需要将-I%java_home%\include -I%java_home%\include\win32参数加上,因为在第四步里面编写本地方法的时候引入了jni.h文件。

6) 运行程序

JNI(Java Native Interface)调用中考虑的问题

在首次使用JNI的时候有些疑问,后来在使用中一一解决,下面就是这些问题的备忘:

1.  java和c是如何互通的?
其实不能互通的原因主要是数据类型的问题,jni解决了这个问题,例如那个c文件中的jstring数据类型就是java传入的String对象,经过jni函数的转化就能成为c的char*。
对应数据类型关系如下表:
Java 类型 本地c类型 说明
boolean jboolean 无符号,8 位
byte jbyte 无符号,8 位
char jchar 无符号,16 位
short jshort 有符号,16 位
int jint 有符号,32 位
long jlong 有符号,64 位
float jfloat 32 位
double jdouble 64 位
void void N/A

2. 如何将java传入的String参数转换为c的char*,然后使用?
java 传入的String参数,在c文件中被jni转换为jstring的数据类型,在c文件中声明char* test,然后test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);注意:test使用完后,通知虚拟机平台相关代码无需再访问:(*env)->ReleaseStringUTFChars(env, jstring, test);

3. 将c中获取的一个char*的buffer传递给java?
这个char*如果是一般的字符串的话,作为string传回去就可以了。如果是含有’\0’的buffer,最好作为bytearray传出,因为可以制定copy的length,如果copy到string,可能到’\0’就截断了。
有两种方式传递得到的数据:
一种是在jni中直接new一个byte数组,然后调用函数(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer);将buffer的值copy到bytearray中,函数直接return bytearray就可以了。
一种是return错误号,数据作为参数传出,但是java的基本数据类型是传值,对象是传递的引用,所以将这个需要传出的byte数组用某个类包一下,如下:

这个对象作为函数的参数retobj传出,通过如下函数将retobj中的byte数组赋值便于传出。代码如下:

4. 不知道占用多少空间的buffer,如何传递出去呢?

在jni的c文件中new出空间,传递出去。java的数据不初始化,指向传递出去的空间即可。

Android JNI知识简介

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/linweig/archive/2010/03/26/5417319.aspx

Java Native Interface (JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI 是本地编程接口,它使得在 Java 虚拟机 (VM) 内部运行的 Java 代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行交互操作。

1.从如何载入.so档案谈起

由于Android的应用层的类都是以Java写的,这些Java类编译为Dex型式的Bytecode之后,必须靠Dalvik虚拟机(VM: Virtual Machine)来执行。VM在Android平台里,扮演很重要的角色。

此外,在执行Java类的过程中,如果Java类需要与C组件沟通时,VM就会去载入C组件,然后让Java的函数顺利地调用到C组件的函数。此时,VM扮演着桥梁的角色,让Java与C组件能通过标准的JNI介面而相互沟通。

应用层的Java类是在虚拟机(VM: Vitual Machine)上执行的,而C件不是在VM上执行,那么Java程式又如何要求VM去载入(Load)所指定的C组件呢? 可使用下述指令:

System.loadLibrary(*.so的档案名);

例如,Android框架里所提供的MediaPlayer.java类,含指令:

这要求VM去载入Android的/system/lib/libmedia_jni.so档案。载入*.so之后,Java类与*.so档案就汇合起来,一起执行了。

2.如何撰写*.so的入口函数

---- JNI_OnLoad()与JNI_OnUnload()函数的用途

当Android的VM(Virtual Machine)执行到System.loadLibrary()函数时,首先会去执行C组件里的JNI_OnLoad()函数。它的用途有二:

(1) 告诉VM此C组件使用那一个JNI版本。如果你的*.so档没有提供JNI_OnLoad()函数,VM会默认该*.so档是使用最老的JNI 1.1版本。由于新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI 1.4的java.nio.ByteBuffer,就必须藉由JNI_OnLoad()函数来告知VM。

(2)由于VM执行到System.loadLibrary()函数时,就会立即先呼叫JNI_OnLoad(),所以C组件的开发者可以藉由JNI_OnLoad()来进行C组件内的初期值之设定(Initialization) 。

例如,在Android的/system/lib/libmedia_jni.so档案里,就提供了JNI_OnLoad()函数,其程式码片段为:

此函数回传JNI_VERSION_1_4值给VM,于是VM知道了其所使用的JNI版本了。此外,它也做了一些初期的动作(可呼叫任何本地函数),例如指令:

就将此组件提供的各个本地函数(Native Function)登记到VM里,以便能加快后续呼叫本地函数的效率。

JNI_OnUnload() 函数与JNI_OnLoad()相对应的。在载入C组件时会立即呼叫JNI_OnLoad()来进行组件内的初期动作;而当VM释放该C组件时,则会呼叫 JNI_OnUnload()函数来进行善后清除动作。当VM呼叫JNI_OnLoad()或JNI_Unload()函数时,都会将VM的指针 (Pointer)传递给它们,其参数如下:

jint JNI_OnLoad(JavaVM* vm, void* reserved) {     }

jint JNI_OnUnload(JavaVM* vm, void* reserved){     }

在JNI_OnLoad()函数里,就透过VM之指标而取得JNIEnv之指标值,并存入env指标变数里,如下述指令:

由 于VM通常是多执行绪(Multi-threading)的执行环境。每一个执行绪在呼叫JNI_OnLoad()时,所传递进来的JNIEnv指标值都 是不同的。为了配合这种多执行绪的环境,C组件开发者在撰写本地函数时,可藉由JNIEnv指标值之不同而避免执行绪的资料冲突问题,才能确保所写的本地 函数能安全地在Android的多执行绪VM里安全地执行。基于这个理由,当在呼叫C组件的函数时,都会将JNIEnv指标值传递给它,如下:

这 JNI_OnLoad()呼叫register_android_media_MediaPlayer(env)函数时,就将env指标值传递过去。如 此,在register_android_media_MediaPlayer()函数就能藉由该指标值而区别不同的执行绪,以便化解资料冲突的问题。

例如,在register_android_media_MediaPlayer()函数里,可撰写下述指令:

查看是否已经有其他执行绪进入此物件,如果没有,此执行绪就进入该物件里执行了。还有,也可撰写下述指令:

查看是否此执行绪正在此物件内执行,如果是,此执行绪就会立即离开。

3.registerNativeMethods()函数的用途

应 用层级的Java类别透过VM而呼叫到本地函数。一般是仰赖VM去寻找*.so里的本地函数。如果需要连续呼叫很多次,每次都需要寻找一遍,会多花许多时 间。此时,组件开发者可以自行将本地函数向VM进行登记。例如,在Android的/system/lib/libmedia_jni.so档案里的代码 段如下:

当 VM载入libmedia_jni.so档案时,就呼叫JNI_OnLoad()函数。接着,JNI_OnLoad()呼叫 register_android_media_MediaPlayer()函数。此时,就呼叫到 AndroidRuntime::registerNativeMethods()函数,向VM(即AndroidRuntime)登记 gMethods[]表格所含的本地函数了。简而言之,registerNativeMethods()函数的用途有二:

(1)更有效率去找到函数。

(2)可在执行期间进行抽换。由于gMethods[]是一个<名称,函数指针>对照表,在程序执行时,可多次呼叫registerNativeMethods()函数来更换本地函数之指针,而达到弹性抽换本地函数之目的。

4.Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数。其中很重要的区别是Andorid使用了一种Java 和 C 函数的映射表数组,并在其中描述了函数的参数和返回值。这个数组的类型是JNINativeMethod,定义如下:

其中比较难以理解的是第二个参数,例如

实际上这些字符是与函数的参数类型一一对应的。

"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();

"(II)V" 表示 void Func(int, int);

具体的每一个字符的对应关系如下(数组则以”["开始,用两个字符表示)

字符 Java类型 C类型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]

上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾,中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring

如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。

例如

Android JNI编程实践

一、直接使用java本身jni接口(windows/ubuntu)

1.在Eclipsh中新建一个android应用程序。两个类:一个继承于Activity,UI显示用。另一个包含native方法。编译生成所有类。

jnitest.java文件:

Nadd.java文件:

2.使用javah命令生成C/C++的.h文件。注意类要包含包名,路径文件夹下要包含所有包中的类,否则会报找不到类的错误。classpath参数指定到包名前一级文件夹,文件夹层次结构要符合java类的组织层次结构。

com_hello_jnitest_Nadd .h文件:

3.编辑.c文件实现native方法。

com_hello_jnitest_Nadd.c文件:

4.编译.c文件生存动态库。

得到libNadd.so文件。

以上在ubuntu中完成。

5.将相应的动态库文件push到avd的system/lib中:adb push libNadd.so /system/lib。若提示Read-only file system错误,运行adb remount命令,即可。

6.在eclipsh中运行原应用程序即可。

对于一中生成的so文件也可采用二中的方法编译进apk包中。只需在工程文件夹中建libs\armeabi文件夹(其他文件夹名无效,只建立libs文件夹也无效),然后将so文件拷入,编译工程即可。

二.使用NDK生成本地方法(ubuntu and windows)

1.安装NDK:解压,然后进入NDK解压后的目录,运行build/host-setup.sh(需要Make 3.81和awk)。若有错,修改host-setup.sh文件:将#!/bin/sh修改为#!/bin/bash,再次运行即可。

2.在apps文件夹下建立自己的工程文件夹,然后在该文件夹下建一文件Application.mk和项project文件夹。

Application.mk文件:

3.在project文件夹下建一jni文件夹,然后新建Android.mk和myjni.c。这里不需要用javah生成相应的.h文件,但函数名要包含相应的完整的包、类名。

4.编辑相应文件内容。

Android.mk文件:

myjni.c文件:

myjni文件组织:

5.编译:

以上内容在ubuntu完成。

6.在eclipsh中创建android application。将myjni中自动生成的libs文件夹拷贝到当前工程文件夹中,编译运行即可。

NdkTest.java文件:

对于二中生成的so文件也可采用一中的方法push到avd中运行。