安卓通讯之《蓝牙单片机通讯助手》②扫描设备、连接设备和双向通讯。

前言


上篇文章我们介绍到了开发经典蓝牙和单片机通讯的过程,安卓通讯之《蓝牙单片机通讯助手》①集成工作 ,我们这里还要兼容最新的安卓6.0及以上的系统,因为从6.0以后的权限机制和以往的不一样,我们需要在代码中向用户询问权限。而且在6.0运行蓝牙,还需要加上获取到此刻的地理位置的权限,这是谷歌加的~~,所以我们先把运行的权限弄好先,再扫描设备、连接设备和双向通讯。


权限问题(兼容安卓6.0及以上)


很多小伙伴问我,为什么你以前写的安卓高版本的蓝牙App现在在高版本的安卓机就不可以获取到附近的蓝牙设备啦?这也是我上面提到的:在6.0运行蓝牙,还需要加上获取到此刻的地理位置的权限。所以我们先把权限弄好。我下面是用郭神封装好的权限代码。你们如有不懂,去CSDN搜索郭霖6.0就有当天的直播权限封装视频解说。


private void checkpermission() {
        //判断是否APi等于或大于23,即6.0
        if(Build.VERSION.SDK_INT >= 23){
            //所需要的权限加进去
            requestRuntimePermission(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION
                    ,Manifest.permission.ACCESS_FINE_LOCATION
                    ,Manifest.permission.BLUETOOTH
                    ,Manifest.permission.BLUETOOTH_ADMIN}, new PermissionListener() {

                //权限回调
                @Override
                public void onGranted() {
                    //全部都授权就跳转下个Activity
                    Log.i("权限全部都同意了","-----=="); 
                                           }
                  //某个没有被授权则强制退出程序
                @Override
                public void onDenied(List<String> deniedPermission) {
                    for(String persmission:deniedPermission){
                        Toast.makeText(SplashActivity.this,"基于你的安卓版本大于6.0,未授予该权限导致不能运行,强制退出:"+persmission,Toast.LENGTH_LONG).show();

                    } }
            });
        }else {
            //手机在6.0以下,则不需要获取到地理位置权限,直接跳过
       }
    }

打开蓝牙扫描工作


打开蓝牙工作,相信大家都会,用户在此打开蓝牙的是否,要用 intent回调。在这里,我们还需要写进一个蓝牙广播接收器,来监听系统发出来的已经搜索到的附近蓝牙设备,并且获取该数据的名字和蓝牙地址。


代码部分说明:

我这里用的是一个listVIew控件来展示当前可以连接的蓝牙设备。点击某一项,则把该项的蓝牙名字和id传下去到一个Activity的通讯工作。中间的是一个进度条之类的,自定义控件。


扫描

package com.example.xuhong.bluetoothassistant_master;

import android.app.Activity;
import android.app.ListActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.os.Process;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;

import com.example.xuhong.bluetoothassistant_master.adapter.DeviceListAdapter;
import com.example.xuhong.bluetoothassistant_master.ui.WhorlView;


public class DeviceScanActivity extends ListActivity {

    private TextView mtv_show;
    // 调试用
    private static final String TAG = "DeviceScanActivity";

    // 开启蓝牙请求码
    private static final int REQUEST_ENABLE = 0;

    // 停止扫描蓝牙消息头
    private static final int WHAT_CANCEL_DISCOVERY = 1;

    // 判断蓝牙列表
    private static final int WHAT_DEVICE_UPDATE = 2;

    // 扫描间隔时间
    private static final int SCAN_PERIOD = 30 * 1000;

    //实例化Adapter
    private DeviceListAdapter mLeDeviceListAdapter = null;

     // 蓝牙适配器
    private BluetoothAdapter mBluetoothAdapter = null;

     // 进度条
    private WhorlView mWhorlView = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_device_scan);
        init();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mLeDeviceListAdapter = new DeviceListAdapter(this);
        // 设置列表适配器,注:调用此方法必须继承ListActivity
        setListAdapter(mLeDeviceListAdapter);
        scanDevice(true);
        mWhorlView.setVisibility(View.VISIBLE);
    }

    @Override
    protected void onPause() {
        scanDevice(false);
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        unregReceiver();
        super.onDestroy();
    }


    //回调函数
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if ( resultCode == Activity.RESULT_CANCELED) {
            finish();
            Process.killProcess(Process.myPid());
        }

        switch (requestCode){
            case REQUEST_ENABLE:
                if ( mBluetoothAdapter.isEnabled()) {
                    registerReceiver();
                    scanDevice(true);
                }

                break;

        }
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);

        BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);

        if (device == null) {
            return;
        }

        Intent intent = new Intent(this, MainActivity.class);
        Bundle bundle = new Bundle();
        bundle.putParcelable("device", device);
        intent.putExtras(bundle);

        scanDevice(false);
        startActivity(intent);
        finish();
    }

    /**
     * 消息处理者
     */
    private Handler mHandler = new Handler(new Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {

            case WHAT_DEVICE_UPDATE:
                mLeDeviceListAdapter.addDevice((BluetoothDevice) msg.obj);
                // 刷新列表
                mLeDeviceListAdapter.notifyDataSetChanged();
                break;

            case WHAT_CANCEL_DISCOVERY:
                mWhorlView.setVisibility(View.GONE);
                mtv_show.setText("搜索完毕!");
                break;
                default:
                break;
            }
            return false;
        }
    });

    /**
     * 初始化
     */
    private void init() {
        mtv_show= (TextView) findViewById(R.id.tv_show);
        mWhorlView = (WhorlView) findViewById(R.id.whorl_view);
        // 开启动画
        mWhorlView.start();

        // 初始化本地蓝牙设备
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        // 检测蓝牙设备是否开启,如果未开启,发起Intent并回调
        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE);
        }

        registerReceiver();
    }

    /**
     * 是否扫描蓝牙设备
     */
    private void scanDevice(boolean enable) {
        if (enable) {

            Log.d(TAG, "[1]--> startDiscovery()");

            // 开启扫描
            mBluetoothAdapter.startDiscovery();

            // 延时30s后取消扫描动作
            mHandler.postDelayed(new Runnable() {

                @Override
                public void run() {
                    mBluetoothAdapter.cancelDiscovery();

                    Log.d(TAG, "[2]--> cancelDiscovery()");

                    // 发送消息
                    mHandler.sendEmptyMessage(WHAT_CANCEL_DISCOVERY);
                }
            }, SCAN_PERIOD);
        } else {
            Log.d(TAG, "[3]--> cancelDiscovery()");
            // 停止扫描
            mBluetoothAdapter.cancelDiscovery();
        }
    }

    /**
     * 注册广播接收器
     */
    private void registerReceiver() {
        registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
    }

    /**
     * 注销广播接收器
     */
    private void unregReceiver() {
        if (mReceiver != null) {
            unregisterReceiver(mReceiver);
        }
    }

    /**
     * 广播接收器接收返回的蓝牙信息
     */
    private BroadcastReceiver mReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            //未配对的设备
            if (BluetoothDevice.ACTION_FOUND == action) {

                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

                Log.d(TAG, "[4] --> " + device.getName() + "------" + device.getAddress());

                if (device != null) {
                    //发送消息
                    mHandler.sendMessage(mHandler.obtainMessage(WHAT_DEVICE_UPDATE, device));
                }
            }
        }
    };
}


通讯工作


我这里用的是一个spinner控件和一个button,怎么使用去看看相关博文,我这里只是供用户选择发送到哪一个数据。因为单片机通讯都是基本的 十六进制 0x00格式,所以我这里只是规范下发送的格式,你们也可以完全按照自己的想法设计UI。


通讯工作


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.ToggleButton;

import com.example.xuhong.bluetoothassistant_master.adapter.ChatListAdapter;
import com.example.xuhong.bluetoothassistant_master.data.ChatListData;
import com.example.xuhong.bluetoothassistant_master.util.Toaster;

// 2016.09.15.   徐宏 编写
public class MainActivity extends Activity  {


    private ListView mChatListView;

    //列表
    private List<ChatListData> mList = new ArrayList<>();

    private ChatListAdapter chatListAdapter;

    private static final String TAG = "MainActivity";
    // uuid
    private static final String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";

    private Spinner mSpinner;

    private String[] data = new String[]{"选择 0x01", "选择 0x02", "选择 0x03", "选择       0x04", "选择 0x05", "选择 0x06", "选择 0x07" , "选择 0x08", "选择 0x09"};


    private Byte sendData = null;
    private String mRecieve =null;

    private ArrayAdapter<String>  adapter;
    //获得系统的适配器
    private BluetoothDevice mBluetoothDevice = null;
    //创建socket
    private BluetoothSocket mSocket = null;
    //io流
    private OutputStream mOutS = null;
    private static  final  int CONNECT_SUCCED =10;
    private static final  int  mRecieve_SUCCED = 20;
    private InputStream input  =null;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //全屏显示
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setContentView(R.layout.activity_main);
        initview();
    }

    private void initview() {
        mSpinner = (Spinner) findViewById(R.id.mSpinner);
        mChatListView = (ListView)findViewById(R.id.mChatListView);

        chatListAdapter=new ChatListAdapter(MainActivity.this,mList);
        mChatListView.setAdapter(chatListAdapter);


        adapter= new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,data);
        mSpinner.setAdapter(adapter);
        mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int id, long l) {
                switch (id){
                    case 0:
                        sendData=1;
                        Log.i(TAG,"点击0");
                        break;
                    case 1:
                        sendData=2;
                        Log.i(TAG,"点击1");
                        break;
                    case 2:
                        sendData=3;
                        Log.i(TAG,"点击2");
                        break;
                    case 3:
                        sendData=4;
                        Log.i(TAG,"点击3");
                        break;
                    case 4:
                        sendData=5;
                        Log.i(TAG,"点击4");
                        break;
                    case 5:
                        sendData=6;
                        Log.i(TAG,"点击5");
                        break;
                    case 6:
                        sendData=7;
                        Log.i(TAG,"点击6");
                        break;
                    case 7:
                        sendData=8;
                        Log.i(TAG,"点击7");
                        break;
                    case 8:
                        sendData=9;
                        Log.i(TAG,"点击8");
                        break;

                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {

            }
        });
    }

    //添加右边文本
    private void addRightItem(String text) {
        ChatListData date = new ChatListData();
        date.setType(ChatListAdapter.VALUE_RIGHT_TEXT);
        date.setText(text);
        mList.add(date);
        //通知adapter刷新
        adapter.notifyDataSetChanged();
        //滚动到底部
        mChatListView.setSelection(mChatListView.getBottom());
    }


    //添加左边文本
    private void addLeftItem(String text) {
        ChatListData date = new ChatListData();
        date.setType(ChatListAdapter.VALUE_LEFT_TEXT);
        date.setText(text);
        mList.add(date);
        //通知adapter刷新
        adapter.notifyDataSetChanged();
        //滚动到底部
        mChatListView.setSelection(mChatListView.getBottom());
    }


    public void btn_send(View v){
        writeStream(sendData);
        addRightItem(sendData+"");
    }

    @Override
    protected void onStart() {
        super.onStart();
        initDevice();
    }


    @Override
    protected void onStop() {
        close();
        super.onStop();
    }

    private void initDevice() {
        Bundle bundle = getIntent().getExtras();
        if (bundle != null) {
            mBluetoothDevice = bundle.getParcelable("device");
            if (mBluetoothDevice != null) {
                new Thread(mConnRun).start();
            }
        }
    }

    private Runnable mConnRun = new Runnable() {

        @Override
        public void run() {
            connect();
        }
    };
    private void connect() {

        UUID uuid = UUID.fromString(SPP_UUID);

        try {
            mSocket = mBluetoothDevice.createRfcommSocketToServiceRecord(uuid);
        } catch (IOException e) {
            if (mSocket != null) {
                try {
                    mSocket.close();
                } catch (IOException e1) {
                    Log.e(TAG, e1.getMessage());
                }
            }
        }

        try {
            mSocket.connect();
        } catch (IOException e) {
            if (mSocket != null) {
                try {
                    mSocket.close();
                } catch (IOException e1) {
                    Log.e(TAG, e1.getMessage());
                }
            }
        }

        try {
            mOutS = mSocket.getOutputStream();
            input=mSocket.getInputStream();
            handler.sendEmptyMessage(CONNECT_SUCCED);
        } catch (IOException e) {
            if (mOutS != null) {
                try {
                    mOutS.close();
                } catch (IOException e1) {
                    Log.e(TAG, e.getMessage());
                }
            }

            if (mSocket != null) {
                try {
                    mSocket.close();
                } catch (IOException e1) {
                    Log.e(TAG, e.getMessage());
                }
            }
        }
    }


    private void close() {
        if (mOutS != null) {
            try {
                mOutS.close();
            } catch (IOException e) {
                Log.e(TAG, e.getMessage());
            }
        }

        if (mSocket != null) {
            try {
                mSocket.close();
            } catch (IOException e) {
                Log.e(TAG, e.getMessage());
            }
        }
    }


    private void writeStream(byte data) {
        try {
            if (mOutS != null) {
                mOutS.write(data);
                Log.i(TAG,"输出---->>>是:"+data);
                mOutS.flush();
            }
        } catch (IOException e) {

            runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    Toaster.shortToastShow(MainActivity.this, "连接超时");
                    MainActivity.this.finish();
                }
            });
        }
    }


    public Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {
                case 1:
                    break;
                case  mRecieve_SUCCED:
                    addLeftItem(mRecieve);
                    break;
                case CONNECT_SUCCED:
                    Log.i(TAG,"接收成功");
                     new MyThread().start(); //开启下面的线程
                    break;

            }
        }
    };

//新开的一个接收的线程
    class MyThread extends Thread{
        @Override
        public void run() {
            while (true){
            try {
              //获取到的数据
                int read = input.read();
                //因为不允许在子线程更新UI所以用handler
                 handler.sendEmptyMessage(mRecieve_SUCCED);
                 Log.i(TAG+"数据是",Integer.toHexString(read));
                } catch (IOException e) {
                    e.printStackTrace();
                Log.i(TAG,"异常"+e);
                }

            }}

        }




    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        if(keyCode== KeyEvent.KEYCODE_BACK){
            AlertDialog.Builder alertDialog=new AlertDialog.Builder(MainActivity.this);
            alertDialog.setTitle("确认!");
            alertDialog.setMessage("        确定退出智能锁控制吗");
            alertDialog.setPositiveButton("否",new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface arg0, int arg1) {

                }
            });
            alertDialog.setNegativeButton("是", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface arg0, int arg1) {

                    MainActivity.this.finish();
                }
            });
            alertDialog.show();
        }
        return false;
    }

}

运行效果:

这里写图片描述


完美通讯~~

单片机多功能调试助手分两个版本,分别是:含CH375 DLL版本、无DLL版本,它们的区别主要体现在是否对CH37X USB提供支持。为了减少对服务器的负担,现在单片机多功能调试助手只提供无DLL的版本,即不对CH372/CH375 USB芯片提供支持,只支持HID USB设备!含有DLL版本的暂只提供给购买开发板的买家! 在此感谢网络上众多的朋友的反馈与建议,没有他们的支持就没有该软件的茁壮成长!本人会继续努力进行更新,为大家提供一个功能完善与稳定的调试工具,为大家节省不必要的时间。 本软件的使用手册可到www.smartmcu.com进行下载! 作者: 温子祺 联系方式:wenziqi@hotmail.com或wenziqi@gmail.com 博客: http://www.cnblogs.com/wenziqi 淘宝: http://shop61791934.taobao.com/ 官网: www.smartmcu.com 注:软件版权归作者所有,未经作者允许,禁止用于商业用途,违者必究! ===================================================== 2015-12-12 (1)内嵌了蓝牙调试助手功能。 ===================================================== 2015-12-8 (1)在Win7以上系统执行更稳定,默认支持管理员运行。 ===================================================== 2013-07-23 (1)增强稳定性 ===================================================== 2012-05-21 (1)优化了Ping和TCP连接过程 (2)开放了检验源码 ===================================================== 2012-02-06 (1)发送区域支持保存发送数据256字节 ===================================================== 2011-10-17 (1)帧换行功能可调 ===================================================== 2011-07-10 (1)串口、USB、网咯接收数据时能够自动对\n和\r\n进行识别 注意:Windows默认对'\r\n'进行换行的 Linux 默认对‘\n’进行换行的 ===================================================== 2011-07-26 (1)修正了PING会出现大延时的问题 (2)修正了网络调试发送计数错误的BUG ===================================================== 2011-07-24 (1)数据校验修正和添加了部分功能 (2)服务器支持多项发送 ===================================================== 2011-07-10 (1)串口、USB、网络接收数据时能够自动对\n和\r\n进行识别 注意:Windows默认对'\r\n'进行换行的 Linux 默认对‘\n’进行换行的 ===================================================== 2011-05-05 (1)串口调试添加Modem模式 ===================================================== 2011-04-25 (1)修正Ping过慢的BUG ===================================================== 2011-04-20 (1)在接收区添加了右键菜单(清空、显示控制、文本输出控制) ===================================================== 2011-04-13 (1)修正串口调试隐藏BUG (2)提升了界面启动的速度 ===================================================== 2011-04-07 (1)发送数据支持回车换行 (2)修正了C51代码向导的BUG ===================================================== 2011-03-31 (1)修改了清空发送和清空接收的BUG ===================================================== 2011-03-24 (1)修改了下载功能 (2)修正了HexToText 和 HexToBin ===================================================== 2011-03-03 (1)在C51代码向导当中增加了计算软件延时的功能 (2)修正了通信端口监视不正常的BUG (3)解决了回车键会退出界面的BUG ===================================================== 2011-02-28 (1)在串口、USB、网络接收数据的过程中,能够正确显示当前接收数据的速度 (2)在C51代码向导当中增加了输出为Keil工程的功能,方便编译 ===================================================== 2011-02-25 (1)增加了双击文本框可以保存数据记录的功能 (2)增加了Hex/Bin文件输出文本的功能 ===================================================== 2011-02-20 (1)修正了在连续发送的逻辑BUG (2)修正了串口监视关闭时发生内存错误的BUG ===================================================== 2011-02-13 (1)修正了在无USB HID设备的情况下还能够打开USB设备的BUG ===================================================== 2011-02-11 (1)修正了界面风格,防止在不同的系统显示不一致! ===================================================== 2011-02-07 (1)修复了部分BUG,如串口高级监视、代码生成等。 (2)编码转换增强、调试工具增强(支持帧换行,方便分析数据)! (3)USB支持自动寻找设备! ===================================================== 2011-01-31 (1)添加了串口高级监视功能,能够监视其他应用程序的串口通信。 (2)添加了8051单片机代码生成功能。(懒人的必备O(∩_∩)O哈哈~) ===================================================== 2011-01-15 (1)修正了发送大量数据出错的BUG ===================================================== 2011-01-13 (1)修正了串口、USB、网络监视的BUG ===================================================== 2011-01-08 (1)新增加COM、USB、NET多项发送功能,更加利于调试! ===================================================== 2011-01-02 (1)调整了界面,让其显示更加多的数据! ===================================================== 2010-12-28 (1)修正Hex接收会出错的BUG ===================================================== 2010-12-21 (1)完美支持中文字数据收发 ===================================================== 2010-12-14 (1)修正COM、USB、NET、SERVER接收数据时会出现乱码的BUG (2)优化了线程接收数据的策略 ===================================================== 2010-12-10 (1)支持USB、网络接口监视 (2)优化了自动升级策略 (3)增强了位图输出16进制数功能(支持单色图、16位图、24位图) (4)具有自动保存参数功能 ===================================================== 2010-12-02 (1)修正了串口检测、接收策略, (2)添加了串口监视功能 (3)添加了BCC校验 (4)增强字模制作功能(支持特殊字符、英文、汉字等) ===================================================== 2010-11-29 (1)添加了记录发送、接收字节数的统计功能 (2)添加了自动检测新版本的功能 ===================================================== 2010-11-25 (1)添加了连续发送的功能 (2)添加了在线下载新版本的功能 (3)完善服务器功能 (4)完善了字模输出(摆脱外接字符文件) (5)完善了位图转16进制文件功能,可保存为C文件 ===================================================== 2010-11-02 (1)添加了服务器功能,网络调试更方便! (2)界面更加宽阔,显示更多的信息! ===================================================== 2010-09-26 (1)修正了单片机功能助手运行时间长导致的假死问题。(多谢网友提供宝贵的意见) ===================================================== 2010-09-01 (1)修正了串口类,提升接收数据的效率。 ===================================================== 2010-08-03 (1)在检验值计算支持TCP/IP校验和计算 (2)在NET调试新增加了Ping功能 ===================================================== 2010-07-28 (1)修正CRC计算错误 ===================================================== 2010-07-15 (1)修正打开关闭串口,接收数据文本框数据丢失 (2)串口对话框更为简洁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半颗心脏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值