Android系统JNI实现 —(以应用层app操作gpio为例)_追风少女hrht的博客-程序员ITS203_android app jni

技术标签: framework  java  Android 驱动调试  jni  android  

前言:

本章主要讲述Android 应用层app通过jni hal的方式操作硬件节点,以gpio节点为例。

此处特别提醒,jni方法仅适用于不需要过谷歌认证(CTS)的Android设备,因为后续涉及system调用vendor的so,需要修改system/sepolicy,CTS是不允许修改此处的。

所以需要过CTS认证的建议构建一个新的hidl去实现该功能,原理大同小异,可参照 https://blog.csdn.net/weixin_41486477/article/details/110948162

一. 驱动层

这个部分不再展开讲了,根据各自需求,生成自己的节点即可。此处我们以高通的gpio节点为例,假设我们要操作的节点路径为sys/class/gpio/gpio1。

二.hal 层

目的:实现读写节点的方法,编译生成testgpio.default.so

在hardware/libhardware/include/hardware/下新建头文件testgpio.h


#include <hardware/hardware.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
#include<stdio.h>
#define testgpio_HARDWARE_MODULE_ID  "testgpio"

typedef struct testgpio_device {
    
    struct hw_device_t common;
	int fd;
    int (*set_gpio_up)(struct testgpio_device *dev, int32_t value);
	int (*set_gpio_down)(struct testgpio_device *dev, int32_t value);
	int (*testgpio_open)(struct testgpio_device *dev);
} testgpio_device_t;

typedef struct testgpio_module {
   
    struct hw_module_t common;
} testgpio_module_t;

在hardware/libhardware/modules下新建文件夹testgpio,该文件夹下包含两个文件,Android.mk控制编译,testgpio.c实现方法


#define LOG_TAG "testgpioHal"

#include <errno.h>
#include <malloc.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <log/log.h>

#include <hardware/testgpio.h>
#include <hardware/hardware.h>


int testgpio_close(struct hw_device_t* device)
{
	struct testgpio_device* ctx = (struct testgpio_device*)device;
	if (ctx) {
		free(ctx);
	}
	int fd=ctx->fd;
	close(fd);
	return 0;
}

int hal_set_gpio_up(struct testgpio_device __unused *dev,int32_t gpio_index)
{
	int ret;
	ret=write(dev->fd, "1", 1);
	if(ret<0){
		ALOGE("testgpio: write fail\n");
        return -1;
    }
	else{
		ALOGI("testgpio: set gpio %d up\n",gpio_index);
		return 0;
	}
}

int hal_set_gpio_down(struct testgpio_device __unused *dev,int32_t gpio_index)
{
	int ret;
	ret=write(dev->fd, "0", 1);
	if(ret<0){
		ALOGE("testgpio: write fail\n");
        return -1;
    }
	else{
		ALOGI("testgpio: set gpio %d down\n",gpio_index);
		return 0;
	}
}

static int hal_testgpio_open(struct testgpio_device *dev)
{
	dev->fd = open("sys/class/gpio/gpio1", O_RDWR);
	if(dev->fd== -1){
		ALOGI("testgpio: testgpiohal_open failed!!!----");
		return 0;
	}else{
		ALOGI("testgpio: testgpiohal_open sucess!!!");
		return -1;
	}
}

static int testgpio_open(const hw_module_t* module, const char __unused *id,
                            hw_device_t** device)
{
    if (device == NULL) {
        ALOGE("NULL device on open");
        return -EINVAL;
    }

    testgpio_device_t *dev = malloc(sizeof(testgpio_device_t));
    memset(dev, 0, sizeof(testgpio_device_t));

    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = 1.0;
    dev->common.module = (struct hw_module_t*) module;
    dev->common.close = testgpio_close;

    dev->set_gpio_up = hal_set_gpio_up;
	dev->set_gpio_down = hal_set_gpio_down;
	dev->testgpio_open = hal_testgpio_open;

    *device = (hw_device_t*) dev;
    return 0;
}

static struct hw_module_methods_t testgpio_module_methods = {
    .open = testgpio_open,
};

testgpio_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag                = HARDWARE_MODULE_TAG,
        .module_api_version = 1.0,
        .hal_api_version    = HARDWARE_HAL_API_VERSION,
        .id                 = testgpio_HARDWARE_MODULE_ID,
        .name               = "testgpio HAL",
        .author             = "The Android Open Source Project",
        .methods            = &testgpio_module_methods,
    },
};

Android.mk,此处主要注意加上 libhardware_headers,不然.c调用haraware下的头文件会报错。

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := testgpio.default
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_PROPRIETARY_MODULE := true

LOCAL_SRC_FILES := \
    testgpio.c \

LOCAL_HEADER_LIBRARIES := \
    libhardware_headers \

LOCAL_SHARED_LIBRARIES := \
    libcutils \
    libdl \
    liblog \
    libutils \

LOCAL_STRIP_MODULE := false
include $(BUILD_SHARED_LIBRARY)


重点提下对节点的操作,此处就是很简单的write,其实还可以利用ioctl,这个根据你驱动里的实现方法去决定,来实现不同的操作方法。

三.jni部分

添加自己的jni

frameworks/base/services/core/jni 目录下:com_android_server_TestgpioService.cpp

#define LOG_TAG "Testgpio-JNI"

#include <utils/Log.h>
#include <nativehelper/JNIHelp.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include "core_jni_helpers.h"
#include <jni.h>
#include "../../../../hardware/libhardware/include/hardware/testgpio.h"

namespace android {
	
static testgpio_device_t* testgpio_device;

static jboolean Set_gpio_up(JNIEnv* env, jobject thiz, jint value) 
{
    ALOGE("testgpioService JNI: Set_gpio_up() is invoked.");

    if (testgpio_device == NULL) {
        ALOGE("testgpioService JNI: testgpio_device was not fetched correctly.");
        return -1;
    } else {
        return testgpio_device->set_gpio_up(testgpio_device, value);
    }
}

static jboolean Set_gpio_down(JNIEnv* env, jobject thiz, jint value) 
{
    ALOGE("testgpioService JNI: Set_gpio_down() is invoked.");

    if (testgpio_device == NULL) {
        ALOGE("testgpioService JNI: testgpio_device was not fetched correctly.");
        return -1;
    } else {
        return testgpio_device->set_gpio_down(testgpio_device, value);
    }
}


static jint testgpio_init(JNIEnv *env, jobject thiz)
{
	jint err;
    hw_module_t* module;
	hw_device_t* device;

	ALOGE("testgpio,native testgpioOpen ...");

	/* 1. hw_get_module */
    err = hw_get_module(testGPIO_HARDWARE_MODULE_ID, (hw_module_t const**)&module);

    if (err == 0) {
		/* 2. get device : module->methods->open */
	    err = module->methods->open(module, NULL, &device);
	    if (err == 0) {
			/* 3. call testgpio_open */
	        testgpio_device = (testgpio_device_t *)device;
			return testgpio_device->testgpio_open(testgpio_device);
	    } else {
		ALOGE("testgpio,open faitestgpio ...");
	        return -1;
    	}
    }else{
	ALOGE("testgpio,get_module faitestgpio ...");
   }
	
    return -1;	
}

static const JNINativeMethod gMethods[] = {
    {"testgpio_init",	  	"()I",	(void *)testgpio_init },
    {"Set_gpio_up",        "(I)Z", (void *)Set_gpio_up },
    {"Set_gpio_down",       "(I)Z", (void *)Set_gpio_down },
};


int register_android_server_testgpioService(JNIEnv *env) {
    return jniRegisterNativeMethods(env, "com/android/server/testgpioService",
            gMethods, NELEM(gMethods));
}
}

在/frameworks/base/services/core/jni/Android.bp将其纳入编译

--- a/frameworks/base/services/core/jni/Android.bp
+++ b/frameworks/base/services/core/jni/Android.bp
@@ -19,6 +19,7 @@ cc_library_static {
         "BroadcastRadio/TunerCallback.cpp",
         "BroadcastRadio/convert.cpp",
         "BroadcastRadio/regions.cpp",
+        "com_android_server_TestgpioService.cpp",
         "com_android_server_AlarmManagerService.cpp",
         "com_android_server_am_BatteryStatsService.cpp",
         "com_android_server_connectivity_Vpn.cpp",

在/frameworks/base/services/core/jni/onload.cpp 注册jni

--- a/frameworks/base/services/core/jni/onload.cpp
+++ b/frameworks/base/services/core/jni/onload.cpp
@@ -54,6 +54,7 @@ int register_android_server_SyntheticPasswordManager(JNIEnv* env);
 int register_android_server_GraphicsStatsService(JNIEnv* env);
 int register_android_hardware_display_DisplayViewport(JNIEnv* env);
 int register_android_server_net_NetworkStatsService(JNIEnv* env);
+int register_android_server_TestgpioService(JNIEnv* env);
 #ifdef USE_ARC
 int register_android_server_ArcVideoService();
 #endif
@@ -104,6 +105,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
     register_android_server_GraphicsStatsService(env);
     register_android_hardware_display_DisplayViewport(env);
     register_android_server_net_NetworkStatsService(env);
+    register_android_server_TestgpioService(env);
 #ifdef USE_ARC
     register_android_server_ArcVideoService();
 #endif

此处仅说明一点,这里并不会像老的Android版本一样生成一个so库,他是一起打包进libservices.core里的,所以不用纠结是否生成so了。

四.framework层

创建service并注册,使其开机可以由system server启动

在frameworks/base/services/core/java/com/android/server

创建TestgpioService.java


package com.android.server;

import android.util.Config;
import android.util.Log;
import android.content.Context;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.IBinder;
import android.hardware.testgpio.ITestgpioService;

public final class TestgpioService extends ITestgpioService.Stub {

    public TestgpioService(Context context) {
        Log.i("TestgpioService", "Go to get Testgpio Stub...");
	    testgpio_init();
    }

    public boolean setUp(int gpio_index) {
        Log.i("TestgpioPlatform", "Testgpio Up");
	    return Set_gpio_up(gpio_index);
    }

    public boolean setDown(int gpio_index) {
        Log.i("TestgpioPlatform", "Testgpio Down");
	    return Set_gpio_down(gpio_index);
    }

    private static native int testgpio_init();
    private static native boolean Set_gpio_up(int gpio_index);
    private static native boolean Set_gpio_down(int gpio_index);
}

在frameworks/base/core/java/android/hardware/testgpio/

创建TestgpioManager.java

package android.hardware.testgpio;

import android.content.Context;
import android.os.Binder;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.Handler;
import android.os.Message;
import android.os.ServiceManager;
import android.util.Log;
import android.hardware.testgpio.ITestgpioService;
import android.content.pm.PackageManager;

public class TestgpioManager
{
    private static final String TAG = "TestgpioManager";
    private ITestgpioService mTestgpioService;
	private Context mContext;

    public TestgpioManager(Context context, ITestgpioService service) {

	    mContext = context;
        mTestgpioService = service;
    }

    public boolean TestgpioUp(int gpio_index) {
        boolean result = false;

        try {
            result = mTestgpioService.setUp(gpio_index);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in TestgpioManager.TestgpioUp:", e);
        }
        return result;
    }

    public boolean TestgpioDown(int gpio_index) {
        boolean result = false;

        try {
            result = mTestgpioService.setDown(gpio_index);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in TestgpioManager.TestgpioDown:", e);
        }
        return result;
    }
}

在frameworks/base/core/java/android/hardware/testgpio

创建ITestgpioService.aidl

package android.hardware.testgpio;

interface ITestgpioService
{
    boolean setUp(int gpio_index);
    boolean setDown(int gpio_index);
}

在frameworks/base/Android.bp将其纳入编译

--- a/frameworks/base/Android.bp
+++ b/frameworks/base/Android.bp
@@ -153,6 +153,7 @@ java_library {
         "core/java/android/hardware/fingerprint/IFingerprintService.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl",
+        "core/java/android/hardware/testgpio/ITestgpioService.aidl",
         "core/java/android/hardware/hdmi/IHdmiControlCallback.aidl",
         "core/java/android/hardware/hdmi/IHdmiControlService.aidl",
         "core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl",

以上service创建完毕,开始注册

在frameworks/base/core/java/android/content/Context.java 定义service

--- a/frameworks/base/core/java/android/content/Context.java
+++ b/frameworks/base/core/java/android/content/Context.java
@@ -3066,6 +3066,7 @@ public abstract class Context {
             //@hide: STATS_COMPANION_SERVICE,
             COMPANION_DEVICE_SERVICE,
             CROSS_PROFILE_APPS_SERVICE,
+            TESTGPIO_SERVICE,
             //@hide: SYSTEM_UPDATE_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -4164,6 +4165,8 @@ public abstract class Context {
      */
     public static final String STATS_COMPANION_SERVICE = "statscompanion";
 
+    public static final String TESTGPIO_SERVICE = "testgpio";
+
     /**
      * Use with {@link #getSystemService(String)} to retrieve an {@link android.app.StatsManager}.
      * @hide

在frameworks/base/core/java/android/app/SystemServiceRegistry.java 注册service

--- a/frameworks/base/core/java/android/app/SystemServiceRegistry.java
+++ b/frameworks/base/core/java/android/app/SystemServiceRegistry.java
@@ -60,6 +60,8 @@ import android.hardware.hdmi.IHdmiControlService;
 import android.hardware.input.InputManager;
 import android.hardware.location.ContextHubManager;
 import android.hardware.radio.RadioManager;
+import android.hardware.testgpio.TestgpioManager;
+import android.hardware.testgpio.ITestgpioService;
 import android.hardware.usb.IUsbManager;
 import android.hardware.usb.UsbManager;
 import android.location.CountryDetector;
@@ -161,7 +163,6 @@ import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.net.INetworkWatchlistManager;
 import com.android.internal.os.IDropBoxManagerService;
 import com.android.internal.policy.PhoneLayoutInflater;
-
 import java.util.HashMap;
 
 /**
@@ -563,6 +564,15 @@ final class SystemServiceRegistry {
                 return new SerialManager(ctx, ISerialManager.Stub.asInterface(b));
             }});
 
+        registerService(Context.TESTGPIO_SERVICE, TestgpioManager.class,
+                new CachedServiceFetcher<TestgpioManager>() {
+            @Override
+            public TestgpioManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+                IBinder iBinder = ServiceManager.getServiceOrThrow(Context.TESTGPIO_SERVICE);
+                ITestgpioService service = ITestgpioService.Stub.asInterface(iBinder);
+                return new TestgpioManager(ctx, service);
+            }});
+
         registerService(Context.VIBRATOR_SERVICE, Vibrator.class,
                 new CachedServiceFetcher<Vibrator>() {
             @Override

在frameworks/base/services/java/com/android/server/SystemServer.java 启动service

--- a/frameworks/base/services/java/com/android/server/SystemServer.java
+++ b/frameworks/base/services/java/com/android/server/SystemServer.java
@@ -79,6 +79,7 @@ import com.android.server.display.DisplayManagerService;
 import com.android.server.dreams.DreamManagerService;
 import com.android.server.emergency.EmergencyAffordanceService;
 import com.android.server.fingerprint.FingerprintService;
+import com.android.server.TestgpioService;
 import com.android.server.hdmi.HdmiControlService;
 import com.android.server.input.InputManagerService;
 import com.android.server.job.JobSchedulerService;
@@ -760,6 +761,7 @@ public final class SystemServer {
         NsdService serviceDiscovery= null;
         WindowManagerService wm = null;
         SerialService serial = null;
+        TestgpioService testgpioservice = null;
         NetworkTimeUpdateService networkTimeUpdater = null;
         CommonTimeManagementService commonTimeMgmtService = null;
         InputManagerService inputManager = null;
@@ -1371,6 +1373,15 @@ public final class SystemServer {
                 traceEnd();
             }
 
+            traceBeginAndSlog("StartTestgpioService");
+            try {
+                testgpioservice = new TestgpioService(context);
+                ServiceManager.addService(Context.TESTGPIO_SERVICE,testgpioservice);
+            } catch (Throwable e) {
+                Slog.e(TAG, "Failure starting TestgpioService", e);
+            }
+            traceEnd();
+
             traceBeginAndSlog("StartHardwarePropertiesManagerService");
             try {
                 hardwarePropertiesService = new HardwarePropertiesManagerService(context);

五.app层

以上修改完毕后,在out\target\common\obj\JAVA_LIBRARIES\framework_intermediates里生成classes.jar,将这个jar包放入你的Android project的app/lib中,并且Add as library......

一开始调试时,直接使用的getSystemService(Context.TESTGPIO_SERVICE),但是报错找不到TESTGPIO_SERVICE字符串,所以app中采用的反射方法获取的service

APP代码(反射方法):

package com.testgpio.testgpiotest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.hardware.testgpio.TestgpioManager;
import android.hardware.testgpio.ITestgpioService;
import android.telecom.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.os.Bundle;

import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.Toast;

import java.lang.reflect.Field;
public class MainActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {

    private CheckBox[] checkBoxes = new CheckBox[1];
    private int[] checkBoxId = {R.id.test1};
    TestgpioManager testgpioManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        for(int i = 0; i < checkBoxes.length; i++) {
            checkBoxes[i] = (CheckBox) findViewById(checkBoxId[i]);
            checkBoxes[i].setOnCheckedChangeListener(MainActivity.this);
        }

        testgpioManager = getTestgpioManager();//该函数利用反射获取server
    }

    private TestgpioManager getTestgpioManager() {
        Class mContext = Context.class;//获取class
        Field fields[] = (mContext.getDeclaredFields());//class拥有的所有属性
        for(int i = 0; i < fields.length; i++) {
            //fields[i].setAccessible(true);对于private成员使用。
            if("TESTGPIO_SERVICE".equals(fields[i].getName())){//利用成员变量名反射出自己需要的属性。
                try {
                    String testgpio_service = (String) fields[i].get(mContext);
                    Toast.makeText(getApplicationContext(), fields[i].getName()+ ":" + testgpio_service, Toast.LENGTH_SHORT).show();
                    return (TestgpioManager) getApplicationContext().getSystemService(testgpio_service);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    return null;
                }
            }
        }
        return null;
    }

    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
        if (compoundButton.getId() == R.id.test1){
            testgpioManager.TestgpioDown(1);
        }

    }
}

为了selinux权限调试,app在Android Studio编译成apk后要放在源码package/app下,利用Android.mk将其纳入系统编译,让其具有system app权限。

在你的Android app的AndroidManifest.xml中加入android:sharedUserId="android.uid.system",使其具有system权限

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.testgpio.testgpiotest"
    android:sharedUserId="android.uid.system">
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.TestgpioTest">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := TestgpioTest
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_TAGS := optional
#LOCAL_DEX_PREOPT := false
#LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE).apk
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
include $(BUILD_PREBUILT)

六.selinux权限调试

不再赘述,根据log即可。

七.调试过程中常见问题

1.jni 注册不上

检查JNINativeMethod gMethods[]数组,一般是由于第二个参数例()I设置不正确造成,参数说明参见https://blog.csdn.net/bigapple88/article/details/6756204,里面博主介绍的很详细。

static const JNINativeMethod gMethods[] = {
    {"testgpio_init",	  	"()I",	(void *)testgpio_init },
    {"Set_gpio_up",        "(I)Z", (void *)Set_gpio_up },
    {"Set_gpio_down",       "(I)Z", (void *)Set_gpio_down },
};

2.APP中使用getSystemService(Context.TESTGPIO_SERVICE),但是报错找不到TESTGPIO_SERVICE字符串

这个我一开始认为是没引用services.jar包导致,但是后来添加引用了也没什么用,至今未找到解决方法。

所以app中采用的反射方法获取的service

3.neverallow报错

hal的so默认是编译在vendor/lib下面,而jni是在framework下也就是system,应该是从Android P开始,system和vendor是不允许互相调用的,违背CTS,所以会有neverallow报错。

(1)此处可以去掉nerverallow规则,网上有很多相关介绍就不在这里说了。

(2)将hal的so放在system/lib/hw下,这样就不会有neverallow报错

当然你如果要过CTS,是不能这样做的,需要构建HIDL。

4.apk没有system权限

(1)在Android app的AndroidManifest.xml中加入android:sharedUserId="android.uid.system"

(2)将编译好的apk放在package/apps目录下(我建议是直接将apk放过去,因为移植apk源码到系统真的太太太麻烦了),添加Android.mk将其纳入编译,在mk中添加LOCAL_CERTIFICATE := platform这一行

这样编译出来的app就具有system权限,可以运行app,通过adb shell ps 查看进程,最左边一列,如下知道setting是具有system权限的。

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_41486477/article/details/110791176

智能推荐

社交3.0——可信社交Dline_区块链星际社的博客-程序员ITS203

Dline是XFS平台重要的一员,也是XFS建立元宇宙生态的第一站。Dline的目标是要建立一个人人皆可信的网络世界。“信”这个字是由“人”字旁和“言”组成,没有人,没有人言,“信”也就无从谈起。所以Dline首先要做的就是使得人人皆可言。应运而生Dline是一个去中心化社交应用,这微博和微信这样的传统中心化社交有着本质不同。微博,微信都是中心化的网络结构,围绕着中心服务器集群进行运作和传输的社交网络模式。现在,庞大的巨头公司集团成为一个无所不在的系统,存在着对用户的监视、利用、限制、欺骗和控制以

logback日志记录器讲解_Leon_Jinhai_Sun的博客-程序员ITS203

我们看一下日志记录器的支持,logback,既支持log4j,也支持logback,找到我们的pom文件,然后看一下他的注入包,在这里我们可以看到有log4j的jar包,有slf4j的,还有logback,还有log4j的都有,logback有的人可能知道他,是log4j的一个扩展,据说logback的日志记录性能,要比log4j的性能要高很多,所以logback以后有替代log4j...

linux xen 管理,Linux 中的 XEN 虚拟化技术(四)管理 Xen 虚拟系统_weixin_39888268的博客-程序员ITS203

1.使用 xm命令管理 Xen虚拟机A.显示运行状态在命令行下使用“xm list”命令显示 Xen 虚拟系统当前运行的状况[[email protected] ~]# xm listName ID Mem(MiB) VCPUs State Time(s)Domain-0 0 366 1 r----- 258.7vmrhel5 2 127 1 ------ 31.0B.连接虚拟系统使用“xm console”命令通...

java获取当前日期并格式化_在乎慧的博客-程序员ITS203

Date date = new Date();SimpleDateFormat df = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);dto.setPromiseDate(df.format(date));

浙江大华Android一面面试题_小鸡仔hana的博客-程序员ITS203

最近几天去杭电的大华校招,在一面的时候被问了很多问题,可能平时会很少碰到,但是是必须要会的知识。面试题基本问了我15道左右的题。。。。1.apk打包资源文件的时候,各种资源文件以及Java代码怎么具体怎么变化的(即apk编译过程)答:res/animator、res/anim、res/color、res/drawable(非Bitmap文件,即非.png、.9.png、.jpg、.gi

Core BlueTooth <一> CoreBluetooth Overview (概述)_安哥iOS的博客-程序员ITS203_bluetooth core specification version 5.1 feature o

CoreBluetooth Overview (概述)核心蓝牙框架可以让你的iOS和Mac应用程序与蓝牙低功耗设备进行通信。例如,您的应用程序可以发现,探索,并与低能量外围设备,如心脏速率监视器,数字恒温器,甚至其他iOS设备进行交互。  该框架是蓝牙4.0规范用于与低功耗设备使用的抽象。这就是说,它隐藏了许多从你的规范,开发商的低层细节,使其更容易为您制定与蓝牙低功耗设备进行交互

随便推点

1118 Birds in Forest(25 分)_galesaur_wcy的博客-程序员ITS203

1118 Birds in Forest(25 分)Some scientists took pictures of thousands of birds in a forest. Assume that all the birds appear in the same picture belong to the same tree. You are supposed to help the ...

RandomUtil随机数工具类_小垣的博客-程序员ITS203

package com.imooc.test;import java.util.ArrayList;import java.util.List;import java.util.Random;public class RandomUtil { /** * 在一定范围内生成给定数量的不重复随机数, 如果开始到结束的数量少于num的数量会抛出数组越界的错误,...

赵伯平--警惕台湾的企业管理垃圾!_suipingsp的博客-程序员ITS203

如同娱乐圈一样,在大陆的企业管理咨询、培训界,一批来自台湾的授业解惑者,挟先行于大陆一步,与欧美的市场经济接触而拥有的先声夺人优势,获取了相当的市场份额。他们最为活跃的领域有三:一、基于流程和制度层面的生产力促进、品质改善、精细管理等。客观地讲,这不但是台湾企业的强项,而且也是大陆企业真正需要向他们学习和跟进的。二、基于场景设计和氛围营造的励志和团队之类的培训。姑且不论其实际效果如何?

matlab仿真过程中XYGraphy 出现图像逐渐消失变空白的问题_luckylt2020的博客-程序员ITS203

针对这个问题,双击XYGraphy模块,在设置中将采样时间修改一下,默认是-1,可以改为0.01出现图像消失的现象是XYGraphy模块采样频率过高

c# 网口通讯_图像处理进阶小白的博客-程序员ITS203_c#网口通信界面

一、命令行 客户端程序:using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net;using System.Net.Sockets;using System.Threading; namespace client{ class Program { pri...

推荐文章

热门文章

相关标签