来自: http://blog.csdn.net/zgjxwl/archive/2011/03/09/6234025.aspx

Android NDK发布后,java+C的编程方式成为android上性能编程的首选。当然,也支持其他语言,如C++等,只要符合JNI规则即可。

在android编程java代码中,我们知道,可以使用Log.v等一些将日志输出到logcat,然后我们就可以看到日志输出信息。当然,也可以在

shell 里使用adb logcat来查看日志信息。对于java代码可以这样查看日志信息,但java调用的C函数呢,是否也可以将日志输出到logcat里呢?这就要看ANDROID NDK是否支持了。以往,在JNI编程中,调试Native Interface Method比较困难,往往都是采用打log的方式将日志输出到文件。今天,在目录

/build/platforms/android-8/arch-arm/usr/include/android/log.h

下发现android NDK提供的头文件,打开瞧瞧

/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _ANDROID_LOG_H #define _ANDROID_LOG_H /****************************************************************** * * IMPORTANT NOTICE: * * This file is part of Android's set of stable system headers * exposed by the Android NDK (Native Development Kit) since * platform release 1.5 * * Third-party source AND binary code relies on the definitions * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. * * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ /* * Support routines to send messages to the Android in-kernel log buffer, * which can later be accessed through the 'logcat' utility. * * Each log message must have * - a priority * - a log tag * - some text * * The tag normally corresponds to the component that emits the log message, * and should be reasonably small. * * Log message text may be truncated to less than an implementation-specific * limit (e.g. 1023 characters max). * * Note that a newline character ("/n") will be appended automatically to your * log message, if not already there. It is not possible to send several messages * and have them appear on a single line in logcat. * * PLEASE USE LOGS WITH MODERATION: * * - Sending log messages eats CPU and slow down your application and the * system. * * - The circular log buffer is pretty small (<64KB), sending many messages * might push off other important log messages from the rest of the system. * * - In release builds, only send log messages to account for exceptional * conditions. * * NOTE: These functions MUST be implemented by /system/lib/liblog.so */ #include <stdarg.h> #ifdef __cplusplus extern "C" { #endif /* * Android log priority values, in ascending priority order. */ typedef enum android_LogPriority { ANDROID_LOG_UNKNOWN = 0, ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */ ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */ } android_LogPriority; /* * Send a simple string to the log. */ int __android_log_write(int prio, const char *tag, const char *text); /* * Send a formatted string to the log, used like printf(fmt,...) */ int __android_log_print(int prio, const char *tag, const char *fmt, ...) #if defined(__GNUC__) __attribute__ ((format(printf, 3, 4))) #endif ; /* * A variant of __android_log_print() that takes a va_list to list * additional parameters. */ int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap); /* * Log an assertion failure and SIGTRAP the process to have a chance * to inspect it, if a debugger is attached. This uses the FATAL priority. */ void __android_log_assert(const char *cond, const char *tag, const char *fmt, ...) #if defined(__GNUC__) __attribute__ ((noreturn)) __attribute__ ((format(printf, 3, 4))) #endif ; #ifdef __cplusplus } #endif #endif /* _ANDROID_LOG_H */

请仔细阅读这个头文件,我们会发现,android NDK完全支持JNI本地方法调试。它提供4个函数供我们使用,如下:

/* * Send a simple string to the log. */ int __android_log_write(int prio, const char *tag, const char *text); /* * Send a formatted string to the log, used like printf(fmt,...) */ int __android_log_print(int prio, const char *tag, const char *fmt, ...) /* * A variant of __android_log_print() that takes a va_list to list * additional parameters. */ int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap); /* * Log an assertion failure and SIGTRAP the process to have a chance * to inspect it, if a debugger is attached. This uses the FATAL priority. */ void __android_log_assert(const char *cond, const char *tag, const char *fmt, ...)

我们可以将本地方法调试信息发送到logcat里。(是不是很爽,以后调试本地方法就不用那么纠结了^_^)

要使用这几个函数,就必须在本地文件中加入如下包含语句

#include<android/log.h>

于是,我们开始编写个测试例程吧,先汗一个,本来是性能测试的,在测试中,发现了这个log.h,于是就用这个例程来展示下吧。

AccessFiledPerformanceTest.java

package com.jni; import android.app.Activity; import android.os.Bundle; import android.util.Log; public class AccessFiledPerformanceTest extends Activity { String s; private final String PerformanceTestTAG ="AccessFiledPerformanceTest:performanceTestFunc :"; /*JNI测试函数,cache Field */ private native void cacheField(); /*JNI测试函数,not cache Field */ private native void notCacheField(); static { try { System.loadLibrary("PerformanceTest"); } catch (UnsatisfiedLinkError ule) { System.err.println("WARNING: Could not load library!"); } } /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); performanceTestFunc(); } /* 性能测试函数 */ public void performanceTestFunc() { /* 测试数量级为100万次 */ int mLoopCount = 1000000; int i = 0; int base = 1000000; String timeStr_ms; /* 毫秒对应String*/ /* 精确度为纳秒级 */ long startTime = System.nanoTime(); /* Not Cache field 调用 */ for(i = 0;i <= mLoopCount; ++ i) { notCacheField(); } long endTime = System.nanoTime(); timeStr_ms = String.valueOf((endTime-startTime)/base); Log.v(PerformanceTestTAG+"Not Cache field:", timeStr_ms+"ms"); startTime = System.nanoTime(); /* Cached field 调用 */ for(i = 0;i <= mLoopCount; ++ i) { cacheField(); } endTime = System.nanoTime(); timeStr_ms = String.valueOf((endTime-startTime)/base); Log.v(PerformanceTestTAG+"Cached field:", timeStr_ms+"ms"); } }

com_jni_AccessFiledPerformanceTest.c

/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_jni_AccessFiledPerformanceTest */ #include <android/log.h> /* 注意这里 */ #include <stdio.h> #ifndef _Included_com_jni_AccessFiledPerformanceTest #define _Included_com_jni_AccessFiledPerformanceTest #ifdef __cplusplus extern "C" { #endif /* * Class: com_jni_AccessFiledPerformanceTest * Method: cacheField * Signature: ()V */ JNIEXPORT void JNICALL Java_com_jni_AccessFiledPerformanceTest_cacheField (JNIEnv *env, jobject obj) { static jfieldID fid_s = NULL; /* cached field ID for s */ jclass cls = (*env)->GetObjectClass(env, obj); jstring jstr; const char *str; if (fid_s == NULL) { fid_s = (*env)->GetFieldID(env, cls, "s","Ljava/lang/String;"); if (fid_s == NULL) { return; /* exception already thrown */ } } __android_log_write(ANDROID_LOG_DEBUG,"Tag","Java_com_jni_AccessFiledPerformanceTest_cacheField"); } /* * Class: com_jni_AccessFiledPerformanceTest * Method: notCacheField * Signature: ()V */ JNIEXPORT void JNICALL Java_com_jni_AccessFiledPerformanceTest_notCacheField (JNIEnv *env, jobject obj) { jfieldID fid_s = NULL; /* Not cached field ID for s */ jclass cls = (*env)->GetObjectClass(env, obj); jstring jstr; const char *str; fid_s = (*env)->GetFieldID(env, cls, "s","Ljava/lang/String;"); if (fid_s == NULL) { return; /* exception already thrown */ } __android_log_write(ANDROID_LOG_DEBUG,"Tag","Java_com_jni_AccessFiledPerformanceTest_notCacheField"); } JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv *env; int result; result = JNI_VERSION_1_4; return result; } #ifdef __cplusplus } #endif #endif


Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := PerformanceTest

LOCAL_SRC_FILES := src/com_jni_AccessFiledPerformanceTest.c

LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)

注意:

LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog

这句很重要

如果在上面的mk文件里没有写LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog这句,则在编译链接的时候会报如下错误

$ ndk-build
Compile thumb : PerformanceTest <= /cygdrive/f/AndroidProjects/AccessFiledPerformanceTest/jni/src/com_jni_AccessFiledPerformanceTest.c
SharedLibrary : libPerformanceTest.so
/cygdrive/f/AndroidProjects/AccessFiledPerformanceTest/obj/local/armeabi/objs/PerformanceTest/src/com_jni_AccessFiledPerformanceTest.o: In function `Java_com_jni_AccessFiledPerform
anceTest_notCacheField':
/cygdrive/f/AndroidProjects/AccessFiledPerformanceTest/jni/src/com_jni_AccessFiledPerformanceTest.c:52: undefined reference to `__android_log_write'
/cygdrive/f/AndroidProjects/AccessFiledPerformanceTest/obj/local/armeabi/objs/PerformanceTest/src/com_jni_AccessFiledPerformanceTest.o: In function `Java_com_jni_AccessFiledPerform
anceTest_cacheField':
/cygdrive/f/AndroidProjects/AccessFiledPerformanceTest/jni/src/com_jni_AccessFiledPerformanceTest.c:32: undefined reference to `__android_log_write'
collect2: ld returned 1 exit status
make: *** [/cygdrive/f/AndroidProjects/AccessFiledPerformanceTest/obj/local/armeabi/libPerformanceTest.so] Error 1

报告错误:引用的__android_log_write函数没有被定义,由此可见,默认情况下并没有引入包含这个函数定义的库文件。于是需要找到这个库文件。

LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog这句的意思就是将liblog.so这个库引入进来,默认只会引入一些关键的库。你会在下面的目录发现这个日志的库文件。

.../build/platforms/android-8/arch-arm/usr/lib/liblog.so

这个函数定义就包含在liblog.so的库里,所以我们要显示的将这个库引入进来。

其中-L参数是指定了搜索lib的路径。至此,这个依赖的库引入后,ndk-build即可生成libPerformanceTest.so的动态库。 当然,上面是生成动态库的例子,如果在生成动态库的时候,使用这句 LOCAL_LDLIBS := -llog 就会报 cannot find -llog的错误。意思是找不到liblog.so这个库文件。这句是在build静态库的时候引用的。 然后运行程序,就会发现如下输出 最后,你是否注意到这了呢?log.h里面的一段,请适度使用。像我这个,就属于重度使用了,--b,不过我这只是用来说明JNI本地方法输出 到logcat的方法。建议你不要像我这样使用,循环100W次。。。呵呵 /* PLEASE USE LOGS WITH MODERATION: * * - Sending log messages eats CPU and slow down your application and the * system. * * - The circular log buffer is pretty small (<64KB), sending many messages * might push off other important log messages from the rest of the system. * * - In release builds, only send log messages to account for exceptional * conditions. * * NOTE: These functions MUST be implemented by /system/lib/liblog.so */ 疑问:我直接使用 LOCAL_LDLIBS := -llog,是没有报错的,可以正常生成so文件 但是不管怎么总,在eclipse的logcat中都没有出现日志信息,但通过模拟器带的adb shell中可以看到日志,不知道这是为什么,请路过的高人指点,非常感谢!

更多相关文章

  1. C语言函数的递归(上)
  2. eclipse Android(安卓)工程在Libs导入第三方jar / Android工程作
  3. 【原创】Android多个xml文件的使用
  4. 菜鸟学Android(安卓)之 selector背景选择器
  5. Android数据持久化之File机制分析
  6. Android(安卓)pdf viewer在android studio应用问题说明详解
  7. 基于frida框架Hook native中的函数(1)
  8. Android(安卓)studio build.gradle 各种错误解决总结
  9. Android语言国际化values资源文件命名规则

随机推荐

  1. android 实时检测网络状态
  2. Android输入框被键盘遮挡
  3. android 获取手机设备信息
  4. Android 源码阅读之MMS
  5. java/android查询手机固话归属地、GSM卡
  6. android > ListView > 加载 网络/assets
  7. android Camera模块分析
  8. Android的jni下c与java数据互传测试代码
  9. Android对文件的读写
  10. Android全屏的两种方法