上节讲到了JAVA框架代码和应用程序的关系,那么框架代码和驱动层是怎么联系的呢?这就是这一节的内容:JNI
java使用一种叫做jni的技术来支持对C/C++代码的调用,在anroid中jni的代码放在froyo/frameworks/base/core/jni下,当然在java框架代码的目录下还有其他地方也多多少少放了jni代码,大家可以打开源码来看看。
整体关系如下图:
| java应用程序
--------------------------------------- Android系统api
| java框架
|本地接口声明
--------------------------------------
| JNI
--------------------------------------
| C/C++代码
继续拿来主义,C/C++中调试用printf,内核调试用printk,呵呵,android调试用log,那么我们就分析log的实现。
log的java代码froyo/frameworks/base/core/java/android/util/Log.java,
/*
* Copyright (C) 2006 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.
*/
packageandroid.util;
importcom.android.internal.os.RuntimeInit;
importjava.io.PrintWriter;
importjava.io.StringWriter;
/**
* API for sending log output.
*
*
Generally, use the Log.v() Log.d() Log.i() Log.w() and Log.e()
* methods.
*
*
The order in terms of verbosity, from least to most is
* ERROR, WARN, INFO, DEBUG, VERBOSE. Verbose should never be compiled
* into an application except during development. Debug logs are compiled
* in but stripped at runtime. Error, warning and info logs are always kept.
*
*
Tip: A good convention is to declare a TAG
constant
* in your class:
*
*
private static final String TAG = "MyActivity";
*
* and use that in subsequent calls to the log methods.
*
*
*
Tip: Don't forget that when you make a call like
*
Log.v(TAG, "index=" + i);
* that when you're building the string to pass into Log.d, the compiler uses a
* StringBuilder and at least three allocations occur: the StringBuilder
* itself, the buffer, and the String object. Realistically, there is also
* another buffer allocation and copy, and even more pressure on the gc.
* That means that if your log message is filtered out, you might be doing
* significant work and incurring significant overhead.
*/
publicfinalclassLog {
/**
* Priority constant for the println method; use Log.v.
*/
publicstaticfinalintVERBOSE =2;
/**
* Priority constant for the println method; use Log.d.
*/
publicstaticfinalintDEBUG =3;
/**
* Priority constant for the println method; use Log.i.
*/
publicstaticfinalintINFO =4;
/**
* Priority constant for the println method; use Log.w.
*/
publicstaticfinalintWARN =5;
/**
* Priority constant for the println method; use Log.e.
*/
publicstaticfinalintERROR =6;
/**
* Priority constant for the println method.
*/
publicstaticfinalintASSERT =7;
/**
* Exception class used to capture a stack trace in {@link #wtf()}.
*/
privatestaticclassTerribleFailureextendsException {
TerribleFailure(String msg, Throwable cause) {super(msg, cause); }
}
privateLog() {
}
/**
* Send a {@link #VERBOSE} log message.
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param msg The message you would like logged.
*/
publicstaticintv(String tag, String msg) {
returnprintln_native(LOG_ID_MAIN, VERBOSE, tag, msg);
}
/**
* Send a {@link #VERBOSE} log message and log the exception.
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param msg The message you would like logged.
* @param tr An exception to log
*/
publicstaticintv(String tag, String msg, Throwable tr) {
returnprintln_native(LOG_ID_MAIN, VERBOSE, tag, msg +'/n'+ getStackTraceString(tr));
}
/**
* Send a {@link #DEBUG} log message.
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param msg The message you would like logged.
*/
publicstaticintd(String tag, String msg) {
returnprintln_native(LOG_ID_MAIN, DEBUG, tag, msg);
}
/**
* Send a {@link #DEBUG} log message and log the exception.
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param msg The message you would like logged.
* @param tr An exception to log
*/
publicstaticintd(String tag, String msg, Throwable tr) {
returnprintln_native(LOG_ID_MAIN, DEBUG, tag, msg +'/n'+ getStackTraceString(tr));
}
/**
* Send an {@link #INFO} log message.
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param msg The message you would like logged.
*/
publicstaticinti(String tag, String msg) {
returnprintln_native(LOG_ID_MAIN, INFO, tag, msg);
}
/**
* Send a {@link #INFO} log message and log the exception.
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param msg The message you would like logged.
* @param tr An exception to log
*/
publicstaticinti(String tag, String msg, Throwable tr) {
returnprintln_native(LOG_ID_MAIN, INFO, tag, msg +'/n'+ getStackTraceString(tr));
}
/**
* Send a {@link #WARN} log message.
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param msg The message you would like logged.
*/
publicstaticintw(String tag, String msg) {
returnprintln_native(LOG_ID_MAIN, WARN, tag, msg);
}
/**
* Send a {@link #WARN} log message and log the exception.
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param msg The message you would like logged.
* @param tr An exception to log
*/
publicstaticintw(String tag, String msg, Throwable tr) {
returnprintln_native(LOG_ID_MAIN, WARN, tag, msg +'/n'+ getStackTraceString(tr));
}
/**
* Checks to see whether or not a log for the specified tag is loggable at the specified level.
*
* The default level of any tag is set to INFO. This means that any level above and including
* INFO will be logged. Before you make any calls to a logging method you should check to see
* if your tag should be logged. You can change the default level by setting a system property:
* 'setprop log.tag. '
* Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will
* turn off all logging for your tag. You can also create a local.prop file that with the
* following in it:
* 'log.tag.='
* and place that in /data/local.prop.
*
* @param tag The tag to check.
* @param level The level to check.
* @return Whether or not that this is allowed to be logged.
* @throws IllegalArgumentException is thrown if the tag.length() > 23.
*/
publicstaticnativebooleanisLoggable(String tag,intlevel);
/*
* Send a {@link #WARN} log message and log the exception.
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param tr An exception to log
*/
publicstaticintw(String tag, Throwable tr) {
returnprintln_native(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));
}
/**
* Send an {@link #ERROR} log message.
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param msg The message you would like logged.
*/
publicstaticinte(String tag, String msg) {
returnprintln_native(LOG_ID_MAIN, ERROR, tag, msg);
}
/**
* Send a {@link #ERROR} log message and log the exception.
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param msg The message you would like logged.
* @param tr An exception to log
*/
publicstaticinte(String tag, String msg, Throwable tr) {
returnprintln_native(LOG_ID_MAIN, ERROR, tag, msg +'/n'+ getStackTraceString(tr));
}
/**
* What a Terrible Failure: Report a condition that should never happen.
* The error will always be logged at level ASSERT with the call stack.
* Depending on system configuration, a report may be added to the
* {@link android.os.DropBoxManager} and/or the process may be terminated
* immediately with an error dialog.
* @param tag Used to identify the source of a log message.
* @param msg The message you would like logged.
*/
publicstaticintwtf(String tag, String msg) {
returnwtf(tag, msg,null);
}
/**
* What a Terrible Failure: Report an exception that should never happen.
* Similar to {@link #wtf(String, String)}, with an exception to log.
* @param tag Used to identify the source of a log message.
* @param tr An exception to log.
*/
publicstaticintwtf(String tag, Throwable tr) {
returnwtf(tag, tr.getMessage(), tr);
}
/**
* What a Terrible Failure: Report an exception that should never happen.
* Similar to {@link #wtf(String, Throwable)}, with a message as well.
* @param tag Used to identify the source of a log message.
* @param msg The message you would like logged.
* @param tr An exception to log. May be null.
*/
publicstaticintwtf(String tag, String msg, Throwable tr) {
tr =newTerribleFailure(msg, tr);
intbytes = println_native(LOG_ID_MAIN, ASSERT, tag, getStackTraceString(tr));
RuntimeInit.wtf(tag, tr);
returnbytes;
}
/**
* Handy function to get a loggable stack trace from a Throwable
* @param tr An exception to log
*/
publicstaticString getStackTraceString(Throwable tr) {
if(tr ==null) {
return"";
}
StringWriter sw =newStringWriter();
PrintWriter pw =newPrintWriter(sw);
tr.printStackTrace(pw);
returnsw.toString();
}
/**
* Low-level logging call.
* @param priority The priority/type of this log message
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param msg The message you would like logged.
* @return The number of bytes written.
*/
publicstaticintprintln(intpriority, String tag, String msg) {
returnprintln_native(LOG_ID_MAIN, priority, tag, msg);
}
/** @hide */publicstaticfinalintLOG_ID_MAIN =0;
/** @hide */publicstaticfinalintLOG_ID_RADIO =1;
/** @hide */publicstaticfinalintLOG_ID_EVENTS =2;
/** @hide */publicstaticfinalintLOG_ID_SYSTEM =3;
/** @hide */publicstaticnativeintprintln_native(intbufID,
intpriority, String tag, String msg);
}
我们看到所有代码都是调用public static native int println_native(int bufID,
int priority, String tag, String msg);来实现输出的,这个函数的实现就是C++,调用的方式就是JNI
我们看一下对应的jni代码froyo/frameworks/base/core/jni/android_util_Log.cpp,最终调用的输出函数是
/*
* In class android.util.Log:
* public static native int println_native(int buffer, int priority, String tag, String msg)
*/
staticjint android_util_Log_println_native(JNIEnv* env, jobject clazz,
jint bufID, jint priority, jstring tagObj, jstring msgObj)
{
constchar* tag = NULL;
constchar* msg = NULL;
if(msgObj == NULL) {
jclass npeClazz;
npeClazz = env->FindClass("java/lang/NullPointerException");
assert(npeClazz != NULL);
env->ThrowNew(npeClazz,"println needs a message");
return-1;
}
if(bufID <0|| bufID >= LOG_ID_MAX) {
jclass npeClazz;
npeClazz = env->FindClass("java/lang/NullPointerException");
assert(npeClazz != NULL);
env->ThrowNew(npeClazz,"bad bufID");
return-1;
}
if(tagObj != NULL)
tag = env->GetStringUTFChars(tagObj, NULL);
msg = env->GetStringUTFChars(msgObj, NULL);
intres = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
if(tag != NULL)
env->ReleaseStringUTFChars(tagObj, tag);
env->ReleaseStringUTFChars(msgObj, msg);
returnres;
}
当然我们发现最终输出是
intres = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
用力grep了一下代码,结果如下
./system/core/include/cutils/log.h:int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text);
./system/core/liblog/logd_write.c:int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
./system/core/liblog/logd_write.c: return __android_log_buf_write(bufID, prio, tag, buf);
这个就是和android专用驱动进行通信的方式,这个分析下去就有点深了,后面分析。
以上三个小节分析了android的JAVA环境,我这里都是简单的抛砖引玉,希望能给大家一点大体的指引,其他修行靠大家了,能成为是一个android程序员是多么幸福的事情,各位已经在幸福中了,我什么时候也可以幸福一把??