如何在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提供的头文件,打开瞧瞧

  1. /* 
  2.  * Copyright (C) 2009 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. #ifndef _ANDROID_LOG_H  
  18. #define _ANDROID_LOG_H  
  19.   
  20. /****************************************************************** 
  21.  * 
  22.  * IMPORTANT NOTICE: 
  23.  * 
  24.  *   This file is part of Android's set of stable system headers 
  25.  *   exposed by the Android NDK (Native Development Kit) since 
  26.  *   platform release 1.5 
  27.  * 
  28.  *   Third-party source AND binary code relies on the definitions 
  29.  *   here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. 
  30.  * 
  31.  *   - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) 
  32.  *   - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS 
  33.  *   - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY 
  34.  *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES 
  35.  */  
  36.   
  37. /* 
  38.  * Support routines to send messages to the Android in-kernel log buffer, 
  39.  * which can later be accessed through the 'logcat' utility. 
  40.  * 
  41.  * Each log message must have 
  42.  *   - a priority 
  43.  *   - a log tag 
  44.  *   - some text 
  45.  * 
  46.  * The tag normally corresponds to the component that emits the log message, 
  47.  * and should be reasonably small. 
  48.  * 
  49.  * Log message text may be truncated to less than an implementation-specific 
  50.  * limit (e.g. 1023 characters max). 
  51.  * 
  52.  * Note that a newline character ("/n") will be appended automatically to your 
  53.  * log message, if not already there. It is not possible to send several messages 
  54.  * and have them appear on a single line in logcat. 
  55.  * 
  56.  * PLEASE USE LOGS WITH MODERATION: 
  57.  * 
  58.  *  - Sending log messages eats CPU and slow down your application and the 
  59.  *    system. 
  60.  * 
  61.  *  - The circular log buffer is pretty small (<64KB), sending many messages 
  62.  *    might push off other important log messages from the rest of the system. 
  63.  * 
  64.  *  - In release builds, only send log messages to account for exceptional 
  65.  *    conditions. 
  66.  * 
  67.  * NOTE: These functions MUST be implemented by /system/lib/liblog.so 
  68.  */  
  69.   
  70. #include <stdarg.h>  
  71.   
  72. #ifdef __cplusplus  
  73. extern "C" {  
  74. #endif  
  75.   
  76. /* 
  77.  * Android log priority values, in ascending priority order. 
  78.  */  
  79. typedef enum android_LogPriority {  
  80.     ANDROID_LOG_UNKNOWN = 0,  
  81.     ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */  
  82.     ANDROID_LOG_VERBOSE,  
  83.     ANDROID_LOG_DEBUG,  
  84.     ANDROID_LOG_INFO,  
  85.     ANDROID_LOG_WARN,  
  86.     ANDROID_LOG_ERROR,  
  87.     ANDROID_LOG_FATAL,  
  88.     ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */  
  89. } android_LogPriority;  
  90.   
  91. /* 
  92.  * Send a simple string to the log. 
  93.  */  
  94. int __android_log_write(int prio, const char *tag, const char *text);  
  95.   
  96. /* 
  97.  * Send a formatted string to the log, used like printf(fmt,...) 
  98.  */  
  99. int __android_log_print(int prio, const char *tag,  const char *fmt, ...)  
  100. #if defined(__GNUC__)  
  101.     __attribute__ ((format(printf, 3, 4)))  
  102. #endif  
  103.     ;  
  104.   
  105. /* 
  106.  * A variant of __android_log_print() that takes a va_list to list 
  107.  * additional parameters. 
  108.  */  
  109. int __android_log_vprint(int prio, const char *tag,  
  110.                          const char *fmt, va_list ap);  
  111.   
  112. /* 
  113.  * Log an assertion failure and SIGTRAP the process to have a chance 
  114.  * to inspect it, if a debugger is attached. This uses the FATAL priority. 
  115.  */  
  116. void __android_log_assert(const char *cond, const char *tag,  
  117.               const char *fmt, ...)      
  118. #if defined(__GNUC__)  
  119.     __attribute__ ((noreturn))  
  120.     __attribute__ ((format(printf, 3, 4)))  
  121. #endif  
  122.     ;  
  123.   
  124. #ifdef __cplusplus  
  125. }  
  126. #endif  
  127.   
  128. #endif /* _ANDROID_LOG_H */  

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

  1. /* 
  2.  * Send a simple string to the log. 
  3.  */  
  4. int __android_log_write(int prio, const char *tag, const char *text);  
  5.   
  6. /* 
  7.  * Send a formatted string to the log, used like printf(fmt,...) 
  8.  */  
  9. int __android_log_print(int prio, const char *tag,  const char *fmt, ...)  
  10.   
  11. /* 
  12.  * A variant of __android_log_print() that takes a va_list to list 
  13.  * additional parameters. 
  14.  */  
  15. int __android_log_vprint(int prio, const char *tag,  
  16.                          const char *fmt, va_list ap);  
  17.   
  18. /* 
  19.  * Log an assertion failure and SIGTRAP the process to have a chance 
  20.  * to inspect it, if a debugger is attached. This uses the FATAL priority. 
  21.  */  
  22. void __android_log_assert(const char *cond, const char *tag,  
  23.               const char *fmt, ...)    

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

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

#include <android/log.h> 

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

AccessFiledPerformanceTest.java

  1. package com.jni;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.util.Log;  
  6.   
  7. public class AccessFiledPerformanceTest extends Activity {  
  8.     String s;  
  9.     private final String PerformanceTestTAG ="AccessFiledPerformanceTest:performanceTestFunc :";  
  10.     /*JNI测试函数,cache Field */  
  11.     private native void cacheField();  
  12.     /*JNI测试函数,not cache Field */  
  13.     private native void notCacheField();  
  14.     static  
  15.     {  
  16.         try   
  17.         {  
  18.             System.loadLibrary("PerformanceTest");  
  19.         } catch (UnsatisfiedLinkError ule)   
  20.         {  
  21.             System.err.println("WARNING: Could not load library!");  
  22.         }     
  23.     }  
  24.     /** Called when the activity is first created. */  
  25.     @Override  
  26.     public void onCreate(Bundle savedInstanceState) {  
  27.         super.onCreate(savedInstanceState);  
  28.         setContentView(R.layout.main);  
  29.         performanceTestFunc();  
  30.     }  
  31.     /* 性能测试函数 */  
  32.     public void performanceTestFunc()  
  33.     {  
  34.         /* 测试数量级为100万次 */  
  35.         int mLoopCount = 1000000;  
  36.         int i = 0;  
  37.         int base = 1000000;   
  38.         String timeStr_ms;  /* 毫秒对应String*/  
  39.         /* 精确度为纳秒级 */  
  40.         long startTime = System.nanoTime();  
  41.         /* Not Cache field  调用 */  
  42.         for(i = 0;i <= mLoopCount; ++ i)  
  43.         {  
  44.             notCacheField();  
  45.         }  
  46.         long endTime = System.nanoTime();  
  47.         timeStr_ms = String.valueOf((endTime-startTime)/base);  
  48.         Log.v(PerformanceTestTAG+"Not Cache field:", timeStr_ms+"ms");  
  49.         startTime = System.nanoTime();  
  50.         /* Cached field  调用 */  
  51.         for(i = 0;i <= mLoopCount; ++ i)  
  52.         {  
  53.             cacheField();  
  54.         }  
  55.         endTime = System.nanoTime();  
  56.         timeStr_ms = String.valueOf((endTime-startTime)/base);  
  57.         Log.v(PerformanceTestTAG+"Cached field:", timeStr_ms+"ms");  
  58.     }  
  59. }  

com_jni_AccessFiledPerformanceTest.c

  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class com_jni_AccessFiledPerformanceTest */  
  4. #include <android/log.h>  /* 注意这里 */  
  5. #include <stdio.h>  
  6.   
  7. #ifndef _Included_com_jni_AccessFiledPerformanceTest  
  8. #define _Included_com_jni_AccessFiledPerformanceTest  
  9. #ifdef __cplusplus  
  10. extern "C" {  
  11. #endif  
  12. /* 
  13.  * Class:     com_jni_AccessFiledPerformanceTest 
  14.  * Method:    cacheField 
  15.  * Signature: ()V 
  16.  */  
  17. JNIEXPORT void JNICALL Java_com_jni_AccessFiledPerformanceTest_cacheField  
  18.   (JNIEnv *env, jobject obj)  
  19.  {  
  20.         static jfieldID fid_s = NULL; /* cached field ID for s */  
  21.         jclass cls = (*env)->GetObjectClass(env, obj);  
  22.         jstring jstr;  
  23.         const char *str;  
  24.         if (fid_s == NULL)  
  25.         {  
  26.                 fid_s = (*env)->GetFieldID(env, cls, "s","Ljava/lang/String;");  
  27.                 if (fid_s == NULL)   
  28.                 {  
  29.                         return/* exception already thrown */  
  30.                 }  
  31.         }  
  32.         __android_log_write(ANDROID_LOG_DEBUG,"Tag","Java_com_jni_AccessFiledPerformanceTest_cacheField");  
  33.  }  
  34.   
  35. /* 
  36.  * Class:     com_jni_AccessFiledPerformanceTest 
  37.  * Method:    notCacheField 
  38.  * Signature: ()V 
  39.  */  
  40. JNIEXPORT void JNICALL Java_com_jni_AccessFiledPerformanceTest_notCacheField  
  41.   (JNIEnv *env, jobject obj)  
  42.  {  
  43.         jfieldID fid_s = NULL; /* Not cached field ID for s */  
  44.         jclass cls = (*env)->GetObjectClass(env, obj);  
  45.         jstring jstr;  
  46.         const char *str;  
  47.         fid_s = (*env)->GetFieldID(env, cls, "s","Ljava/lang/String;");  
  48.         if (fid_s == NULL)   
  49.         {  
  50.                 return/* exception already thrown */  
  51.         }  
  52.         __android_log_write(ANDROID_LOG_DEBUG,"Tag","Java_com_jni_AccessFiledPerformanceTest_notCacheField");  
  53.  }  
  54. JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)  
  55. {  
  56.     JNIEnv *env;  
  57.     int result;  
  58.   
  59.     result = JNI_VERSION_1_4;  
  60.     return result;  
  61.   
  62. }  
  63. #ifdef __cplusplus  
  64. }  
  65. #endif  
  66. #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次。。。呵呵
  1. /* PLEASE USE LOGS WITH MODERATION:   
  2.  *   
  3.  *  - Sending log messages eats CPU and slow down your application and the   
  4.  *    system.   
  5.  *   
  6.  *  - The circular log buffer is pretty small (<64KB), sending many messages   
  7.  *    might push off other important log messages from the rest of the system.   
  8.  *   
  9.  *  - In release builds, only send log messages to account for exceptional   
  10.  *    conditions.   
  11.  *   
  12.  * NOTE: These functions MUST be implemented by /system/lib/liblog.so   
  13.  */   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值