Android Ble蓝牙App(三)特性和属性

Ble蓝牙App(三)特性使用

  • 前言
  • 正文
    • 一、获取属性列表
    • 二、属性适配器
    • 三、获取特性名称
    • 四、特性适配器
    • 五、加载特性
    • 六、显示特性和属性
    • 七、源码

前言

  在上一篇中我们完成了连接和发现服务两个动作,那么再发现服务之后要做什么呢?发现服务只是让你知道设备有什么服务,可以做什么事情。

在这里插入图片描述

正文

  本篇要做的是显示服务下的特性,首先我们了解一下特性的基本知识。在蓝牙低功耗(BLE)中,特性(Characteristic)是蓝牙设备提供的一种数据单元,用于描述设备的某个属性或功能。特性包含了一系列的属性和值,可以用于读取、写入和通知数据。

BLE特性相关的关键概念和说明:

  1. UUID(Universally Unique Identifier):每个特性都会有一个唯一的UUID,用于标识该特性。
  2. 值(Value):特性包含一个值,可以是字节数组、字符串或其他数据类型。该值代表特性的当前状态或数据内容。
  3. 属性(Properties):特性具有一组属性,包括读、写、通知等。属性决定了可以对特性进行哪些操作。
  4. 读(Read):允许外部设备从特性中读取当前的值。
  5. 写(Write):允许外部设备向特性写入一个新的值。
  6. 通知(Notify):当特性的值发生变化时,可以通过通知方式将新的值发送给订阅该特性的外部设备。
  7. 描述符(Descriptor):特性可以附带一个或多个描述符,用于提供关于特性的额外信息或配置。

  使用BLE特性,可以实现各种功能和数据交互,例如传感器数据的读取、设备状态的监控、远程控制等。特性的读写和通知操作可以通过与蓝牙设备的交互来实现。需要注意的是,BLE特性的操作和功能是由设备的厂商定义的,并在设备的GATT(Generic Attribute Profile)配置文件中进行描述。

  首先理清一下思路,我们现在知道服务下面有特性,特性下面有一些属性值,其中属性(Properties)尤为重要,因为它决定了你的特性可以进行那些操作。用一个图来说明服务,特性,属性之间的关系。

在这里插入图片描述

一、获取属性列表

下面我们先获取最下面的属性,这是一个列表,属性值的处理有一些不同,首先我们在BleUtils中增加一个函数,代码如下所示:

    /**
     * 获取属性
     */
    fun getProperties(property: Int): List<String> {
        val properties: MutableList<String> = ArrayList()
        for (i in 0..7) {
            when (property and (1 shl i)) {
                0x01 -> properties.add("Broadcast")
                0x02 -> properties.add("Read")
                0x04 -> properties.add("Write No Response")
                0x08 -> properties.add("Write")
                0x10 -> properties.add("Notify")
                0x20 -> properties.add("Indicate")
                0x40 -> properties.add("Authenticated Signed Writes")
                0x80 -> properties.add("Extended Properties")
            }
        }
        return properties
    }

  这里是通过位运算进行计算属性的值,首先是循环遍历,shl 是一种位运算符,用于执行按位左移操作。shl 是 “shift left” 的缩写。and用于执行按位与操作。先左移再按位与,得到最终的值,根据值得到属性描述,这些描述就是具体的功能操作。会返回一个属性列表,有了列表我们就可以写一个适配器了。

二、属性适配器

首先我们在layout下创建一个item_property.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tv_property"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginEnd="8dp"
    android:text="property"
    android:textColor="@color/orange" />

  因为是String类型,所以我们就直接用一个TextView显示即可,下面我们写适配器,在adapter包下新建一个PropertyAdapter类,代码如下所示:

class PropertyAdapter(
    private val properties: List<String>,
    private val listener: OnItemClickListener
) : RecyclerView.Adapter<PropertyAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(ItemPropertyBinding.inflate(LayoutInflater.from(parent.context), parent, false)).apply {
            binding.tvProperty.setOnClickListener { v -> listener.onItemClick(v, adapterPosition) }
        }
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.binding.tvProperty.text = properties[position]
    }

    override fun getItemCount() = properties.size

    class ViewHolder(itemView: ItemPropertyBinding) : RecyclerView.ViewHolder(itemView.root) {
        var binding: ItemPropertyBinding

        init {
            binding = itemView
        }
    }
}

这里进行了属性的点击监听,我们可以回调到特性适配器中去处理,下面我们要处理的就是特性了。

三、获取特性名称

  首先是特性名称,同样是根据UUID,同样是那个PDF文档,在BleUtils中增加一个getCharacteristicsName()函数,代码有点多,如下所示:

    /**
     * 获取特性名称
     * @param uuid UUID
     */
    fun getCharacteristicsName(uuid: UUID) =
        when ("0x${uuid.toString().substring(4, 8).uppercase(Locale.getDefault())}") {
            "0x2A00" -> "Device Name"
            "0x2A01" -> "Appearance"
            "0x2A02" -> "Peripheral Privacy Flag"
            "0x2A03" -> "Reconnection Address"
            "0x2A04" -> "Peripheral Preferred Connection Parameters"
            "0x2A05" -> "Service Changed"
            "0x2A06" -> "Alert Level"
            "0x2A07" -> "Tx Power Level"
            "0x2A08" -> "Date Time"
            "0x2A09" -> "Day of Week"
            "0x2A0A" -> "Day Date Time"
            "0x2A0C" -> "Exact Time 256"
            "0x2A0D" -> "DST Offset"
            "0x2A0E" -> "Time Zone"
            "0x2A0F" -> "Local Time Information"
            "0x2A11" -> "Time with DST"
            "0x2A12" -> "Time Accuracy"
            "0x2A13" -> "Time Source"
            "0x2A14" -> "Reference Time Information"
            "0x2A16" -> "Time Update Control Point"
            "0x2A17" -> "Time Update State"
            "0x2A18" -> "Glucose Measurement"
            "0x2A19" -> "Battery Level"
            "0x2A1C" -> "Temperature Measurement"
            "0x2A1D" -> "Temperature Type"
            "0x2A1E" -> "Intermediate Temperature"
            "0x2A21" -> "Measurement Interval"
            "0x2A22" -> "Boot Keyboard Input Report"
            "0x2A23" -> "System ID"
            "0x2A24" -> "Model Number String"
            "0x2A25" -> "Serial Number String"
            "0x2A26" -> "Firmware Revision String"
            "0x2A27" -> "Hardware Revision String"
            "0x2A28" -> "Software Revision String"
            "0x2A29" -> "Manufacturer Name String"
            "0x2A2A" -> "IEEE 11073-20601 Regulatory Certification Data List"
            "0x2A2B" -> "Current Time"
            "0x2A2C" -> "Magnetic Declination"
            "0x2A31" -> "Scan Refresh"
            "0x2A32" -> "Boot Keyboard Output Report"
            "0x2A33" -> "Boot Mouse Input Report"
            "0x2A34" -> "Glucose Measurement Context"
            "0x2A35" -> "Blood Pressure Measurement"
            "0x2A36" -> "Intermediate Cuff Pressure"
            "0x2A37" -> "Heart Rate Measurement"
            "0x2A38" -> "Body Sensor Location"
            "0x2A39" -> "Heart Rate Control Point"
            "0x2A3F" -> "Alert Status"
            "0x2A40" -> "Ringer Control Point"
            "0x2A41" -> "Ringer Setting"
            "0x2A42" -> "Alert Category ID Bit Mask"
            "0x2A43" -> "Alert Category ID"
            "0x2A44" -> "Alert Notification Control Point"
            "0x2A45" -> "Unread Alert Status"
            "0x2A46" -> "New Alert"
            "0x2A47" -> "Supported New Alert Category"
            "0x2A48" -> "Supported Unread Alert Category"
            "0x2A49" -> "Blood Pressure Feature"
            "0x2A4A" -> "HID Information"
            "0x2A4B" -> "Report Map"
            "0x2A4C" -> "HID Control Point"
            "0x2A4D" -> "Report"
            "0x2A4E" -> "Protocol Mode"
            "0x2A4F" -> "Scan Interval Window"
            "0x2A50" -> "PnP ID"
            "0x2A51" -> "Glucose Feature"
            "0x2A52" -> "Record Access Control Point"
            "0x2A53" -> "RSC Measurement"
            "0x2A54" -> "RSC Feature"
            "0x2A55" -> "SC Control Point"
            "0x2A5A" -> "Aggregate"
            "0x2A5B" -> "CSC Measurement"
            "0x2A5C" -> "CSC Feature"
            "0x2A5D" -> "Sensor Location"
            "0x2A5E" -> "PLX Spot-Check Measurement"
            "0x2A5F" -> "PLX Continuous Measurement"
            "0x2A60" -> "PLX Features"
            "0x2A63" -> "Cycling Power Measurement"
            "0x2A64" -> "Cycling Power Vector"
            "0x2A65" -> "Cycling Power Feature"
            "0x2A66" -> "Cycling Power Control Point"
            "0x2A67" -> "Location and Speed"
            "0x2A68" -> "Navigation"
            "0x2A69" -> "Position Quality"
            "0x2A6A" -> "LN Feature"
            "0x2A6B" -> "LN Control Point"
            "0x2A6C" -> "Elevation"
            "0x2A6D" -> "Pressure"
            "0x2A6E" -> "Temperature"
            "0x2A6F" -> "Humidity"
            "0x2A70" -> "True Wind Speed"
            "0x2A71" -> "True Wind Direction"
            "0x2A72" -> "Apparent Wind Speed"
            "0x2A73" -> "Apparent Wind Direction"
            "0x2A74" -> "Gust Factor"
            "0x2A75" -> "Pollen Concentration"
            "0x2A76" -> "UV Index"
            "0x2A77" -> "Irradiance"
            "0x2A78" -> "Rainfall"
            "0x2A79" -> "Wind Chill"
            "0x2A7A" -> "Heat Index"
            "0x2A7B" -> "Dew Point"
            "0x2A7D" -> "Descriptor Value Changed"
            "0x2A7E" -> "Aerobic Heart Rate Lower Limit"
            "0x2A7F" -> "Aerobic Threshold"
            "0x2A80" -> "Age"
            "0x2A81" -> "Anaerobic Heart Rate Lower Limit"
            "0x2A82" -> "Anaerobic Heart Rate Upper Limit"
            "0x2A83" -> "Anaerobic Threshold"
            "0x2A84" -> "Aerobic Heart Rate Upper Limit"
            "0x2A85" -> "Date of Birth"
            "0x2A86" -> "Date of Threshold Assessment"
            "0x2A87" -> "Email Address"
            "0x2A88" -> "Fat Burn Heart Rate Lower Limit"
            "0x2A89" -> "Fat Burn Heart Rate Upper Limit"
            "0x2A8A" -> "First Name"
            "0x2A8B" -> "Five Zone Heart Rate Limits"
            "0x2A8C" -> "Gender"
            "0x2A8D" -> "Heart Rate Max"
            "0x2A8E" -> "Height"
            "0x2A8F" -> "Hip Circumference"
            "0x2A90" -> "Last Name"
            "0x2A91" -> "Maximum Recommended Heart Rate"
            "0x2A92" -> "Resting Heart Rate"
            "0x2A93" -> "Sport Type for Aerobic and Anaerobic Thresholds"
            "0x2A94" -> "Three Zone Heart Rate Limits"
            "0x2A95" -> "Two Zone Heart Rate Limits"
            "0x2A96" -> "VO2 Max"
            "0x2A97" -> "Waist Circumference"
            "0x2A98" -> "Weight"
            "0x2A99" -> "Database Change Increment"
            "0x2A9A" -> "User Index"
            "0x2A9B" -> "Body Composition Feature"
            "0x2A9C" -> "Body Composition Measurement"
            "0x2A9D" -> "Weight Measurement"
            "0x2A9E" -> "Weight Scale Feature"
            "0x2A9F" -> "User Control Point"
            "0x2AA0" -> "Magnetic Flux Density - 2D"
            "0x2AA1" -> "Magnetic Flux Density - 3D"
            "0x2AA2" -> "Language"
            "0x2AA3" -> "Barometric Pressure Trend"
            "0x2AA4" -> "Bond Management Control Point"
            "0x2AA5" -> "Bond Management Feature"
            "0x2AA6" -> "Central Address Resolution"
            "0x2AA7" -> "CGM Measurement"
            "0x2AA8" -> "CGM Feature"
            "0x2AA9" -> "CGM Status"
            "0x2AAA" -> "CGM Session Start Time"
            "0x2AAB" -> "CGM Session Run Time"
            "0x2AAC" -> "CGM Specific Ops Control Point"
            "0x2AAD" -> "Indoor Positioning Configuration"
            "0x2AAE" -> "Latitude"
            "0x2AAF" -> "Longitude"
            "0x2AB0" -> "Local North Coordinate"
            "0x2AB1" -> "Local East Coordinate"
            "0x2AB2" -> "Floor Number"
            "0x2AB3" -> "Altitude"
            "0x2AB4" -> "Uncertainty"
            "0x2AB5" -> "Location Name"
            "0x2AB6" -> "URI"
            "0x2AB7" -> "HTTP Headers"
            "0x2AB8" -> "HTTP Status Code"
            "0x2AB9" -> "HTTP Entity Body"
            "0x2ABA" -> "HTTP Control Point"
            "0x2ABB" -> "HTTPS Security"
            "0x2ABC" -> "TDS Control Point"
            "0x2ABD" -> "OTS Feature"
            "0x2ABE" -> "Object Name"
            "0x2ABF" -> "Object Type"
            "0x2AC0" -> "Object Size"
            "0x2AC1" -> "Object First -Created"
            "0x2AC2" -> "Object Last - Modified"
            "0x2AC3" -> "Object ID"
            "0x2AC4" -> "Object Properties"
            "0x2AC5" -> "Object Action Control Point"
            "0x2AC6" -> "Object List Control Point"
            "0x2AC7" -> "Object List Filter"
            "0x2AC8" -> "Object Changed"
            "0x2AC9" -> "Resolvable Private Address Only"
            "0x2ACC" -> "Fitness Machine Feature"
            "0x2ACD" -> "Treadmill Data"
            "0x2ACE" -> "Cross Trainer Data"
            "0x2ACF" -> "Step Climber Data"
            "0x2AD0" -> "Stair Climber Data"
            "0x2AD1" -> "Rower Data"
            "0x2AD2" -> "Indoor Bike Data"
            "0x2AD3" -> "Training Status"
            "0x2AD4" -> "Supported Speed Range"
            "0x2AD5" -> "Supported Inclination Range"
            "0x2AD6" -> "Supported Resistance Level Range"
            "0x2AD7" -> "Supported Heart Rate Range"
            "0x2AD8" -> "Supported Power Range"
            "0x2AD9" -> "Fitness Machine Control Point"
            "0x2ADA" -> "Fitness Machine Status"
            "0x2ADB" -> "Mesh Provisioning Data In"
            "0x2ADC" -> "Mesh Provisioning Data Out"
            "0x2ADD" -> "Mesh Proxy Data In"
            "0x2ADE" -> "Mesh Proxy Data Out"
            "0x2AE0" -> "Average Current"
            "0x2AE1" -> "Average Voltage"
            "0x2AE2" -> "Boolean"
            "0x2AE3" -> "Chromatic Distance from Planckian"
            "0x2AE4" -> "Chromaticity Coordinates"
            "0x2AE5" -> "Chromaticity in CCT and Duv Values"
            "0x2AE6" -> "Chromaticity Tolerance"
            "0x2AE7" -> "CIE 13.3 - 1995 Color Rendering Index"
            "0x2AE8" -> "Coefficient"
            "0x2AE9" -> "Correlated Color Temperature"
            "0x2AEA" -> "Count 16"
            "0x2AEB" -> "Count 24"
            "0x2AEC" -> "Country Code"
            "0x2AED" -> "Date UTC"
            "0x2AEE" -> "Electric Current"
            "0x2AEF" -> "Electric Current Range"
            "0x2AF0" -> "Electric Current Specification"
            "0x2AF1" -> "Electric Current Statistics"
            "0x2AF2" -> "Energy"
            "0x2AF3" -> "Energy in a Period of Day"
            "0x2AF4" -> "Event Statistics"
            "0x2AF5" -> "Fixed String 16"
            "0x2AF6" -> "Fixed String 24"
            "0x2AF7" -> "Fixed String 36"
            "0x2AF8" -> "Fixed String 8"
            "0x2AF9" -> "Generic Level"
            "0x2AFA" -> "Global Trade Item Number"
            "0x2AFB" -> "Illuminance"
            "0x2AFC" -> "Luminous Efficacy"
            "0x2AFD" -> "Luminous Energy"
            "0x2AFE" -> "Luminous Exposure"
            "0x2AFF" -> "Luminous Flux"
            "0x2B00" -> "Luminous Flux Range"
            "0x2B01" -> "Luminous Intensity"
            "0x2B02" -> "Mass Flow"
            "0x2B03" -> "Perceived Lightness"
            "0x2B04" -> "Percentage 8"
            "0x2B05" -> "Power"
            "0x2B06" -> "Power Specification"
            "0x2B07" -> "Relative Runtime in a Current Range"
            "0x2B08" -> "Relative Runtime in a Generic Level Range"
            "0x2B09" -> "Relative Value in a Voltage Range"
            "0x2B0A" -> "Relative Value in an Illuminance Range"
            "0x2B0B" -> "Relative Value in a Period of Day"
            "0x2B0C" -> "Relative Value in a Temperature Range"
            "0x2B0D" -> "Temperature 8"
            "0x2B0E" -> "Temperature 8 in a Period of Day"
            "0x2B0F" -> "Temperature 8 Statistics"
            "0x2B10" -> "Temperature Range"
            "0x2B11" -> "Temperature Statistics"
            "0x2B12" -> "Time Decihour 8"
            "0x2B13" -> "Time Exponential 8"
            "0x2B14" -> "Time Hour 24"
            "0x2B15" -> "Time Millisecond 24"
            "0x2B16" -> "Time Second 16"
            "0x2B17" -> "Time Second 8"
            "0x2B18" -> "Voltage"
            "0x2B19" -> "Voltage Specification"
            "0x2B1A" -> "Voltage Statistics"
            "0x2B1B" -> "Volume Flow"
            "0x2B1C" -> "Chromaticity Coordinate"
            "0x2B1D" -> "RC Feature"
            "0x2B1E" -> "RC Settings"
            "0x2B1F" -> "Reconnection Configuration Control Point"
            "0x2B20" -> "IDD Status Changed"
            "0x2B21" -> "IDD Status"
            "0x2B22" -> "IDD Annunciation Status"
            "0x2B23" -> "IDD Features"
            "0x2B24" -> "IDD Status Reader Control Point"
            "0x2B25" -> "IDD Command Control Point"
            "0x2B26" -> "IDD Command Data"
            "0x2B27" -> "IDD Record Access Control Point"
            "0x2B28" -> "IDD History Data"
            "0x2B29" -> "Client Supported Features"
            "0x2B2A" -> "Database Hash"
            "0x2B2B" -> "BSS Control Point"
            "0x2B2C" -> "BSS Response"
            "0x2B2D" -> "Emergency ID"
            "0x2B2E" -> "Emergency Text"
            "0x2B2F" -> "ACS Status"
            "0x2B30" -> "ACS Data In"
            "0x2B31" -> "ACS Data Out Notify"
            "0x2B32" -> "ACS Data Out Indicate"
            "0x2B33" -> "ACS Control Point"
            "0x2B34" -> "Enhanced Blood Pressure Measurement"
            "0x2B35" -> "Enhanced Intermediate Cuff Pressure"
            "0x2B36" -> "Blood Pressure Record"
            "0x2B37" -> "Registered User"
            "0x2B38" -> "BR - EDR Handover Data"
            "0x2B39" -> "Bluetooth SIG Data"
            "0x2B3A" -> "Server Supported Features"
            "0x2B3B" -> "Physical Activity Monitor Features"
            "0x2B3C" -> "General Activity Instantaneous Data"
            "0x2B3D" -> "General Activity Summary Data"
            "0x2B3E" -> "CardioRespiratory Activity Instantaneous Data"
            "0x2B3F" -> "CardioRespiratory Activity Summary Data"
            "0x2B40" -> "Step Counter Activity Summary Data"
            "0x2B41" -> "Sleep Activity Instantaneous Data"
            "0x2B42" -> "Sleep Activity Summary Data"
            "0x2B43" -> "Physical Activity Monitor Control Point"
            "0x2B44" -> "Activity Current Session"
            "0x2B45" -> "Physical Activity Session Descriptor"
            "0x2B46" -> "Preferred Units"
            "0x2B47" -> "High Resolution Height"
            "0x2B48" -> "Middle Name"
            "0x2B49" -> "Stride Length"
            "0x2B4A" -> "Handedness"
            "0x2B4B" -> "Device Wearing Position"
            "0x2B4C" -> "Four Zone Heart Rate Limits"
            "0x2B4D" -> "High Intensity Exercise Threshold"
            "0x2B4E" -> "Activity Goal"
            "0x2B4F" -> "Sedentary Interval Notification"
            "0x2B50" -> "Caloric Intake"
            "0x2B51" -> "TMAP Role"
            "0x2B77" -> "Audio Input State"
            "0x2B78" -> "Gain Settings Attribute"
            "0x2B79" -> "Audio Input Type"
            "0x2B7A" -> "Audio Input Status"
            "0x2B7B" -> "Audio Input Control Point"
            "0x2B7C" -> "Audio Input Description"
            "0x2B7D" -> "Volume State"
            "0x2B7E" -> "Volume Control Point"
            "0x2B7F" -> "Volume Flags"
            "0x2B80" -> "Volume Offset State"
            "0x2B81" -> "Audio Location"
            "0x2B82" -> "Volume Offset Control Point"
            "0x2B83" -> "Audio Output Description"
            "0x2B84" -> "Set Identity Resolving Key"
            "0x2B85" -> "Coordinated Set Size"
            "0x2B86" -> "Set Member Lock"
            "0x2B87" -> "Set Member Rank"
            "0x2B88" -> "Encrypted Data Key Material"
            "0x2B89" -> "Apparent Energy 32"
            "0x2B8A" -> "Apparent Power"
            "0x2B8B" -> "Live Health Observations"
            "0x2B8C" -> "CO \\{} text-subscript { 2 } Concentration"
            "0x2B8D" -> "Cosine of the Angle"
            "0x2B8E" -> "Device Time Feature"
            "0x2B8F" -> "Device Time Parameters"
            "0x2B90" -> "Device Time"
            "0x2B91" -> "Device Time Control Point"
            "0x2B92" -> "Time Change Log Data"
            "0x2B93" -> "Media Player Name"
            "0x2B94" -> "Media Player Icon Object ID"
            "0x2B95" -> "Media Player Icon URL"
            "0x2B96" -> "Track Changed"
            "0x2B97" -> "Track Title"
            "0x2B98" -> "Track Duration"
            "0x2B99" -> "Track Position"
            "0x2B9A" -> "Playback Speed"
            "0x2B9B" -> "Seeking Speed"
            "0x2B9C" -> "Current Track Segments Object ID"
            "0x2B9D" -> "Current Track Object ID"
            "0x2B9E" -> "Next Track Object ID"
            "0x2B9F" -> "Parent Group Object ID"
            "0x2BA0" -> "Current Group Object ID"
            "0x2BA1" -> "Playing Order"
            "0x2BA2" -> "Playing Orders Supported"
            "0x2BA3" -> "Media State"
            "0x2BA4" -> "Media Control Point"
            "0x2BA5" -> "Media Control Point Opcodes Supported"
            "0x2BA6" -> "Search Results Object ID"
            "0x2BA7" -> "Search Control Point"
            "0x2BA8" -> "Energy 32"
            "0x2BA9" -> "Media Player Icon Object Type"
            "0x2BAA" -> "Track Segments Object Type"
            "0x2BAB" -> "Track Object Type"
            "0x2BAC" -> "Group Object Type"
            "0x2BAD" -> "Constant Tone Extension Enable"
            "0x2BAE" -> "Advertising Constant Tone Extension Minimum Length"
            "0x2BAF" -> "Advertising Constant Tone Extension Minimum Transmit Count"
            "0x2BB0" -> "Advertising Constant Tone Extension Transmit Duration"
            "0x2BB1" -> "Advertising Constant Tone Extension Interval"
            "0x2BB2" -> "Advertising Constant Tone Extension PHY"
            "0x2BB3" -> "Bearer Provider Name"
            "0x2BB4" -> "Bearer UCI"
            "0x2BB5" -> "Bearer Technology"
            "0x2BB6" -> "Bearer URI Schemes Supported List"
            "0x2BB7" -> "Bearer Signal Strength"
            "0x2BB8" -> "Bearer Signal Strength Reporting Interval"
            "0x2BB9" -> "Bearer List Current Calls"
            "0x2BBA" -> "Content Control ID"
            "0x2BBB" -> "Status Flags"
            "0x2BBC" -> "Incoming Call Target Bearer URI"
            "0x2BBD" -> "Call State"
            "0x2BBE" -> "Call Control Point"
            "0x2BBF" -> "Call Control Point Optional Opcodes"
            "0x2BC0" -> "Termination Reason"
            "0x2BC1" -> "Incoming Call"
            "0x2BC2" -> "Call Friendly Name"
            "0x2BC3" -> "Mute"
            "0x2BC4" -> "Sink ASE"
            "0x2BC5" -> "Source ASE"
            "0x2BC6" -> "ASE Control Point"
            "0x2BC7" -> "Broadcast Audio Scan Control Point"
            "0x2BC8" -> "Broadcast Receive State"
            "0x2BC9" -> "Sink PAC"
            "0x2BCA" -> "Sink Audio Locations"
            "0x2BCB" -> "Source PAC"
            "0x2BCC" -> "Source Audio Locations"
            "0x2BCD" -> "Available Audio Contexts"
            "0x2BCE" -> "Supported Audio Contexts"
            "0x2BCF" -> "Ammonia Concentration"
            "0x2BD0" -> "Carbon Monoxide Concentration"
            "0x2BD1" -> "Methane Concentration"
            "0x2BD2" -> "Nitrogen Dioxide Concentration"
            "0x2BD3" -> "Non -Methane Volatile Organic Compounds Concentration"
            "0x2BD4" -> "Ozone Concentration"
            "0x2BD5" -> "Particulate Matter - PM1 Concentration"
            "0x2BD6" -> "Particulate Matter - PM2.5 Concentration"
            "0x2BD7" -> "Particulate Matter - PM10 Concentration"
            "0x2BD8" -> "Sulfur Dioxide Concentration"
            "0x2BD9" -> "Sulfur Hexafluoride Concentration"
            "0x2BDA" -> "Hearing Aid Features"
            "0x2BDB" -> "Hearing Aid Preset Control Point"
            "0x2BDC" -> "Active Preset Index"
            "0x2BDD" -> "Stored Health Observations"
            "0x2BDE" -> "Fixed String 64"
            "0x2BDF" -> "High Temperature"
            "0x2BE0" -> "High Voltage"
            "0x2BE1" -> "Light Distribution"
            "0x2BE2" -> "Light Output"
            "0x2BE3" -> "Light Source Type"
            "0x2BE4" -> "Noise"
            "0x2BE5" -> "Relative Runtime in a Correlated Color Temperature Range"
            "0x2BE6" -> "Time Second 32"
            "0x2BE7" -> "VOC Concentration"
            "0x2BE8" -> "Voltage Frequency"
            "0x2BE9" -> "Battery Critical Status"
            "0x2BEA" -> "Battery Health Status"
            "0x2BEB" -> "Battery Health Information"
            "0x2BEC" -> "Battery Information"
            "0x2BED" -> "Battery Level Status"
            "0x2BEE" -> "Battery Time Status"
            "0x2BEF" -> "Estimated Service Date"
            "0x2BF0" -> "Battery Energy Status"
            "0x2BF1" -> "Observation Schedule Changed"
            "0x2BF2" -> "Current Elapsed Time"
            "0x2BF3" -> "Health Sensor Features"
            "0x2BF4" -> "GHS Control Point"
            "0x2BF5" -> "LE GATT Security Levels"
            "0x2BF6" -> "ESL Address"
            "0x2BF7" -> "AP Sync Key Material"
            "0x2BF8" -> "ESL Response Key Material"
            "0x2BF9" -> "ESL Current Absolute Time"
            "0x2BFA" -> "ESL Display Information"
            "0x2BFB" -> "ESL Image Information"
            "0x2BFC" -> "ESL Sensor Information"
            "0x2BFD" -> "ESL LED Information"
            "0x2BFE" -> "ESL Control Point"
            "0x2BFF" -> "UDI for Medical Devices"
            else -> "Unknown Characteristics"
        }

同修改一下原有的getServiceUUID()getShortUUID(),只改名字而已,之前命名有点不太严谨。

四、特性适配器

首先我们在layout下创建一个item_characteristic.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="1dp"
    android:foreground="?attr/selectableItemBackground"
    android:paddingStart="16dp">

    <TextView
        android:id="@+id/tv_character_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="特性"
        android:textColor="@color/black"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_uuid_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="UUID:"
        app:layout_constraintStart_toStartOf="@+id/tv_character_name"
        app:layout_constraintTop_toBottomOf="@+id/tv_character_name" />

    <TextView
        android:id="@+id/tv_character_uuid"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="UUID"
        android:textColor="@color/black"
        app:layout_constraintBottom_toBottomOf="@+id/tv_uuid_title"
        app:layout_constraintStart_toEndOf="@+id/tv_uuid_title"
        app:layout_constraintTop_toTopOf="@+id/tv_uuid_title" />

    <TextView
        android:id="@+id/tv_property_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="Properties:"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="@+id/tv_character_name"
        app:layout_constraintTop_toBottomOf="@+id/tv_uuid_title" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_property"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="@+id/tv_property_title"
        app:layout_constraintStart_toEndOf="@+id/tv_property_title"
        app:layout_constraintTop_toTopOf="@+id/tv_property_title" />
        
</androidx.constraintlayout.widget.ConstraintLayout>

  这里显示特性的名称和UUIID,同时加载属性列表,然后写适配器,因为需要操作属性的缘故,这些写一个接口,在adapter包下新建一个OperateCallback接口,代码如下所示:

interface OperateCallback {
    /**
     * 属性操作
     */
    fun onPropertyOperate(characteristic: BluetoothGattCharacteristic, operateName: String)
}

通过这个接口可以知道当前操作的是那个特性和属性名称。下面我们写适配器,在adapter包下新建一个CharacteristicAdapter类,代码如下所示:

class CharacteristicAdapter(
    private val characteristics: List<BluetoothGattCharacteristic>,
    private val callback: OperateCallback
) : RecyclerView.Adapter<CharacteristicAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(ItemCharacteristicBinding.inflate(LayoutInflater.from(parent.context), parent, false))
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.binding.tvCharacterName.text = BleUtils.getCharacteristicsName(characteristics[position].uuid)
        holder.binding.tvCharacterUuid.text = BleUtils.getShortUUID(characteristics[position].uuid)
        //加载特性下的属性
        holder.binding.rvProperty.apply {
            layoutManager = LinearLayoutManager(context).apply { orientation = LinearLayoutManager.HORIZONTAL }
            val properties: List<String> = BleUtils.getProperties(characteristics[position].properties)
            adapter = PropertyAdapter(properties, object : OnItemClickListener {
                //点击属性
                override fun onItemClick(view: View?, position: Int) { callback.onPropertyOperate(characteristics[position], properties[position]) }
            })
        }
    }

    override fun getItemCount() = characteristics.size

    class ViewHolder(itemView: ItemCharacteristicBinding) : RecyclerView.ViewHolder(itemView.root) {
        var binding: ItemCharacteristicBinding

        init {
            binding = itemView
        }
    }
}

在这里我们就可以处理特性的名称和UUID显示,同时加载属性适配器,显示出来。

五、加载特性

  因为特性是在服务下的,所以我们可以在服务适配器中加载特性适配器。首先我们修改一下item_service.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:background="@color/white"
    android:layout_height="wrap_content">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/item_service"
        android:layout_width="match_parent"
        android:foreground="?attr/selectableItemBackground"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_service_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginTop="8dp"
            android:text="服务"
            android:textColor="@color/black"
            android:textSize="16sp"
            android:textStyle="bold"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tv_uuid_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="UUID:"
            app:layout_constraintStart_toStartOf="@+id/tv_service_name"
            app:layout_constraintTop_toBottomOf="@+id/tv_service_name" />

        <TextView
            android:id="@+id/tv_service_uuid"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="UUID"
            android:textColor="@color/black"
            app:layout_constraintBottom_toBottomOf="@+id/tv_uuid_title"
            app:layout_constraintStart_toEndOf="@+id/tv_uuid_title"
            app:layout_constraintTop_toTopOf="@+id/tv_uuid_title" />

        <TextView
            android:id="@+id/tv_service_info"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:text="PRIMARY SERVICE"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="@+id/tv_service_name"
            app:layout_constraintTop_toBottomOf="@+id/tv_uuid_title" />

        <ImageView
            android:id="@+id/iv_state"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="16dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:srcCompat="@drawable/ic_right_24" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_characteristics"
        android:visibility="gone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingStart="16dp" />
</LinearLayout>

  整体上变化不大,只是加了一个RecyclerView,同时增加了一个ImageView,用于显示当前的服务是否展开,可以通过当前的服务item的方式控制是否显示特性列表,这里用到两个图标,在drawable下创建ic_right_24.xml,代码如下所示:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="#000000"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M10,17l5,-5 -5,-5v10z" />
</vector>

还有一个ic_down_24.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="#000000"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M7,10l5,5 5,-5z" />
</vector>

下面修改一下ServiceAdapter,代码如下所示:

class ServiceAdapter(
    private val services: List<BluetoothGattService>,
    private val callback: OperateCallback
) : RecyclerView.Adapter<ServiceAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val viewHolder = ViewHolder(ItemServiceBinding.inflate(LayoutInflater.from(parent.context), parent, false))
        viewHolder.binding.itemService.setOnClickListener {
            //显示特性列表
            viewHolder.binding.rvCharacteristics.visibility = if (viewHolder.binding.rvCharacteristics.visibility == View.VISIBLE) View.GONE else View.VISIBLE
            //更换图标
            viewHolder.binding.ivState.setImageDrawable(
                if (viewHolder.binding.rvCharacteristics.visibility == View.VISIBLE) ContextCompat.getDrawable(parent.context, R.drawable.ic_down_24)
                else ContextCompat.getDrawable(parent.context, R.drawable.ic_right_24)
            )
        }
        return viewHolder
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.binding.tvServiceName.text = BleUtils.getServiceName(services[position].uuid)
        holder.binding.tvServiceUuid.text = BleUtils.getShortUUID(services[position].uuid)
        //加载服务下的特性
        holder.binding.rvCharacteristics.apply {
            layoutManager = LinearLayoutManager(context)
            adapter = CharacteristicAdapter(services[position].characteristics, callback)
        }
    }

    override fun getItemCount() = services.size

    class ViewHolder(itemView: ItemServiceBinding) : RecyclerView.ViewHolder(itemView.root) {
        var binding: ItemServiceBinding

        init {
            binding = itemView
        }
    }
}

  和之前的区别就在于构造的时候增加了一个回调,并且在onCreateViewHolder()函数中就处理了服务Item的点击事件,而不是像之前一样回调到Activity中,在服务Item的点击事件中判断是否显示特性列表同时修改图标资源。最后再将接口回调到Activity中。

六、显示特性和属性

  现在要做的就是修改MainActivity中的代码,首先修改activity_main.xml中的代码,主要是修改之前的rv_service中的属性值,修改后如下所示:

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_service"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_device_info" />

主要就是让RecyclerView占满剩下的空间,避免内容过多导致无法滑动的情况,最后我们修改一下MainActivity中代码,如下所示:

class MainActivity : BaseActivity(), BleCallback, OperateCallback {

	...

    override fun onServicesDiscovered(services: List<BluetoothGattService>) {
        runOnUiThread {
            mServiceList.clear()
            mServiceList.addAll(services)
            mServiceAdapter ?: run {
                mServiceAdapter = ServiceAdapter(mServiceList, this@MainActivity)
                binding.rvService.apply {
                    (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
                    layoutManager = LinearLayoutManager(this@MainActivity)
                    adapter = mServiceAdapter
                    //增加分隔线
                    addItemDecoration(DividerItemDecoration(this@MainActivity, DividerItemDecoration.VERTICAL))
                }
                mServiceAdapter
            }
            mServiceAdapter!!.notifyDataSetChanged()
        }
    }

    /**
     * 属性操作
     */
    override fun onPropertyOperate(characteristic: BluetoothGattCharacteristic, operateName: String) {
        showMsg(operateName)
    }
}

  修改的地方有三个,第一个就是MainActivity实现OnItemClickListener改成OperateCallback ,第二个是onServicesDiscovered()函数中,构建ServiceAdapter适配器中实现接口,然后添加分隔线,最后一个就是去掉之前onItemClick()函数改成onPropertyOperate()函数,运行一下看看效果。

在这里插入图片描述

七、源码

如果对你有所帮助的话,不妨 StarFork,山高水长,后会有期~

源码地址:GoodBle

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/69147.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【二】数据库系统

数据库系统的分层抽象DBMS 数据的三个层次从 数据 到 数据的结构----模式数据库系统的三级模式&#xff08;三级视图&#xff09;数据库系统的两层映像数据库系统的两个独立性数据库系统的标准结构 数据模型从 模式 到 模式的结构----数据模型三大经典数据模型 数据库的演变与发…

windows使用/服务(13)戴尔电脑怎么设置通电自动开机

戴尔pc机器通电自启动 1、将主机显示器键盘鼠标连接好后&#xff0c;按主机电源键开机 2、在开机过程中按键盘"F12",进入如下界面&#xff0c;选择“BIOS SETUP” 3、选择“Power Management” 4、选择“AC Recovery”&#xff0c;点选“Power On”&#xff0c;点击“…

uniapp 格式化时间刚刚,几分钟前,几小时前,几天前…

效果如图&#xff1a; 根目录下新建utils文件夹&#xff0c;文件夹下新增js文件&#xff0c;文件内容&#xff1a; export const filters {dateTimeSub(data) {if (data undefined) {return;}// 传进来的data必须是日期格式&#xff0c;不能是时间戳//将字符串转换成时间格式…

使用 prometheus client SDK 暴露指标

目录 1. 使用 prometheus client SDK 暴露指标1.1. How Go exposition works1.2. Adding your own metrics1.3. Other Go client features 2. Golang Application monitoring using Prometheus2.1. Metrics and Labels2.2. Metrics Types2.2.1. Counters:2.2.2. Gauges:2.2.3. …

Python测试框架pytest:常用参数、查找子集、参数化、跳过

Pytest是一个基于python的测试框架&#xff0c;用于编写和执行测试代码。pytest主要用于API测试&#xff0c;可以编写代码来测试API、数据库、UI等。 pytest是一个非常成熟的全功能的Python测试框架&#xff0c;主要有以下几个优点&#xff1a; 简单灵活&#xff0c;容易上手。…

前端渲染数据

在前端对接受后端数据处理后返回的接收值的时候&#xff0c;为了解决数据过于庞大&#xff0c;而对数据进行简化处理例如性别&#xff0c;经常会使用1&#xff0c; 0这俩个来代替文字的男&#xff0c;女。以下就是前端渲染的具体实现。 以下是部分代码 <el-table-columnpr…

【MFC】10.MFC六大机制:RTTI(运行时类型识别),动态创建机制,窗口切分,子类化-笔记

运行时类信息&#xff08;RTTI&#xff09; C: ##是拼接 #是替换成字符串 // RTTI.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <iostream> #include <afxwin.h>#ifdef _DEBUG #define new DEBUG_NEW #endifCWinApp th…

ubuntu 安装 nvidia 驱动

ubuntu 安装 nvidia 驱动 初环境与设备查询型号查询对应的驱动版本安装驱动验证驱动安装结果 本篇文章将介绍ubuntu 安装 nvidia 驱动 初 希望能写一些简单的教程和案例分享给需要的人 环境与设备 系统&#xff1a;ubuntu 设备&#xff1a;Nvidia GeForce RTX 4090 查询型…

Tcp是怎样进行可靠准确的传输数据包的?

概述 很多时候&#xff0c;我们都在说Tcp协议&#xff0c;Tcp协议解决了什么问题&#xff0c;在实际工作中有什么具体的意义&#xff0c;想到了这些我想你的技术会更有所提升&#xff0c;Tcp协议是程序员编程中的最重要的一块基石&#xff0c;Tcp是怎样进行可靠准确的传输数据…

web-ssrf

目录 ssrf介绍 以pikachu靶场为例 curl 访问外网链接 利用file协议查看本地文件 利用dict协议扫描内网主机开放端口 file_get_content 利用file协议查看本地文件&#xff1a; fsockopen() 防御方式: ssrf介绍 服务器端请求伪造&#xff0c;是一种由攻击者构造形成…

CSP复习每日一题(四)

树的重心 给定一颗树&#xff0c;树中包含 n n n 个结点&#xff08;编号 1 ∼ n 1∼n 1∼n&#xff09;和 n − 1 n−1 n−1条无向边。请你找到树的重心&#xff0c;并输出将重心删除后&#xff0c;剩余各个连通块中点数的最大值。 重心定义&#xff1a; 重心是指树中的一…

链式二叉树统计结点个数的方法和bug

方法一&#xff1a; 分治&#xff1a;分而治之 int BTreeSize1(BTNode* root) {if (root NULL) return 0;else return BTreeSize(root->left)BTreeSize(root->right)1; } 方法二&#xff1a; 遍历计数&#xff1a;设置一个计数器&#xff0c;对二叉树正常访问&#…

dubbo之高可用

负载均衡 概述 负载均衡是指在集群中&#xff0c;将多个数据请求分散到不同的单元上执行&#xff0c;主要是为了提高系统的容错能力和对数据的处理能力。 Dubbo 负载均衡机制是决定一次服务调用使用哪个提供者的服务。 策略 在Dubbo中提供了7中负载均衡策略&#xff0c;默…

冒泡排序 简单选择排序 插入排序 快速排序

bubblesort 两个for循环&#xff0c;从最右端开始一个一个逐渐有序 #include <stdio.h> #include <string.h> #include <stdlib.h>void bubble(int *arr, int len); int main(int argc, char *argv[]) {int arr[] {1, 2, 3, 4, 5, 6, 7};int len sizeof(…

想要延长Macbook寿命?这六个保养技巧你必须get!

Mac作为我们工作生活的伙伴&#xff0c;重要性不需要多说。但在使用的过程中&#xff0c;我们总会因不当操作导致Mac出现各种问题。 要想它长久的陪伴&#xff0c;平时的维护与保养自然不能少&#xff0c;Mac的保养很重要的两点就是硬件保养和电脑系统保养&#xff0c;硬件保养…

【一】初步认识数据库

数据库概览数据库 缘起表(Table)的理解用表来定义数据库数据库系统的理解概念层次的理解实例层次的理解 数据库管理系统的理解从用户角度看从系统实现角度看典型的数据库管理系统 数据库语言数据库定义、操纵、控制语言数据库语言 VS 高级语言 内容回顾练习 数据库概览 走马观…

gitblit-使用

1.登入GitBlit服务器 默认用户和密码: admin/admin 2.创建一个新的版本库 点击图中的“版本库”&#xff0c;然后点击图中“创建版本库” 填写名称和描述&#xff0c;注意名称最后一定要加 .git选择限制查看、克隆和推送勾选“加入README”和“加入.gitignore文件”在图中的1处…

2023一带一路东盟工商领袖峰会在曼谷成功举行,发明家周初材被授予中泰友好交流大使

今年是共建“一带一路”倡议提出十周年。十年来&#xff0c;共建“一带一路”倡议从理念到行动&#xff0c;从愿景到现实&#xff0c;开展更大范围、更高水平、更深层次的区域合作&#xff0c;致力于维护全球自由贸易体系和开放型世界经济&#xff0c;推动文明交流互鉴&#xf…

openeuler服务器 ls 和ll 命令报错 command not found...

在openeuler服务器执行 ls 和ll 命令报错 command not found... 大概是系统环境变量导致的问题。 我在安装redis是否没有安装成功后就出现了这样的情况。编辑profile文件没有写正确&#xff0c;导致在命令行下ls 和 ll 等命令不能够识别。 重新设置一下环境变量。 export PAT…

【项目学习1】如何将java对象转化为XML字符串

如何将java对象转化为XML字符串 将java对象转化为XML字符串&#xff0c;可以使用Java的XML操作库JAXB&#xff0c;具体操作步骤如下&#xff1a; 主要分为以下几步&#xff1a; 1、创建JAXBContext对象&#xff0c;用于映射Java类和XML。 JAXBContext jaxbContext JAXBConte…
最新文章