安卓串口通讯工具库封装及使用

2023-05-16

在这里插入图片描述

串口通讯

引言

对于安卓开发的小伙伴来说,很少用到串口通信开发。主要用来外接一些硬件设备,例如扫码器,读卡器,体温枪等一些硬件设备。这些设备与安卓之间通过串口来交换数据。如果有安卓串口开发这方面的需求,本文将会对你有所帮助。

1.基本概念

串口的概念: 用串行通信方式的扩展接口,指数据一位一位地顺序传送。

串口操作相关参数:

  • 1.设备名(串口地址):安卓是基于Linux系统,所以通常的串口地址为:/dev/ttyS0,/dev/ttyS1… 等。可通过linux命令查看对应的串口状态。
  • 2.波特率:串口传输速率,用来衡量数据传输的快慢,即单位时间内载波参数变化的次数。波特率与距离成反比,波特率越大传输距离相应的就越短。
  • 3.数据位:衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据往往不会是8位的,标准的值是6、7和8位。如何设置取决于你想传送的信息。
  • 4.停止位:用于表示单个包的最后一位。
  • 5.校验位:在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。

注意:

在 google 官方提供的 demo 代码基本上能够应付很多在Android设备使用串口的场景,该demo中不支持设置N,8,1。N对应校验位,8对应数据位,1对应停止位。在安卓串口操作中,不需要这三个参数。

2.串口通信 lib 准备

网上提供的代码都是通过 ndk 来编译 jni 文件生成对应的.so库。通过编写 jni 文件来打开串口,获取串口的数据流,来进行相应的读写操作。

本文通过CMake来编译 jni 文件生成对应的 so 库文件。

1.创建Android-library库。

1.首先创建:seriallibrary库

通常一些独立的功能模块,可能出现复用的情况,通常建议提取到 library 中,这样整个工程架构更加清晰明了,非常建议这样做。

在这里插入图片描述

2.配置CMake支持

在seriallibrary库的 build.gradle 中添加依赖:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FFA6XM5a-1633935046145)(image-20210317190054115.png)]

3.创建 jni 文件

在 main 文件夹下创建 jni 文件夹,并创建对应的文件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yaYkZaHs-1633935046147)(image-20210317190236684.png)]

4.编写 CMakeLists.txt 文件

这里涉及到 jni 和 cmake 编译相关知识,可以自行查阅 cmake 使用教程。

CMakeLists.txt 文件配置 如下:

cmake_minimum_required(VERSION 3.10.2)

# Declares and names the project.

project("serial_port2")

add_library( # Sets the name of the library.
             serial

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             SerialPort.c)

include_directories(src/main/jni/)

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log )

target_link_libraries( # Specifies the target library.
        serial

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib} )

在编写 jni 接口时,首先需要创建对应的 java 类及对应的 native 方法,创建 SerialPort.java文件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vXL5FH4U-1633935046148)(image-20210317191826314.png)]
SerialPort.java 类有 open 和 close 方法,用来打开和关闭串口:

package com.android.library;

import java.io.FileDescriptor;

public class SerialPort {

    /**
     * 打开串口
     * @param path       串口名称
     * @param porterRate 波特率
     * @param flags
     * @return
     */
    private native static FileDescriptor open(String path, int porterRate, int flags);

    /**
     * 关闭串口
     */
    private native void close();

    /**
     * 加载 so 库,该库名称与在 CMakeLists.txt 配置的库名相同
     */
    static {
        System.loadLibrary("serial");
    }
}

open 方法对应的全路径名称为:com.android.library.SerialPort#open

close 方法对应的全路径名称为:com.android.library.SerialPort#close

编写对应的 SerialPort.h 文件:注意 JNICALL 后的方法名不要写错,open 方法路径中的 . 对应__

com.android.library.SerialPort#open -> Java_com_android_library_SerialPort_open

com.android.library.SerialPort#close -> Java_com_android_library_SerialPort_close

SerialPort.h 文件如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com.android.library.SerialPort */
#ifndef _Included_com_android_libary_SerialPort
#ifdef __cplusplus
extern "C" {
#endif
/*
 *
 * Method:    open
 * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
 */
/**
 * Class:      com.android.library.SerialPort
 * Method:    open
 * 对应的jni接口为: com_android_library_SerialPort_open
 */
JNIEXPORT jobject JNICALL Java_com_android_library_SerialPort_open
  (JNIEnv *, jclass, jstring, jint, jint);

/**
 * Class:      com.android.library.SerialPort
 * Method:    open
 * 对应的jni接口为: com_android_library_SerialPort_close
 */
JNIEXPORT void JNICALL Java_com_android_library_SerialPort_close
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

SerialPort.c 文件如下:除了 JNICALL 方法名不同,其它都一样,来自 google 提供的串口工具 demo

#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>

#include "SerialPort.h"

#include "android/log.h"
static const char *TAG="serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)

static speed_t getBaudrate(jint baudrate)
{
	switch(baudrate) {
	case 0: return B0;
	case 50: return B50;
	case 75: return B75;
	case 110: return B110;
	case 134: return B134;
	case 150: return B150;
	case 200: return B200;
	case 300: return B300;
	case 600: return B600;
	case 1200: return B1200;
	case 1800: return B1800;
	case 2400: return B2400;
	case 4800: return B4800;
	case 9600: return B9600;
	case 19200: return B19200;
	case 38400: return B38400;
	case 57600: return B57600;
	case 115200: return B115200;
	case 230400: return B230400;
	case 460800: return B460800;
	case 500000: return B500000;
	case 576000: return B576000;
	case 921600: return B921600;
	case 1000000: return B1000000;
	case 1152000: return B1152000;
	case 1500000: return B1500000;
	case 2000000: return B2000000;
	case 2500000: return B2500000;
	case 3000000: return B3000000;
	case 3500000: return B3500000;
	case 4000000: return B4000000;
	default: return -1;
	}
}

/*
 * Class:     com.android.library.SerialPort
 * Method:    open
 * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
 */
JNIEXPORT jobject JNICALL Java_com_android_library_SerialPort_open
  (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags)
{
	int fd;
	speed_t speed;
	jobject mFileDescriptor;

	/* Check arguments */
	{
		speed = getBaudrate(baudrate);
		if (speed == -1) {
			/* TODO: throw an exception */
			LOGE("Invalid baudrate");
			return NULL;
		}
	}

	/* Opening device */
	{
		jboolean iscopy;
		const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
		LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
		fd = open(path_utf, O_RDWR | flags);
		LOGD("open() fd = %d", fd);
		(*env)->ReleaseStringUTFChars(env, path, path_utf);
		if (fd == -1)
		{
			/* Throw an exception */
			LOGE("Cannot open port");
			/* TODO: throw an exception */
			return NULL;
		}
	}

	/* Configure device */
	{
		struct termios cfg;
		LOGD("Configuring serial port");
		if (tcgetattr(fd, &cfg))
		{
			LOGE("tcgetattr() failed");
			close(fd);
			/* TODO: throw an exception */
			return NULL;
		}

		cfmakeraw(&cfg);
		cfsetispeed(&cfg, speed);
		cfsetospeed(&cfg, speed);

		if (tcsetattr(fd, TCSANOW, &cfg))
		{
			LOGE("tcsetattr() failed");
			close(fd);
			/* TODO: throw an exception */
			return NULL;
		}
	}

	/* Create a corresponding file descriptor */
	{
		jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
		jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
		jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
		mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
		(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
	}

	return mFileDescriptor;
}

/*
 * Class:     cedric_serial_SerialPort
 * Method:    close
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_android_library_SerialPort_close
  (JNIEnv *env, jobject thiz)
{
	jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
	jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");

	jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
	jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");

	jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
	jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);

	LOGD("close(fd = %d)", descriptor);
	close(descriptor);
}

3.将 seriallibrary 引入到 app 模块下

在 app 的 build.gradle 文件中引入 seriallibrary 库:

implementation project(path: ‘:seriallibrary’)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qZNYJ3ha-1633935046150)(image-20210317193457148.png)]

编译项目可以打开 debug.apk 看一下:

对应的 libserial.so文件已经打包到我们的 lib 中,当然你也可以在 CMakeLists.txt 中配置对应的参数,生成需要的 armeabi-v7a 等类型。其中 libserial.so 中的 lib 字段是系统自动添加的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-86vXW3gc-1633935046151)(image-20210317194008929.png)]

4.封装串口方法进行读写操作

java目录如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X9uxZhlw-1633935046151)(image-20210317194509119.png)]

1.SerialPort.java 类,获取 FileDescriptor 进行 串口操作。
通过 native 方法获取 FileDescriptor 进而进行 IO 流操作,详细代码:

package com.android.library;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class SerialPort {

    /**
     * Do not remove or rename the field mFd: it is used by native method close();
     */
    private FileDescriptor mFd;
    /**
     * 读数据
     */
    private FileInputStream mFileInputStream;

    /**
     * 写数据
     */
    private FileOutputStream mFileOutputStream;


    public SerialPort(File dev, int porterRate, int flags) throws IOException, SecurityException {
        mFd = open(dev.getAbsolutePath(), porterRate, flags);
        if (mFd == null) {
            throw new IOException();
        }
        mFileInputStream = new FileInputStream(mFd);
        mFileOutputStream = new FileOutputStream(mFd);
    }

    public FileInputStream getFileInputStream() {
        return mFileInputStream;
    }

    public FileOutputStream getFileOutputStream() {
        return mFileOutputStream;
    }

    public void closePort() {
        try {
            if (mFileInputStream != null) {
                mFileInputStream.close();
            }
            if (mFileOutputStream != null) {
                mFileOutputStream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        close();
    }


    /**
     * 打开串口
     * @param path       串口名称
     * @param porterRate 波特率
     * @param flags
     * @return
     */
    private native static FileDescriptor open(String path, int porterRate, int flags);

    /**
     * 关闭串口
     */
    private native void close();

    /**
     * 加载 so 库
     */
    static {
        System.loadLibrary("serial");
    }
}

2.ISerialDataListener 数据监听接口:

public interface ISerialDataListener {

    void onDataReceived(String data);
}

3.IOpenSerialListener 串口打开结果监听:

public interface IOpenSerialListener {

    void onResult(boolean success,String msg);
}

串口数据读写操作需要在子线程中执行:

1.SerialReader 读取串口数据:
网上参考的代码都是传入一个固定的 byte[] 数组来读取 InputStream 数据,会出现数据读取不全等问题。这里进行了优化。

package com.android.library;

import android.os.SystemClock;

import com.blankj.utilcode.util.ConvertUtils;

import java.io.IOException;
import java.io.InputStream;

import static com.android.library.ExecutorUtils.executors;


public class SerialReader implements Runnable {

    private final InputStream mInputStream;
    private final ISerialDataListener mReceiver;
    private boolean mIsInterrupted = false;

    public SerialReader(InputStream inputStream, ISerialDataListener receiver) {
        mInputStream = inputStream;
        mReceiver = receiver;
    }

    @Override
    public void run() {
        InputStream is = mInputStream;
        int available;
        int first;
        try {
            while (!mIsInterrupted && is != null && (first = is.read()) != -1) {
                do {
                    available = is.available();
                    SystemClock.sleep(5);
                } while (available != is.available());
                available = is.available();
                byte[] bytes = new byte[available + 1];
                is.read(bytes, 1, available);
                bytes[0] = (byte) (first & 0xFF);
                report(bytes);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }

    private void report(final byte[] data) {
        executors.execute(() -> {
            //直接用的 blankj 库 将 16 进制转成 string
            String temp = ConvertUtils.bytes2HexString(data);
            mReceiver.onDataReceived(temp);
        });
    }
}

2.SerialWriter 向串口写入数据

package com.android.library;

import com.blankj.utilcode.util.ConvertUtils;

import java.io.IOException;
import java.io.OutputStream;

import static com.android.library.ExecutorUtils.executors;


public class SerialWriter {

    private final OutputStream mOutputStream;

    public SerialWriter(OutputStream outputStream){
        mOutputStream = outputStream;
    }

    public void write(String data) {
        executors.execute(() -> {
            try {
                byte[] temp = ConvertUtils.hexString2Bytes(data);
                mOutputStream.write(temp);
                mOutputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
}

3.ExecutorUtils 线程池

package com.android.library;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 线程池工具
 */
public class ExecutorUtils {

    public final static ExecutorService executors = Executors.newCachedThreadPool();
}

4.SerialManager 单例类来管理串口操作

package com.android.library;

import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.android.library.ExecutorUtils.executors;

/**
 * author : shengping.tian
 * time   : 2021/03/10
 * desc   : 串口管理工具类
 * version: 1.0
 */
public class SerialManager implements ISerialDataListener {

    private volatile SerialPort mPort;

    private static final String TAG = "SerialManager";

    private SerialWriter mWriter;

    private List<ISerialDataListener> mListenerList = new ArrayList<>();

    private SerialManager() {

    }

    private static final SerialManager instance = new SerialManager();

    public static SerialManager getInstance() {
        return instance;
    }


    public void openSerialPort(String portName, int porterRate, IOpenSerialListener listener) {
        executors.execute(() -> {
            if (mPort == null) {
                Log.d(TAG, "first init open serial");
                try {
                    mPort = new SerialPort(new File(portName), porterRate, 0);
                    mWriter = new SerialWriter(mPort.getFileOutputStream());
                    executors.execute(new SerialReader(mPort.getFileInputStream(), this::onDataReceived));
                } catch (IOException e) {
                    e.printStackTrace();
                    listener.onResult(false, e.getMessage());
                }
            } else {
                Log.d(TAG, "serial has opened");
            }
            listener.onResult(true, "");
        });
    }

    public void closeSerialPort() {
        if (mPort != null) {
            Log.d(TAG, "close serial");
            mPort.closePort();
            mPort = null;
        } else {
            Log.d(TAG, "serial is has closed");
        }
    }

    public void write(String data){
        if(mPort==null){
            Log.e(TAG,"serial not opened,please check serial.");
            return;
        }
        if(mPort.getFileOutputStream() == null){
            Log.e(TAG,"serial outPutStream is null,please check seria.");
            return;
        }
        mWriter.write(data);
    }

    public synchronized void registerListener(ISerialDataListener listener){
        mListenerList.add(listener);
    }

    public synchronized void unRegisterListener(ISerialDataListener listener){
        mListenerList.remove(listener);
    }


    @Override
    public void onDataReceived(String data) {
        synchronized (this){
            for (ISerialDataListener listener : mListenerList) {
                listener.onDataReceived(data);
            }
        }
    }
}

5.在app中应用

直接在 app 的 activity 中 通过 SerialManager 获取数据:

package com.android.serialcomdemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.android.library.IOpenSerialListener;
import com.android.library.ISerialDataListener;
import com.android.library.SerialManager;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private boolean serialIsOpen = false;

    private TextView tv_show;
    private EditText input;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv_show = findViewById(R.id.tv_result);
        input = findViewById(R.id.ed_send);
    }

    /**
     * 获取串口数据
     */
    private ISerialDataListener mListener = new ISerialDataListener() {
        @Override
        public void onDataReceived(String data) {
            // TODO: 2021/3/10 这里获取串口数据
            runOnUiThread(() -> {
                tv_show.setText(data);
            });
        }
    };
    /**
     * 打开串口
     */
    private void openSerial() {
        SerialManager.getInstance().openSerialPort("/dev/ttyS1", 9600, new IOpenSerialListener() {
            @Override
            public void onResult(boolean success, String msg) {
                serialIsOpen = success;
                if (success) {
                    Log.i(TAG, "open serial success ");
                    SerialManager.getInstance().registerListener(mListener);
                } else {
                    Log.e(TAG, "open serial failed " + msg);
                }
            }
        });
    }


    public void open(View view) {
        if (!serialIsOpen) {
            openSerial();
        } else {
            Toast.makeText(this, "serial has opened", Toast.LENGTH_SHORT).show();
        }
    }

    public void send(View view) {
        if (serialIsOpen) {
            String data = input.getText().toString().trim();
            SerialManager.getInstance().write(data);
        } else {
            Toast.makeText(this, "serial not opened", Toast.LENGTH_SHORT).show();
        }
    }

    public void close(View view) {
        if (serialIsOpen) {
            SerialManager.getInstance().closeSerialPort();
        } else {
            Toast.makeText(this, "serial not opened", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        SerialManager.getInstance().unRegisterListener(mListener);
    }
}

6.结语

以上就是安卓进行串口通信的代码,经过一定的封装之后,使用起来还是很便捷。

当然,如果你的需求是多个应用需要与该设备进行串口通信,则需要考虑对该 library 进行封装,通过 AIDL 进程间通信的方法,对方提供统一的访问服务。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

安卓串口通讯工具库封装及使用 的相关文章

  • 无人机仿真XTDrone学习四:XTDrone键盘控制无人机程序分析(MAVRos)一

    在XTDrone安装完成后 xff0c 运行一个键盘控制无人机程序测试XTDrone安装是否存在问题 通过分析该例程 xff0c 理解ROS对无人机的控制方法与控制过程 XTDrone键盘控制无人机例程仿真 XTDrone键盘控制无人机例程
  • 无人机仿真XTDrone学习五:XTDrone键盘控制无人机indoor1.launch程序分析二

    launch程序如下 这个文件启动了Gazebo仿真环境 xff0c 配置MAVROS功能包和PX4 SITL功能包 span class token prolog lt xml version 61 34 1 0 34 gt span s
  • 接收机灵敏度的计算公式推导和分析

    接收机灵敏度定义的接收机能够接收到的并且还能正常工作的最低电平强度 接收机灵敏度跟很多东西有关 xff0c 如噪声系数 信号带宽 解调信噪比等 xff0c 灵敏度一般来说越高 xff08 数值越低 xff09 xff0c 说明其接收微弱信号
  • 无人机仿真XTDrone学习六:XTDrone键盘控制无人机multirotor_communication.py程序分析三

    multirotor communication py程序 这个程序是实现对无人机控制的主要程序 xff0c 需要重点分析 可以实现对无人机的位置 xff0c 速度 xff0c 加速度的控制 主要节点 xff1a sys span clas
  • 无人机仿真XTDrone学习七:XTDrone键盘控制无人机 multirotor_keyboard_control.py程序分析四

    multirotor keyboard control py 该程序可以通过键盘对一个或者多个无人机进行速度或者加速度的控制并更改无人机或领导者无人机飞行状态 这个脚本通过发布状态命令和速度命令话题与通讯脚本进行通讯 xff0c 通讯脚本进
  • XTDrone仿真平台与Prometheus仿真平台

    常见的无人机仿真平台大都是用于对底层飞控算法的仿真与调试 xff0c XTDrone仿真平台与Prometheus仿真平台可以针对无人机上层算法进行仿真或者进行编队仿真 xff0c 这两者都采用ROS 43 PX4 43 Gazebo开源平
  • ROS中功能包二进制安装改为源码安装

    在学习XTDrone的二维路径规划时需要使用navigation功能包 xff0c 使用文档安装方式为二进制安装 sudo apt install span class token operator span y ros span clas
  • 2016晚安 2017你好

    不知不觉开通CSDN账号已有三年多的时间 xff0c 三年多以前抱着学习坚持的态度想要在CSDN上记录自己学习的点滴 结果三年多过去了 xff0c 2016年也随着过去了 xff0c 回顾2016年主要的三件事情就是 xff1a 1 从大学
  • Python入门学习--环境配置

    工作将近两年了 xff0c 做过B S结构的项目 xff0c 也做过android xff0c 也做过C S结构的项目 xff0c 相信无论是那种项目都是基于Java 学习运用Java也已经好多年了 xff0c 虽然也接触过C C 43 4
  • Python入门学习-数据类型

    一 类型的概念 首先 xff0c 对于一个数据1011100 xff0c 改怎么解释呢 xff1f 同Java等变成语言类型 xff0c 首先我们要明确数据的类型 xff0c 程序设计语言不允许语法歧义 xff0c 因此需要明确数据的类型
  • 流年似水 启航2019

    凌晨1点无意间看到一个演讲视频 感谢你给我机会上场 xff0c 很久之前的一个演讲视频 xff0c 看完除了羡慕还是羡慕吧 xff0c 也许就是一句话吧 xff0c 开挂的人生不需要解释 30多年的生活他做了很多事 xff0c 也做成了很多
  • Matplotlib 可视化必备神书,附pdf下载

    出品 xff1a Python数据之道 大家好 xff0c 我是阳哥 大家知道 xff0c 在利用Python进行数据可视化过程中 xff0c 基本上是很难绕开 Matplotlib 的 xff0c 因为 不少其他的可视化库多多少少是建立在
  • ubuntu学习笔记02

    1 sudo sh 与sudo bash sh区别 以超级用户身份运行 34 sh 34 xff0c sh实用程序是一个命令语言解释器 以超级用户身份运行 34 bash 34 xff0c Bash是shell或命令语言解释器 xff0c
  • 史上最浅显易懂的Git教程!

    从零起步的Git教程 xff0c 让你无痛苦上手世界上最流行的分布式版本控制系统Git xff01 既然号称史上最浅显易懂的Git教程 xff0c 那这个教程有什么让你怦然心动的特点呢 xff1f 首先 xff0c 本教程绝对面向初学者 x
  • 数据库死锁原因及解决办法

    死锁 xff08 Deadlock xff09 所谓死锁 xff1a 是指两个或两个以上的进程在执行过程中 xff0c 因争夺资源而造成的一种互相等待的现象 xff0c 若无外力作用 xff0c 它们都将无法推进下去 此时称系统处于死锁状态
  • spring和springmvc父子容器的关系

    大家都知道 xff0c 在spring的配置中要分开配置service层的注解扫描 xff0c 以及springmvc变现层的注解扫描 xff0c 如下 xff1a lt 扫描加载Service实现类 gt lt context compo
  • pageHelper分页插件实现原理及使用方法

    插件官方网站 xff1a https github com pagehelper Mybatis PageHelper tree master src main java com github pagehelper 实现原理 xff1a 使
  • 虚拟机Linux系统安装nginx服务器并启动的步骤

    工作前的准备 xff1a 1 装有Linux的虚拟机 2 nginx安装包 xff0c 注意是gz结尾的压缩文件 具体步骤1 xff1a 1 nginx安装环境 nginx是 C 语言开发 xff0c 建议在 linux 上运行 xff0c
  • 什么是反射机制,有什么作用

    1 反射机制定义 反射的概念是由Smith在1982年首次提出的 xff0c 主要是指程序可以访问 检测和修改其本身状态或行为的一种能力 在Java环境中 xff0c 反射机制允许程序在执行时获取某个类自身的定义信息 xff0c 例如熟悉和
  • 写给2016

    你不能期待着遇见怎样的自己 xff0c 但你可以选择成为怎样的自己 转眼16年就迎来了它的落幕 xff0c 不论怎样华丽的开场 xff0c 总有归于平静散场的结束 xff0c 不早不晚 xff0c 于清晨到傍晚 xff0c 于四季的轮回 x

随机推荐

  • 模块化建立项目流程(Maven聚合模块)

    先说项目使用Maven的好处 1 项目构建 Maven定义了软件开发的整套流程体系 xff0c 并进行了封装 xff0c 开发人员只需要指定项目的构建流程 xff0c 无需针对每个流程编写自己的构建脚本 2 依赖管理 除了项目构建 xff0
  • 如何在linux下判断web服务是否开启?

    对于web服务的开启的判断有以下几种常用方法 xff1a 1 端口查看 xff1a 本地 xff1a ss xff0c netstat xff0c lsof 1 2 3 4 5 6 7 8 9 10
  • git基本命令

    最近再写一些项目上传到github xff0c 所以要用到git命令 本地需要先安装git客户端 xff0c 然后指定一个git地址为本地仓库 然后右键git bash here打开git命令界面 首先服务端需要创建一个项目以便clone到
  • jps查看Java线程,jstack查看具体线程堆状态

    想要使用jps需要配置环境变量 xff0c 在classpath后在加一个指定Java bin目录 具体命令如下 t2挂起了 xff0c 堆里面显示t2为RUNNABLE xff0c suspend xff0c resume废弃使用 IBM
  • heap_1.c详解--------FreeRTOS内存管理

    heap 1源码分析 include lt stdlib h gt Defining MPU WRAPPERS INCLUDED FROM API FILE prevents task h from redefining all the A
  • 记录一个类加载变量引发的问题

    类加载变量导致的问题 类加载变量导致的问题 类加载变量导致的问题 因为项目需要 xff0c 银行要求使用weblogic部署并且启动所有项目 xff0c 不允许项目单独开服务启动一般都有这样的要求 xff0c 我所在的项目组有两个单独mai
  • Ubuntu16.04安装intel RealSense D435i驱动并在ROS中使用

    参考 xff1a https blog csdn net qq 43265072 article details 106437287https blog csdn net zhangfenger article details 849980
  • 【ROS】的单线程Spinning和多线程Spinning

    参考 xff1a https www cnblogs com feixiao5566 p 5288206 htmlhttps www freesion com article 9499126134 https blog csdn net y
  • Intel RealSense D435i 深度相机介绍

    参考 xff1a https www sohu com a 340984033 715754https www chiphell com thread 1945054 1 1 htmlhttps blog csdn net cherry y
  • 【ROS】rosnode信息命令

    参考 xff1a https www cnblogs com kay2018 p 10314741 html 一 概述 ROS信息命令用于识别话题 服务 节点和参数等信息 尤其是rostopic rosservice rosnode和ros
  • 【ROS】launch文件详解

    参考 xff1a https www cnblogs com fuzhuoxin p 12588402 html 在节点少 xff0c 程序小的情况下可以一个一个节点来启动 xff0c 测试运行效果 xff1b 但是当工程规模大 xff0c
  • uC/OS-II的任务同步与通信

    在多任务合作过程中的 xff0c 操作系统应解决两个问题 xff1a 一是各任务之间应具有一种互斥关系 xff0c 即对于某个共享资源的共享 xff0c 如果一个任务正在使用 xff0c 则其他任务只能等待 xff0c 等到该任务释放该资源
  • Jetson Xavier NX系统烧录(使用NVIDIA SDK Manager)

    目录 一 在host主机下安装NVIDIA SDK Manager 二 安装系统镜像 三 设置SSD为系统启动项 四 安装CUDA等环境的包 注意 xff1a 本文使用的是国产开发套件 xff0c 不支持SD卡镜像文件烧录 一 在host主
  • 使用Spring Security,在登陆页面没有跳转的问题。(ssm)

    首先检查有没有在spring security xml中配置成功页面和失败页面 其次判断web xml中是否引用了spring security xml文件 最后 xff0c 如果上两步都没问题 xff0c 删去spring securit
  • for 循环嵌套性能的比较

    有人对着汇编语言不够一屑 xff0c 认为那已经是古老的低级语言 xff0c 是当今的非主流语言 xff0c 学了也不知道有什么用 是的 xff0c 我们不得不承认 xff0c 作为一门古老的语言 xff0c 汇编已经完成了历史赋予它的使命
  • 同一个单片机系统要做到共地

    同一系统可以不同的电源供电 xff0c 但是要做到共地 原因 xff1a 不同的仪器要连接成一个电气系统 共地就是建立一个共同的电位参考点 否则没有标准如何能够测量 同时 公共地一般也是接大地的地线 仪器外壳接地可以消除干扰信号
  • STM32串口下载程序

    STM32 串口下载程序 引言 xff1a 如果我们用下载器下载程序很快 xff0c 很方便 xff0c 但是需要购买下载器 xff0c 很破费 为此我们用串口 下载程序 xff0c 省去了购买下载器的麻烦 下面介绍用串口下载程序的方法 x
  • Python实现微信自动回复

    先安装 itchat requests itchat uos itchat uos主要解决微信提示禁止网页登录导致登录失败的问题 以下有三种可玩方式 xff1a 1 回复好友 源代码如下 xff1a wechat autoreply imp
  • linux屏幕录像

    安装 sudo apt get install kazam 功能 屏幕录像 屏幕截图 区域 全屏 窗口 自定义区域 音频 光标 扬声器 麦克风 配置 帧率 录音源 保存位置
  • 安卓串口通讯工具库封装及使用

    串口通讯 引言 对于安卓开发的小伙伴来说 xff0c 很少用到串口通信开发 主要用来外接一些硬件设备 xff0c 例如扫码器 xff0c 读卡器 xff0c 体温枪等一些硬件设备 这些设备与安卓之间通过串口来交换数据 如果有安卓串口开发这方