2022-11-11
来源:华纳网
责任编辑:谷雨老师
人气:
核心提示:大家好,欢迎来到谷雨课堂 本节, 我们使用Android来实现蓝牙BLE数据的双向收发, 本节虽然使用Android,但协议和内容和上节其实大同小异, 本节与上节联合一起就实现了Android与ESP32通过蓝牙的双向数据通讯 以下是Android实现蓝牙通信的部分代码: public
大家好,欢迎来到谷雨课堂
本节,
我们使用Android来实现蓝牙BLE数据的双向收发,
本节虽然使用Android,但协议和内容和上节其实大同小异,
本节与上节联合一起就实现了Android与ESP32通过蓝牙的双向数据通讯
以下是Android实现蓝牙通信的部分代码:
免责声明:本文仅代表作者个人观点,与华纳网无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
public class YsBtle {
class cYsBleItem{
public int idx=0;
public String mac="";
public BluetoothDevice dev=null;
}
public static String TAG="ysbt";
public static YsBtle m_instance;
BluetoothAdapter bta;
BluetoothDevice dev;
Handler mHandler;
BluetoothLeScanner scanner;
Context ctx;
private BluetoothGattService bluetoothGattService;
private BluetoothGattCharacteristic writeCharacteristic;
private BluetoothGattCharacteristic notifyCharacteristic;
//private BluetoothGatt btDeviceGatt;
List<cYsBleItem> lstDevices=new ArrayList<>();
//Server
BluetoothGattService mGattService;
BluetoothGattCharacteristic mCharacteristicRead;
BluetoothGattCharacteristic mCharacteristicWrite;
BluetoothGattServer mBluetoothGattServer;
BluetoothManager bluetoothManager;
static UUID UUID_SERVER=UUID.fromString("0000180d-0000-1000-8000-00805f9b34fb");
static UUID UUID_CHARREAD=UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb");
static UUID UUID_CHARWRITE=UUID.fromString("00002a00-0000-1000-8000-00805f9b34fb");
static UUID UUID_DESCRIPTOR=UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private ConcurrentHashMap<String, BluetoothDevice> btClients = new ConcurrentHashMap<>();
private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
public boolean isCurUUID(String uuid,String uuid_s){
String uuid_ss=uuid.substring(4,8).toUpperCase();
return uuid_ss.equals(uuid_s.toUpperCase());
}
public static String bytesToHexFun1(byte[] bytes) {
if(bytes==null)return "";
// 一个byte为8位,可用两个十六进制位标识
char[] buf = new char[bytes.length * 2];
int a = 0;
int index = 0;
for(byte b : bytes) { // 使用除与取余进行转换
if(b < 0) {
a = 256 + b;
} else {
a = b;
}
buf[index++] = HEX_CHAR[a / 16];
buf[index++] = HEX_CHAR[a % 16];
}
return new String(buf);
}
public String getPropInfo(int prop){
String ret="";
if((prop & BluetoothGattCharacteristic.PROPERTY_BROADCAST)==BluetoothGattCharacteristic.PROPERTY_BROADCAST)ret+=" BROADCAST";
if((prop & BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS)==BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS)ret+=" EXTENDED";
if((prop & BluetoothGattCharacteristic.PROPERTY_INDICATE)==BluetoothGattCharacteristic.PROPERTY_INDICATE)ret+=" INDICATE";
if((prop & BluetoothGattCharacteristic.PROPERTY_READ)==BluetoothGattCharacteristic.PROPERTY_READ)ret+=" READ";
if((prop & BluetoothGattCharacteristic.PROPERTY_NOTIFY)==BluetoothGattCharacteristic.PROPERTY_NOTIFY)ret+=" NOTIFY";
if((prop & BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE)==BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE)ret+=" SIGNED_WRITE";
if((prop & BluetoothGattCharacteristic.PROPERTY_WRITE)==BluetoothGattCharacteristic.PROPERTY_WRITE)ret+=" WRITE";
if((prop & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)==BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)ret+=" WRITE_NO_RESPONSE";
return ret;
}
public String getUuidInfo(String typ,String uuid){
String ret="";
String uuid_s=uuid.substring(4,8).toUpperCase();
ret=uuid_s+" "+YsBleStd.getInfo(uuid_s);
return ret;
}
public static String bytesToHexFun2(byte[] bytes) {
char[] buf = new char[bytes.length * 2];
int index = 0;
for(byte b : bytes) { // 利用位运算进行转换,可以看作方法一的变种
buf[index++] = HEX_CHAR[b >>> 4 & 0xf];
buf[index++] = HEX_CHAR[b & 0xf];
}
return new String(buf);
}
public static String bytesToHexFun3(byte[] bytes) {
StringBuilder buf = new StringBuilder(bytes.length * 2);
for(byte b : bytes) { // 使用String的format方法进行转换
buf.append(String.format("%02x", new Integer(b & 0xff)));
}
return buf.toString();
}
public static byte[] StringtoBytes(String str) {
if (str == null || str.trim().equals("")) {
return new byte[0];
}
byte[] bytes = new byte[str.length() / 2];
for (int i = 0; i < str.length() / 2; i++) {
String subStr = str.substring(i * 2, i * 2 + 2);
bytes[i] = (byte) Integer.parseInt(subStr, 16);
}
return bytes;
}
public YsBtle(Context ctx_){
ctx=ctx_;
bluetoothManager= (BluetoothManager)ctx.getSystemService(BLUETOOTH_SERVICE);
bta=bluetoothManager.getAdapter(); //获取蓝牙适配器
mHandler=new Handler();
}
public void l(String s){
Log.e(TAG,s);
}
public static YsBtle getDefault(Context c) {
if(m_instance==null){
m_instance=new YsBtle(c);
}
return m_instance;
}
public boolean isOK(){
if(bta==null){
return false;
}
if(!bta.isEnabled()){
return false;
}
return true;
}
public void start_connect(){
if(bta.isDiscovering())
{
l("正在扫描呢");
return;
}
//btDeviceGatt.connect();
}
public void start_scan(){
if(bta.isDiscovering())
{
l("正在扫描呢");
return;
}
scanner=bta.getBluetoothLeScanner(); //获取BluetoothLeScanner对象
scanner.startScan(callback); //开始扫描,扫描的结果在callback里面处理
mHandler.postDelayed(new Runnable() { //20秒后停止扫描,在callback里获取扫描结果
public void run() {
scanner.stopScan(callback);
}
},20000);
l("开始扫描...");
}
public void start_server(){
//BluetoothServerSocket serverSocket = bta.listenUsingRfcommWithServiceRecord(NAME_INSECURE, MY_UUID);
//BluetoothSocket socket = serverSocket.accept();
}
public void start_bt_server(){
if(!bta.isEnabled()){
return;
}
bta.setName("云杉数据");
//获取广播对象
BluetoothLeAdvertiser mBluetoothLeAdvertiser = bta.getBluetoothLeAdvertiser();
if(mBluetoothLeAdvertiser == null){
l("设备不支持广播");
Toast.makeText(ctx,"设备不支持蓝牙广播",Toast.LENGTH_SHORT).show();
return;
}
//准备广播的参数,最后这个setTimeout是设置广播 发送的时间间隔吧,0应该是默认值,
//如果填的太高,可能会导致别人扫描的时候不一定能扫描得到你
AdvertiseSettings.Builder settingsBuilder=new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
.setConnectable(true)
.setTimeout(0);
AdvertiseSettings advertiseSettings = settingsBuilder.build();
//初始化广播数据
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
dataBuilder.setIncludeDeviceName(true)
.addServiceUuid(new ParcelUuid(UUID_SERVER))
.setIncludeTxPowerLevel(true);
AdvertiseData advertiseData = dataBuilder.build();
//初始化广播的应答数据(当别人扫描时,我们返回这个数据)
AdvertiseData scanResponse = new AdvertiseData.Builder()
//.addManufacturerData(MANUFACTURE_ID_RESPONSE, SPECIFIC_DATA_RESPONSE) //可以加上设备的制造商信息
.addServiceUuid(new ParcelUuid(UUID_SERVER))
.build();
//开始广播, mAdertiseCallback是开启广播时的回调,我们在下面定义它
mBluetoothLeAdvertiser.startAdvertising(advertiseSettings, advertiseData, scanResponse, mAdertiseCallback);
}
//广播成功或失败时的回调处理
private AdvertiseCallback mAdertiseCallback = new AdvertiseCallback() {
public void onStartFailure(int errorCode){
super.onStartFailure(errorCode);
l("广播失败:"+errorCode);
}
public void onStartSuccess(AdvertiseSettings settingsInEffect){
super.onStartSuccess(settingsInEffect);
l("服务端的广播成功开启");
//广播开启成功后,我们初始化并蓝牙服务(实现见下面)
initBleServer();
}
};
//添加一个蓝牙服务,该服务下两个特征,一个读特征、一个写特征。
private void initBleServer(){
//创建蓝牙服务对象
mGattService = new BluetoothGattService(UUID_SERVER, BluetoothGattService.SERVICE_TYPE_PRIMARY);
//创建读特征值,主要用于客户端获取蓝牙的基本信息
mCharacteristicRead = new BluetoothGattCharacteristic(UUID_CHARREAD, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ);
BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(UUID_DESCRIPTOR,BluetoothGattCharacteristic.PERMISSION_READ);
mCharacteristicRead.addDescriptor(descriptor);
//给特征值添加描述符,我理解相当于告诉客户端这个特征值能干嘛,在这个例子里这个特征值就纯用于发送蓝牙的信息到客户端
mGattService.addCharacteristic(mCharacteristicRead);
//创建写特征值,这个特征值有读属性(READ),有写属性(WRITE,发送数据),还可以用于通知客户端接收数据(NOTIFY)
mCharacteristicWrite = new BluetoothGattCharacteristic(UUID_CHARWRITE,
BluetoothGattCharacteristic.PROPERTY_WRITE
| BluetoothGattCharacteristic.PROPERTY_READ
| BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_WRITE
|BluetoothGattCharacteristic.PERMISSION_READ
| BluetoothGattCharacteristic.PROPERTY_NOTIFY);
l( "写UUID:"+UUID_CHARWRITE);
//设置描述符的值,此处要注意:如果要用通知方式向客户端发数据,需要加上此两行
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mCharacteristicWrite.addDescriptor(descriptor);
//将写特征值加入到服务
mGattService.addCharacteristic(mCharacteristicWrite);
//启动蓝牙服务,完成这个步骤后,我们就可以收发数据了
//注意:此处需要传入gattServerCallback回调,它的实现在下面
BluetoothManager bluetoothManager= (BluetoothManager)ctx.getSystemService(BLUETOOTH_SERVICE);
mBluetoothGattServer = bluetoothManager.openGattServer(ctx, gattServerCallback);
mBluetoothGattServer.addService(mGattService);
l("初始化服务成功:initServices ok");
}
//服务事件的回调
private BluetoothGattServerCallback gattServerCallback = new BluetoothGattServerCallback() {
//1、首先是连接状态的回调
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
l("连接状态发生改变,安卓系统回调onConnectionStateChange:device name="+device.getName()+"address="+device.getAddress()+"status="+status+"newstate="+newState);
if(newState == BluetoothProfile.STATE_CONNECTED) {
l("Add device to clients:"+device.getName()+","+device.getAddress());
btClients.put(device.getAddress(), device);
try {
l("try to create bt socket");
BluetoothSocket soc = device.createInsecureRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
soc.connect();
InputStream is= soc.getInputStream();
l("bt is ok");
}catch (Exception e){
l("create bt socket error "+e.getMessage());
}
} else if(newState == BluetoothProfile.STATE_DISCONNECTED) {
l("Remove device from clents:"+device.getName()+","+device.getAddress());
btClients.remove(device.getAddress());
}
}
//处理客户端主动发起的读的操作,通过sendResponse发送数据过去,这个数据可以被多次请求重复发送
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
l("客户端有读的请求,安卓系统回调该onCharacteristicReadRequest()方法");
l("requestId="+requestId+",offset="+offset+","+characteristic.getUuid().toString()+",value:"+bytesToHexFun1(characteristic.getValue()));
mBluetoothGattServer.sendResponse(device,requestId, BluetoothGatt.GATT_SUCCESS,0,new byte[]{'A','B','C'});
}
//接收客户端发过来的数据,当有特征被写入时,回调该方法,写入的数据为参数中的value
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
l("客户端有写的请求,安卓系统回调该onCharacteristicWriteRequest()方法");
//特征被读取,回复客户端接收数据成功,注意:此处作用为通知客户端已收到该数据,否则客户端可能会认为数据传输失败而断开蓝牙
mBluetoothGattServer.sendResponse(device,requestId,BluetoothGatt.GATT_SUCCESS,offset,value);
//客户端发送过来的数据,此处是给客户端又发送了回去
l("收到"+value.length+"字节 "+bytesToHexFun1(value)); //响应客户端
}
//特征被读取。当回复相应成功后,客户端胡读取然后触发本方法
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor);
l("onDescriptorReadRequest调用了"+requestId);
mBluetoothGattServer.sendResponse(device,requestId,BluetoothGatt.GATT_SUCCESS,offset,null);
}
//当有描述请求被写入时,回调该方法,
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
mBluetoothGattServer.sendResponse(device,requestId,BluetoothGatt.GATT_SUCCESS,offset,value);
l("有数据写入");
// onResponseToClient(value,device,requestId,descriptor.getCharacteristic());
}
//蓝牙服务添加成功时的回调
public void onServiceAdded(int status, BluetoothGattService service){
super.onServiceAdded(status,service);
l("添加服务成功,安卓系统回调该onServiceAdded()方法");
}
//MTU改变时的回调,MTU为每个蓝牙数据包最大的字节数,通常默认是20,
//如果你有一个数据超过20字节,那需要分成很多个小于或等于20字节的包来发送
public void onMtuChanged(BluetoothDevice device, int mtu) {
super.onMtuChanged(device, mtu);
l( "MTU改变:"+mtu);
}
//当数据向客户端发送成功时,会调用 此回调
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
l("Notification Sent:"+ status);
}
//没试,不知道作用
public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
super.onExecuteWrite(device, requestId, execute);
l( "执行写");
}
};
public void start_bt(){
if(!bta.isEnabled()){
return;
}
start_scan();
//start_bt_server();
}
AdvertiseSettings settings = new AdvertiseSettings.Builder().setConnectable(true).build();
AdvertiseData advertiseData = new AdvertiseData.Builder()
.setIncludeDeviceName(true)
.setIncludeTxPowerLevel(true)
.build();
AdvertiseData scanResponseData = new AdvertiseData.Builder()
//.addServiceUuid(new ParcelUuid(UUID_SERVER))
.setIncludeTxPowerLevel(true)
.build();
AdvertiseCallback adcallback1 = new AdvertiseCallback() {
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
l("AdvertiseCallback onStartSuccess");
}
public void onStartFailure(int errorCode) {
l("Failed to add BLE advertisement, reason: " + errorCode);
}
};
ScanCallback callback=new ScanCallback() { //扫描回调
(api = Build.VERSION_CODES.M)
public void onScanResult(int callbackType, ScanResult result) { //找到设备
super.onScanResult(callbackType,result);
//在这里面对你扫描到的蓝牙设备进行操作
ScanRecord scanRecord = result.getScanRecord(); //获取扫描记录
BluetoothDevice dev=result.getDevice();
String dev_name=dev.getName();
String dev_address=dev.getAddress();
int rssi=result.getRssi();
int txpower=result.getTxPower();
//l("bt device found name="+dev_name+",address="+dev_address+" ,rssi="+rssi+" ,txpower="+txpower);
if(scanRecord!=null){
//l(scanRecord.toString());
SparseArray<byte[]> xx=scanRecord.getManufacturerSpecificData();
if(xx!=null){
}
}
scanner.stopScan(callback);
}
}
public void onScanFailed(int errorCode) { //没有开启扫描
super.onScanFailed(errorCode);
}
};
(api = Build.VERSION_CODES.M)
void add_to_list(BluetoothDevice dev){
String dev_address=dev.getAddress();
l("准备连接手环... "+dev_address);
for (cYsBleItem d:lstDevices) {
if(dev.getAddress().equals(d.mac)){
l("重复手环,跳过");
return;
}
}
int idx=lstDevices.size()+1;
l("连接第 "+(idx)+" 个");
dev.connectGatt(ctx,true,mBluetoothGattCallback,BluetoothDevice.TRANSPORT_LE);
}
//准备操作手环读写
public void init_tba_oper(BluetoothGatt gatt){
String mac=gatt.getDevice().getAddress();
BluetoothGattService serv= gatt.getService(UUID.fromString("0000180d-0000-1000-8000-00805f9b34fb"));
if(serv==null){
l("获取BLE指定Service失败,返回Null");
}else{
BluetoothGattCharacteristic c=serv.getCharacteristic(UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb"));
if(c==null){
l("getCharacteristic is null ");
}else{
l("准备开启notify");
c.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
gatt.writeCharacteristic(c);
if(!gatt.setCharacteristicNotification(c,true)){
l("fail to start notify");
}
BluetoothGattDescriptor descriptor = c.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
int i=0;
if (!gatt.writeDescriptor(descriptor)){
l(" !! error writeDescriptor");
}
/*
new Timer().schedule(new TimerTask() {
@Override
public void run() {
l("读!");
if(!bluetoothGatt.readCharacteristic(c)){
l("try to read err");
}
}
},0 ,100);
*/
}
}
}
BluetoothGattCallback mBluetoothGattCallback=new BluetoothGattCallback() { //连接回调
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
l("onConnectionStateChange status="+status);
if (status==BluetoothGatt.GATT_SUCCESS) {
l("onConnectionStateChange: ========》》连接成功");
gatt.discoverServices();
}
}
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic c, int status) {
byte[] buf=c.getValue();
String uuid_info=getUuidInfo("",c.getUuid().toString());
l(" -> LE读取到数据 ["+uuid_info+"] "+bytesToHexFun1(buf)+" "+(new String(buf)).toString());
}
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
l("onDescriptorRead "+status);
}
//写入成功回调函数
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
l("onCharacteristicWrite 写入成功");
}
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
l("onDescriptorWrite status="+status);
}
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
l("onMtuChanged");
}
//UUID搜索成功回调
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
l("onServicesDiscovered status="+status);
/*
if (status == BluetoothGatt.GATT_SUCCESS) {
List<BluetoothGattService> srvs = gatt.getServices();
for (int i = 0; i < srvs.size(); i++) {
l(" ");
l(" ");
String uuid=srvs.get(i).getUuid().toString();
String uuid_s=getUuidInfo("Service",uuid);
l((i+1)+" Service ["+uuid_s+"] UUID=:" + uuid);
List<BluetoothGattCharacteristic> Characteristic = srvs.get(i).getCharacteristics();
for (int j = 0; j < Characteristic.size(); j++) {
BluetoothGattCharacteristic c=Characteristic.get(j);
int prop=c.getProperties();
String uuid_c=c.getUuid().toString();
String uuid_sc=getUuidInfo("Characteristic",uuid_c);
l(" "+(j+1)+". Characteristic ["+uuid_sc+"] UUID=:" +uuid_c+",Prop="+getPropInfo(prop));
boolean ret=gatt.setCharacteristicNotification(c,true);
if(!ret)l(" fail");
for(BluetoothGattDescriptor d : c.getDescriptors()){
String uuid_d=d.getUuid().toString();
String uuid_sd=getUuidInfo("Descriptor",uuid_d);
l(" desc ["+uuid_sd+"] UUID="+uuid_d);
}
}
}
} else {
l("fail , onservicesdiscovered收到: " + status);
}
*/
init_tba_oper(gatt);
}
//接受数据回调
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
String mac=gatt.getDevice().getAddress();
byte[] value = characteristic.getValue();
//l("ReceiveSuccess "+ bytesToHexFun1(value));
}
};
int get_ble_idx(String mac){
for (cYsBleItem x:lstDevices) {
if(x.mac.equals(mac)){
return x.idx;
}
}
return -1;
}
public void ble_get_connected_list() {
Set<BluetoothDevice> devices =bta.getBondedDevices();
for(BluetoothDevice dev:devices){
boolean isConnect = false;
try {
//获取当前连接的蓝牙信息
isConnect = (boolean) dev.getClass().getMethod("isConnected").invoke(dev);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
l("err ");
}
if (isConnect) {
l("device2=" + dev.getAddress());
}
l("name="+dev.getName());
}
}
}
完整的源代码可以登录【华纳网】下载。
https://www.worldwarner.com/
免责声明:本文仅代表作者个人观点,与华纳网无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。