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的库里,所以我们要显示的将这个库引入进来。
- /* 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
- */