如何在JNI编程中使用logCat

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静态库的时候引用的。
然后运行程序,就会发现如下输出
如何在JNI编程中使用logCat_第1张图片
最后,你是否注意到这了呢?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 */

你可能感兴趣的:(java,编程,android,exception,String,jni)