Android 系統日志(Log) JNI實現流程源碼分析

1、JNI概述

Java Native Interface (JNI) 是一種編程框架,使得Java代碼能夠與用其他編程語言(如C和C++)編寫的本地代碼進行交互。JNI允許Java代碼調用本地代碼的函數,也允許本地代碼調用Java代碼的函數。下面是對JNI機制的詳細概述,包括其基本原理、工作流程、常見用途和示例代碼。

1.1. JNI的基本原理

JNI的核心原理是通過一個標準化的接口,使得Java虛擬機(JVM)和本地代碼可以互相通信和操作。JNI提供了一套函數,這些函數可以在本地代碼中使用,以便與JVM交互,例如創建和操作Java對象,調用Java方法,處理異常等。

1.2. JNI的工作流程

使用JNI的工作流程通常包括以下幾個步驟:

1、聲明本地方法:在Java類中聲明native方法。
2、加載本地庫:在Java類中加載包含本地方法實現的庫。
3、生成頭文件:使用javac和javah工具生成包含native方法聲明的頭文件。
4、實現本地方法:在C/C++中實現native方法。
5、編譯本地庫:編譯C/C++代碼生成共享庫。
6、調用本地方法:在Java代碼中調用native方法。

1.3. JNI的常見用途

JNI常用于以下幾種場景:

1、性能優化:將性能關鍵的部分用C/C++實現,以提高執行效率。
2、訪問底層系統資源:訪問操作系統的底層功能或硬件資源。
3、重用現有庫:調用已有的C/C++庫或API。
4、實現平臺特定功能:在跨平臺應用中實現特定平臺的功能。

2、JNI的優缺點

2.1、優點

1、性能優化:可以使用高效的本地代碼,尤其是在性能關鍵的部分。
2、硬件訪問:能夠直接訪問Java API不提供的底層硬件功能。
3、代碼復用:重用現有的C/C++庫,無需重新實現復雜的邏輯。
4、多語言互操作:可以在同一個應用中使用多種編程語言,各取所長。

2.2、缺點

1、復雜性增加:引入了額外的復雜性,需要了解C/C++和JNI API。
2、平臺依賴性:本地代碼需要針對不同的平臺編譯,增加了維護成本。
3、內存管理:需要手動管理內存,容易出現內存泄漏和指針錯誤。
4、調試困難:調試JNI代碼比純Java代碼困難,需要使用特定的工具和方法。

3、JNI的使用場景

1、性能優化:在需要大量計算或復雜邏輯的地方使用本地代碼。
2、硬件功能:訪問攝像頭、傳感器等底層硬件功能。
3、現有庫:使用已有的C/C++庫,例如圖像處理庫、加密庫等。
4、跨語言調用:在需要與其他編程語言互操作時,例如從Java調用C++代碼。

4、Android日志系統概述

Android日志系統主要由Log類和__android_log_print等C/C++函數組成,提供了記錄調試信息的功能。Java層的日志API(如android.util.Log)最終調用的是本地日志函數,這些函數將日志消息寫入系統日志緩沖區。

要深入了解Android Log的JNI實現流程,我們需要從JNI機制、Android日志系統、JNI方法實現及其相互配合的細節等方面進行詳細分析。

5、本地方法聲明(關鍵字native)

frameworks\base\core\java\android\util\Log.java

 /*** 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.<YOUR_LOG_TAG> <LEVEL>'*  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.<YOUR_LOG_TAG>=<LEVEL>'*  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.*/public static native boolean isLoggable(String tag, int level);

在這里插入圖片描述

/*** 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.*/public static int println(int priority, String tag, String msg) {return println_native(LOG_ID_MAIN, priority, tag, msg);}/** @hide */ public static final int LOG_ID_MAIN = 0;/** @hide */ public static final int LOG_ID_RADIO = 1;/** @hide */ public static final int LOG_ID_EVENTS = 2;/** @hide */ public static final int LOG_ID_SYSTEM = 3;/** @hide */ public static native int println_native(int bufID,int priority, String tag, String msg);

在這里插入圖片描述

6、本地方法實現

frameworks\base\core\jni\android_util_Log.cpp

static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
{if (tag == NULL) {return false;}const char* chars = env->GetStringUTFChars(tag, NULL);if (!chars) {return false;}jboolean result = false;if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {char buf2[200];snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %d characters\n",chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));jniThrowException(env, "java/lang/IllegalArgumentException", buf2);} else {result = isLoggable(chars, level);}env->ReleaseStringUTFChars(tag, chars);return result;
}

在這里插入圖片描述

/** In class android.util.Log:*  public static native int println_native(int buffer, int priority, String tag, String msg)*/
static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,jint bufID, jint priority, jstring tagObj, jstring msgObj)
{const char* tag = NULL;const char* msg = NULL;if (msgObj == NULL) {jniThrowNullPointerException(env, "println needs a message");return -1;}if (bufID < 0 || bufID >= LOG_ID_MAX) {jniThrowNullPointerException(env, "bad bufID");return -1;}if (tagObj != NULL)tag = env->GetStringUTFChars(tagObj, NULL);msg = env->GetStringUTFChars(msgObj, NULL);int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);if (tag != NULL)env->ReleaseStringUTFChars(tagObj, tag);env->ReleaseStringUTFChars(msgObj, msg);return res;
}

在這里插入圖片描述

7、虛擬機里面動態注冊

frameworks\base\core\jni\AndroidRuntime.cpp

/** Register android native functions with the VM.*/
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{/** This hook causes all future threads created in this process to be* attached to the JavaVM.  (This needs to go away in favor of JNI* Attach calls.)*/androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);ALOGV("--- registering native functions ---\n");/** Every "register" function calls one or more things that return* a local reference (e.g. FindClass).  Because we haven't really* started the VM yet, they're all getting stored in the base frame* and never released.  Use Push/Pop to manage the storage.*/env->PushLocalFrame(200);if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {env->PopLocalFrame(NULL);return -1;}env->PopLocalFrame(NULL);//createJavaThread("fubar", quickTest, (void*) "hello");return 0;
}

在這里插入圖片描述

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{for (size_t i = 0; i < count; i++) {if (array[i].mProc(env) < 0) {
#ifndef NDEBUGALOGD("----------!!! %s failed to load\n", array[i].mName);
#endifreturn -1;}}return 0;
}

在這里插入圖片描述

注意傳入進來的參數

static const RegJNIRec gRegJNI[] = {REG_JNI(register_android_debug_JNITest),REG_JNI(register_com_android_internal_os_RuntimeInit),REG_JNI(register_android_os_SystemClock),REG_JNI(register_android_util_EventLog),REG_JNI(register_android_util_Log),REG_JNI(register_android_util_FloatMath),REG_JNI(register_android_text_format_Time),REG_JNI(register_android_content_AssetManager),REG_JNI(register_android_content_StringBlock),REG_JNI(register_android_content_XmlBlock),REG_JNI(register_android_emoji_EmojiFactory),REG_JNI(register_android_text_AndroidCharacter),REG_JNI(register_android_text_AndroidBidi),REG_JNI(register_android_view_InputDevice),REG_JNI(register_android_view_KeyCharacterMap),REG_JNI(register_android_os_Process),REG_JNI(register_android_os_SystemProperties),REG_JNI(register_android_os_Binder),REG_JNI(register_android_os_Parcel),REG_JNI(register_android_view_DisplayEventReceiver),REG_JNI(register_android_nio_utils),REG_JNI(register_android_graphics_PixelFormat),REG_JNI(register_android_graphics_Graphics),REG_JNI(register_android_view_GLES20DisplayList),REG_JNI(register_android_view_GLES20Canvas),REG_JNI(register_android_view_HardwareRenderer),REG_JNI(register_android_view_Surface),REG_JNI(register_android_view_SurfaceSession),REG_JNI(register_android_view_TextureView),REG_JNI(register_com_google_android_gles_jni_EGLImpl),REG_JNI(register_com_google_android_gles_jni_GLImpl),REG_JNI(register_android_opengl_jni_EGL14),REG_JNI(register_android_opengl_jni_GLES10),REG_JNI(register_android_opengl_jni_GLES10Ext),REG_JNI(register_android_opengl_jni_GLES11),REG_JNI(register_android_opengl_jni_GLES11Ext),REG_JNI(register_android_opengl_jni_GLES20),REG_JNI(register_android_graphics_Bitmap),REG_JNI(register_android_graphics_BitmapFactory),REG_JNI(register_android_graphics_BitmapRegionDecoder),REG_JNI(register_android_graphics_Camera),REG_JNI(register_android_graphics_Canvas),REG_JNI(register_android_graphics_ColorFilter),REG_JNI(register_android_graphics_DrawFilter),REG_JNI(register_android_graphics_Interpolator),REG_JNI(register_android_graphics_LayerRasterizer),REG_JNI(register_android_graphics_MaskFilter),REG_JNI(register_android_graphics_Matrix),REG_JNI(register_android_graphics_Movie),REG_JNI(register_android_graphics_NinePatch),REG_JNI(register_android_graphics_Paint),REG_JNI(register_android_graphics_Path),REG_JNI(register_android_graphics_PathMeasure),REG_JNI(register_android_graphics_PathEffect),REG_JNI(register_android_graphics_Picture),REG_JNI(register_android_graphics_PorterDuff),REG_JNI(register_android_graphics_Rasterizer),REG_JNI(register_android_graphics_Region),REG_JNI(register_android_graphics_Shader),REG_JNI(register_android_graphics_SurfaceTexture),REG_JNI(register_android_graphics_Typeface),REG_JNI(register_android_graphics_Xfermode),REG_JNI(register_android_graphics_YuvImage),REG_JNI(register_android_database_CursorWindow),REG_JNI(register_android_database_SQLiteConnection),REG_JNI(register_android_database_SQLiteGlobal),REG_JNI(register_android_database_SQLiteDebug),REG_JNI(register_android_os_Debug),REG_JNI(register_android_os_FileObserver),REG_JNI(register_android_os_FileUtils),REG_JNI(register_android_os_MessageQueue),REG_JNI(register_android_os_ParcelFileDescriptor),REG_JNI(register_android_os_SELinux),REG_JNI(register_android_os_Trace),REG_JNI(register_android_os_UEventObserver),REG_JNI(register_android_net_LocalSocketImpl),REG_JNI(register_android_net_NetworkUtils),REG_JNI(register_android_net_TrafficStats),REG_JNI(register_android_net_wifi_WifiManager),REG_JNI(register_android_os_MemoryFile),REG_JNI(register_com_android_internal_os_ZygoteInit),REG_JNI(register_android_hardware_Camera),REG_JNI(register_android_hardware_SensorManager),REG_JNI(register_android_hardware_SerialPort),REG_JNI(register_android_hardware_UsbDevice),REG_JNI(register_android_hardware_UsbDeviceConnection),REG_JNI(register_android_hardware_UsbRequest),REG_JNI(register_android_media_AudioRecord),REG_JNI(register_android_media_AudioSystem),REG_JNI(register_android_media_AudioTrack),REG_JNI(register_android_media_JetPlayer),REG_JNI(register_android_media_RemoteDisplay),REG_JNI(register_android_media_ToneGenerator),REG_JNI(register_android_opengl_classes),REG_JNI(register_android_server_NetworkManagementSocketTagger),REG_JNI(register_android_server_Watchdog),REG_JNI(register_android_ddm_DdmHandleNativeHeap),REG_JNI(register_android_backup_BackupDataInput),REG_JNI(register_android_backup_BackupDataOutput),REG_JNI(register_android_backup_FileBackupHelperBase),REG_JNI(register_android_backup_BackupHelperDispatcher),REG_JNI(register_android_app_backup_FullBackup),REG_JNI(register_android_app_ActivityThread),REG_JNI(register_android_app_NativeActivity),REG_JNI(register_android_view_InputChannel),REG_JNI(register_android_view_InputEventReceiver),REG_JNI(register_android_view_KeyEvent),REG_JNI(register_android_view_MotionEvent),REG_JNI(register_android_view_PointerIcon),REG_JNI(register_android_view_VelocityTracker),REG_JNI(register_android_content_res_ObbScanner),REG_JNI(register_android_content_res_Configuration),REG_JNI(register_android_animation_PropertyValuesHolder),REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
};

在這里插入圖片描述

看到register_android_util_Log了吧

int register_android_util_Log(JNIEnv* env)
{jclass clazz = env->FindClass("android/util/Log");if (clazz == NULL) {ALOGE("Can't find android/util/Log");return -1;}levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE", "I"));levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I"));levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I"));levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I"));levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I"));levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I"));return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods));
}

在這里插入圖片描述
動態注冊完成,那共享庫(.so)是在哪里加載的呢?

8、共享庫加載

看本地實現文件的mk:frameworks\base\core\jni

LOCAL_SRC_FILES:= \AndroidRuntime.cpp \Time.cpp \com_android_internal_content_NativeLibraryHelper.cpp \com_google_android_gles_jni_EGLImpl.cpp \com_google_android_gles_jni_GLImpl.cpp.arm \android_app_NativeActivity.cpp \android_opengl_EGL14.cpp \android_opengl_GLES10.cpp \android_opengl_GLES10Ext.cpp \android_opengl_GLES11.cpp \android_opengl_GLES11Ext.cpp \android_opengl_GLES20.cpp \android_database_CursorWindow.cpp \android_database_SQLiteCommon.cpp \android_database_SQLiteConnection.cpp \android_database_SQLiteGlobal.cpp \android_database_SQLiteDebug.cpp \android_emoji_EmojiFactory.cpp \android_view_DisplayEventReceiver.cpp \android_view_Surface.cpp \android_view_SurfaceSession.cpp \android_view_TextureView.cpp \android_view_InputChannel.cpp \android_view_InputDevice.cpp \android_view_InputEventReceiver.cpp \android_view_KeyEvent.cpp \android_view_KeyCharacterMap.cpp \android_view_HardwareRenderer.cpp \android_view_GLES20DisplayList.cpp \android_view_GLES20Canvas.cpp \android_view_MotionEvent.cpp \android_view_PointerIcon.cpp \android_view_VelocityTracker.cpp \android_text_AndroidCharacter.cpp \android_text_AndroidBidi.cpp \android_os_Debug.cpp \android_os_FileUtils.cpp \android_os_MemoryFile.cpp \android_os_MessageQueue.cpp \android_os_ParcelFileDescriptor.cpp \android_os_Parcel.cpp \android_os_SELinux.cpp \android_os_SystemClock.cpp \android_os_SystemProperties.cpp \android_os_Trace.cpp \android_os_UEventObserver.cpp \android_net_LocalSocketImpl.cpp \android_net_NetUtils.cpp \android_net_TrafficStats.cpp \android_net_wifi_Wifi.cpp \android_nio_utils.cpp \android_text_format_Time.cpp \android_util_AssetManager.cpp \android_util_Binder.cpp \android_util_EventLog.cpp \android_util_Log.cpp \android_util_FloatMath.cpp \android_util_Process.cpp \android_util_StringBlock.cpp \android_util_XmlBlock.cpp \android/graphics/AutoDecodeCancel.cpp \android/graphics/Bitmap.cpp \android/graphics/BitmapFactory.cpp \android/graphics/Camera.cpp \android/graphics/Canvas.cpp \android/graphics/ColorFilter.cpp \android/graphics/DrawFilter.cpp \android/graphics/CreateJavaOutputStreamAdaptor.cpp \android/graphics/Graphics.cpp \android/graphics/HarfbuzzSkia.cpp \android/graphics/Interpolator.cpp \android/graphics/LayerRasterizer.cpp \android/graphics/MaskFilter.cpp \android/graphics/Matrix.cpp \android/graphics/Movie.cpp \android/graphics/NinePatch.cpp \android/graphics/NinePatchImpl.cpp \android/graphics/NinePatchPeeker.cpp \android/graphics/Paint.cpp \android/graphics/Path.cpp \android/graphics/PathMeasure.cpp \android/graphics/PathEffect.cpp \android_graphics_PixelFormat.cpp \android/graphics/Picture.cpp \android/graphics/PorterDuff.cpp \android/graphics/BitmapRegionDecoder.cpp \android/graphics/Rasterizer.cpp \android/graphics/Region.cpp \android/graphics/Shader.cpp \android/graphics/SurfaceTexture.cpp \android/graphics/TextLayout.cpp \android/graphics/TextLayoutCache.cpp \android/graphics/Typeface.cpp \android/graphics/Utils.cpp \android/graphics/Xfermode.cpp \android/graphics/YuvToJpegEncoder.cpp \android_media_AudioRecord.cpp \android_media_AudioSystem.cpp \android_media_AudioTrack.cpp \android_media_JetPlayer.cpp \android_media_RemoteDisplay.cpp \android_media_ToneGenerator.cpp \android_hardware_Camera.cpp \android_hardware_SensorManager.cpp \android_hardware_SerialPort.cpp \android_hardware_UsbDevice.cpp \android_hardware_UsbDeviceConnection.cpp \android_hardware_UsbRequest.cpp \android_debug_JNITest.cpp \android_util_FileObserver.cpp \android/opengl/poly_clip.cpp.arm \android/opengl/util.cpp.arm \android_server_NetworkManagementSocketTagger.cpp \android_server_Watchdog.cpp \android_ddm_DdmHandleNativeHeap.cpp \com_android_internal_os_ZygoteInit.cpp \android_backup_BackupDataInput.cpp \android_backup_BackupDataOutput.cpp \android_backup_FileBackupHelperBase.cpp \android_backup_BackupHelperDispatcher.cpp \android_app_backup_FullBackup.cpp \android_content_res_ObbScanner.cpp \android_content_res_Configuration.cpp \android_animation_PropertyValuesHolder.cpp

在這里插入圖片描述

編譯的共享庫為:LOCAL_MODULE:= libandroid_runtime

ifeq ($(HAVE_SELINUX),true)
LOCAL_C_INCLUDES += external/libselinux/include
LOCAL_SHARED_LIBRARIES += libselinux
LOCAL_CFLAGS += -DHAVE_SELINUX
endif # HAVE_SELINUXifeq ($(USE_OPENGL_RENDERER),true)LOCAL_SHARED_LIBRARIES += libhwui
endifLOCAL_SHARED_LIBRARIES += \libdl
# we need to access the private Bionic header
# <bionic_tls.h> in com_google_android_gles_jni_GLImpl.cpp
LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/privateLOCAL_LDLIBS += -lpthread -ldlifeq ($(WITH_MALLOC_LEAK_CHECK),true)LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
endifLOCAL_MODULE:= libandroid_runtimeinclude $(BUILD_SHARED_LIBRARY)include $(call all-makefiles-under,$(LOCAL_PATH))

在這里插入圖片描述

而共享庫LOCAL_MODULE:= libandroid_runtime又被編譯到另一個共享庫:LOCAL_MODULE:= libandroid_servers

frameworks\base\services\jni:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)LOCAL_SRC_FILES:= \com_android_server_AlarmManagerService.cpp \com_android_server_BatteryService.cpp \com_android_server_input_InputApplicationHandle.cpp \com_android_server_input_InputManagerService.cpp \com_android_server_input_InputWindowHandle.cpp \com_android_server_LightsService.cpp \com_android_server_power_PowerManagerService.cpp \com_android_server_SerialService.cpp \com_android_server_SystemServer.cpp \com_android_server_UsbDeviceManager.cpp \com_android_server_UsbHostManager.cpp \com_android_server_VibratorService.cpp \com_android_server_location_GpsLocationProvider.cpp \com_android_server_connectivity_Vpn.cpp \onload.cppLOCAL_C_INCLUDES += \$(JNI_H_INCLUDE) \frameworks/base/services \frameworks/base/core/jni \external/skia/include/core \libcore/include \libcore/include/libsuspend \$(call include-path-for, libhardware)/hardware \$(call include-path-for, libhardware_legacy)/hardware_legacy \LOCAL_SHARED_LIBRARIES := \libandroid_runtime \libandroidfw \libcutils \libhardware \libhardware_legacy \libnativehelper \libsystem_server \libutils \libui \libinput \libskia \libgui \libusbhost \libsuspendifeq ($(WITH_MALLOC_LEAK_CHECK),true)LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
endifLOCAL_MODULE:= libandroid_serversinclude $(BUILD_SHARED_LIBRARY)

在這里插入圖片描述

那就看共享庫libandroid_servers是在那里加載的吧!
frameworks\base\services\java\com\android\server

public static void main(String[] args) {if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {// If a device's clock is before 1970 (before 0), a lot of// APIs crash dealing with negative numbers, notably// java.io.File#setLastModified, so instead we fake it and// hope that time from cell towers or NTP fixes it// shortly.Slog.w(TAG, "System clock is before 1970; setting to 1970.");SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);}if (SamplingProfilerIntegration.isEnabled()) {SamplingProfilerIntegration.start();timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {SamplingProfilerIntegration.writeSnapshot("system_server", null);}}, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);}// Mmmmmm... more memory!dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();// The system server has to run all of the time, so it needs to be// as efficient as possible with its memory usage.VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);System.loadLibrary("android_servers");init1(args);}

在這里插入圖片描述
現在知道Log的JNI實現流程了吧?!!!

9、總結

JNI是Android開發中的一個重要工具,提供了Java代碼與本地代碼之間的橋梁。通過JNI,開發者可以在Java代碼中調用高效的本地代碼,訪問特定硬件功能,以及重用現有的C/C++庫。理解JNI的工作原理和使用方法,對于開發高性能、功能豐富的Android應用至關重要。

JNI(Java Native Interface)是Java平臺的重要組成部分,允許Java代碼與本地(通常是C/C++)代碼進行互操作。在Android開發中,JNI廣泛應用于性能優化、硬件訪問以及重用現有的本地庫。以下是關于JNI使用的總結,包括其優缺點、使用場景、開發步驟、注意事項以及最佳實踐。

Android日志系統從應用程序層到JNI層、本地層、Logd守護進程,再到Logcat工具,形成了一個完整的日志記錄與查看的流程。通過分析各個層級的代碼實現,可以更深入地理解日志系統的設計和工作原理。這些組件緊密協作,提供了一個高效、可靠的日志記錄和查看機制,幫助開發者調試和監控應用程序。

歡迎點贊|關注|收藏|評論,您的肯定是我創作的動力

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/16183.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/16183.shtml
英文地址,請注明出處:http://en.pswp.cn/web/16183.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【單片機】STM32F070F6P6 開發指南(一)STM32建立HAL工程

文章目錄 一、基礎入門二、工程初步建立三、HSE 和 LSE 時鐘源設置四、時鐘系統&#xff08;時鐘樹&#xff09;配置五、GPIO 功能引腳配置六、配置 Debug 選項七、生成工程源碼八、生成工程源碼九、用戶程序下載 一、基礎入門 f0 pack下載&#xff1a; https://www.keil.arm…

【OpenCV 基礎知識 19】拉普拉斯變換

功能&#xff1a; cvLaplace 是計算圖像的 Laplacian 變換 &#xff0c;是Intel開源項目opencv中的函數 函數形式&#xff1a; void cvLaplace( const CvArr* src, CvArr* dst, int aperture_size3 ); 參數列表&#xff1a; Src 輸入圖像. Dst 輸出圖像. aperture_size算子內…

離線初始化k8s

導出和導入所有必要的 Kubernetes 鏡像&#xff0c;使用阿里云作為源。 在能訪問外網的機器上拉取鏡像 首先&#xff0c;在有外網訪問的機器上運行以下命令來拉取所有 Kubernetes v1.29.5 版本需要的鏡像&#xff1a; kubeadm config images pull --image-repository regist…

大模型應用:基于Golang實現GPT模型API調用

1.背景 當前OpenAI提供了開放接口&#xff0c;支持通過api的方式調用LLM進行文本推理、圖片生成等能力&#xff0c;但目前官方只提供了Python SDK。為了后續更方便集成和應用&#xff0c;可以采用Golang對核心推理調用接口進行封裝&#xff0c;提供模型調用能力。 2.相關準備…

Spark運行模式詳解

Spark概述 Spark 可以在多種不同的運行模式下執行&#xff0c;每種模式都有其自身的特點和適用場景。 部署Spark集群大體上分為兩種模式&#xff1a;單機模式與集群模式。大多數分布式框架都支持單機模式&#xff0c;方便開發者調試框架的運行環境。但是在生產環境中&#xff…

軟件web化的趨勢

引言 在信息技術飛速發展的今天&#xff0c;軟件Web化已成為一個不可忽視的趨勢。所謂軟件Web化&#xff0c;即將傳統的桌面應用軟件轉變為基于Web的應用程序&#xff0c;使用戶能夠通過瀏覽器進行訪問和使用。傳統軟件通常需要在用戶的計算機上進行安裝和運行&#xff0c;而W…

Cadence OrCAD學習筆記(3)capture使用技巧_1

本期介紹capture的一些使用技巧。資料來源于小破站up主硬小二 1、導出像Visio規格的圖紙 2、全局修改元件屬性 然后保存、關閉即可。 3、導出BOM 4、導出網表 5、元件自動編號 6、capture軟件和allegro關聯 7、新建原理圖symbol 以上為添加封裝庫的路徑 如果要創建多部分的sy…

積累|新質生產力之地方發展的不同賽道

“不要搞一種模式”。任何事物都是共性和個性的統一&#xff0c;也就是矛盾普遍性和特殊性的統一。就發展新質生產力而言&#xff0c;既要遵循新質生產力的普遍規律和共同特征&#xff0c;又要充分考慮各地、各產業的實際情況和特殊性&#xff0c;準確把握共性與個性。 總述 …

神器EasyRecovery2024中文電腦版下載!讓數據恢復不再難

在數字化時代&#xff0c;數據就是我們的財富。無論是重要的工作報告&#xff0c;還是那些珍貴的生活瞬間照片&#xff0c;或是我們與朋友間的聊天記錄&#xff0c;都儲存在我們的電腦或手機中。然而&#xff0c;有時候&#xff0c;意外總是突如其來&#xff0c;電腦突然崩潰&a…

C++Qt操作Lotus Domino數據庫 Lotus Domino C++連接Lotus Domino C++快速開發Lotus Domino

java連接domino C#連接domino python連接domino go連接domino,delphi連接domino Excel連接domino Flutter、微信小程序連接domino C 操作 Lotus Domino 數據庫&#xff1a;自動化與效率的結合 引言 在企業級應用中&#xff0c;Lotus Domino 提供了一個強大的協作平臺&#xff0…

【Linux】TCP協議【下一】{三次握手/四次揮手的深度解讀==狀態變化}

文章目錄 本篇知識需要有TCP協議【中】的知識&#xff01;詳情點擊&#x1f447;1.測試一&#xff1a;服務器start函數不定義任何行為&#xff08;不調用accept&#xff09;的三次握手狀態變化int listen(int sockfd, int backlog);的backlog參數全連接隊列當全連接隊列已滿&am…

BGP策略實驗(路徑屬性和選路規則)

要求&#xff1a; 1、使用preval策略&#xff0c;確保R4通過R2到達192.168.10.0/24 2、使用AS Path策略&#xff0c;確保R4通過R3到達192.168.11.0/24 3、配置MED策略&#xff0c;確保R4通過R3到達192.168.12.0/24 4、使用Local Preference策略&#xff0c;確保R1通過R2到達19…

Python輕松玩轉excel操作指導

目錄 一、一圖概覽 二、表格操作 三、內容操作 四、單元格操作 五、Pandas實現表格操作 六、常見場景示例 一、一圖概覽 ? ?本文主要對openpyxl庫的常用表格操作進行了梳理&#xff0c;熟練的運用后可極大地提升工作效率。 二、表格操作 #創建一個表格sheet.xlsx #…

LINQ(四) ——使用LINQ進行對象類型初始化

總目錄 C# 語法總目錄 上一篇&#xff1a;LINQ(三) ——查詢表達式/into關鍵字 LINQ 四 ——使用LINQ進行對象類型初始化 6. 使用LINQ進行對象初始化6.1 對象類型 6. 使用LINQ進行對象初始化 6.1 對象類型 需要聲明定義一個對象類&#xff0c;然后使用select 配合new關鍵字進…

C++編程揭秘:虛表機制與ABI兼容性的實例剖析

前言&#xff1a; 假設你的應用程序引用的一個庫某天更新了&#xff0c;雖然 API 和調用方式基本沒變&#xff0c;但你需要重新編譯你的應用程序才能使用這個庫&#xff0c;那么一般說這個庫是源碼兼容&#xff08;Source compatible&#xff09;&#xff1b;反之&#xff0c;如…

C語言指針相關知識(第五篇章)(非常詳細版)

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、sizeof和strlen對比二、數組之間的比較&#xff08;依據strlen和sizeof來呈現&#xff09;&#xff08;一&#xff09;、一維整型數組&#xff08;二&#…

Value-Based Reinforcement Learning(2)

Temporal Difference &#xff08;TD&#xff09; Learning 上節已經提到了如果我們有DQN&#xff0c;那么agent就知道每一步動作如何做了&#xff0c;那么DQN如何訓練那&#xff1f;這里面使用TD算法。 簡略分析&#xff1a; 是的估計 是的估計 所以&#xff1a; Deep Re…

對vue3/core源碼ref.ts文件API的認識過程

對toRef()API的認識的過程: 最開始認識toRef()是從vue3源碼中的ref.ts看見的,右側GPT已經舉了例子 然后根據例子,在控制臺輸出ref對象是什么樣子的: 這就是ref對象了,我們根據對象中有沒有__v_isRef來判斷是不是一個ref對象,當對象存在且__v_isRef true的時候他就判定為是一個…

Linux-組管理和權限管理

1 Liunx組的基本介紹&#xff1a; 在Linux中的每個用戶必須屬于一個組&#xff0c;不能獨立于組外。在Linux中每個文件都有所有者、所在組、其他組的概念 所有者所在組其它組改變用戶所在的組 2 文件/目錄的所有者 一般文件的創建者&#xff0c;誰創建了該文件&#xff0c;就…

Docker in Docker(DinD)原理與實踐

隨著云計算和容器化技術的快速發展&#xff0c;Docker作為開源的應用容器引擎&#xff0c;已經成為企業部署和管理應用程序的首選工具。然而&#xff0c;在某些場景下&#xff0c;我們可能需要在Docker容器內部再運行一個Docker環境&#xff0c;即Docker in Docker&#xff08;…