Android 蓝牙客户端实现

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.bingo.assistant.common.remoteControl.bluetooth.client;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import com.infrastructure.util.LogUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 蓝牙通信基础客户端框架
 * BluetoothBaseClient
 */
public class BBC {

    private static final UUID MY_UUID_SECURE = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
    //private static final UUID MY_UUID_INSECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    private static final UUID MY_UUID_INSECURE = UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");


    private static final int MSG_BLUETOOTH_START = 0;           //蓝牙开始
    private static final int MSG_BLUETOOTH_CONNECT = 1;         //蓝牙连接
    private static final int MSG_BLUETOOTH_CONNECTED = 2;       //蓝牙连接成功
    private static final int MSG_BLUETOOTH_CONNECT_TIMEOUT = 3; //蓝牙连接超时
    private static final int MSG_BLUETOOTH_CLOSE = 4;           //蓝牙连接关闭

    private final BluetoothAdapter mBluetoothAdapter;
    private ConnectManagerThread mConnectManagerThread;
    private BluetoothStatusChangeHandler mBluetoothStatusChangeHandler;
    private BluetoothReadHandler mBluetoothReadHandler;
    private volatile ConnectedHandler mConnectedHandler;

    private BluetoothDevice device;

    public BBC() {
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        HandlerThread handlerThread1 = new HandlerThread("BluetoothStatusChangeHandler");
        handlerThread1.start();
        mBluetoothStatusChangeHandler = new BluetoothStatusChangeHandler(handlerThread1.getLooper());

        HandlerThread handlerThread2 = new HandlerThread("BluetoothReadHandler");
        handlerThread2.start();
        mBluetoothReadHandler = new BluetoothReadHandler(handlerThread2.getLooper());
    }

    private void clean(){
        if(mBluetoothStatusChangeHandler != null){
            mBluetoothStatusChangeHandler.removeMessages(MSG_BLUETOOTH_START);
            mBluetoothStatusChangeHandler.removeMessages(MSG_BLUETOOTH_CONNECT);
            mBluetoothStatusChangeHandler.removeMessages(MSG_BLUETOOTH_CLOSE);
            mBluetoothStatusChangeHandler.removeMessages(MSG_BLUETOOTH_CONNECT_TIMEOUT);
            mBluetoothStatusChangeHandler.removeMessages(MSG_BLUETOOTH_CONNECTED);
        }
    }

    /**
     * 设置设备
     */
    public synchronized void switchDevice(BluetoothDevice device){
        this.device = device;
    }



    private static final int READ_OPEN = 1;
    private static final int READ_CLOSE = 2;
    private static final int READ = 3;

    private class BluetoothReadHandler extends Handler{
        private AtomicBoolean isOpen = new AtomicBoolean(false);

        BluetoothReadHandler(Looper looper){
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            int what = msg.what;
            switch (what){
                case READ:
                    if(isOpen.get() && onPushListener != null){
                        LogUtils.bluetooth("读取流....");
                        try{
                            if(mConnectedHandler != null){
                                mConnectedHandler.read();
                            }
                            sendEmptyMessage(READ);
                        }catch (Exception e){
                            e.printStackTrace();
                            bluetoothErrorHandler(ERROR_BLUETOOTH_READ, e);
                        }
                    }
                    break;

                case READ_OPEN:
                    isOpen.set(true);
                    synchronized (this){
                        notify();
                    }
                    sendEmptyMessage(READ);
                    break;

                case READ_CLOSE:
                    removeMessages(READ);
                    isOpen.set(false);
                    break;
            }
        }

        public void open(){
            synchronized (this){
                try {
                    sendEmptyMessage(READ_OPEN);
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public void close(){
            sendEmptyMessage(READ_CLOSE);
            isOpen.set(false);
        }
    }


    private class BluetoothStatusChangeHandler extends Handler{
        private int BBCmsg;

        BluetoothStatusChangeHandler(Looper looper){
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            int what = msg.what;
            switch (what){
                case MSG_BLUETOOTH_START:
                    closeBluetoothDeviceHandler();
                    boolean openSucess = openBluetoothDeviceHandler();
                    if(openSucess){
                        BBCmsg = MSG_BLUETOOTH_START; //设置状态为蓝牙打开
                        bluetoothStatusChangeHandler(STATUS_BLUETOOTH_OPEN, "蓝牙打开!");
                        sendEmptyMessage(MSG_BLUETOOTH_CONNECT);
                    }else {
                        bluetoothErrorHandler(ERROR_BLUETOOTH_UNABLE, new RuntimeException("蓝牙不可用!"));
                    }

                    break;

                case MSG_BLUETOOTH_CONNECT:

                    if(BBCmsg == MSG_BLUETOOTH_START){
                        BBCmsg = MSG_BLUETOOTH_CONNECT;
                        connectBluetoothDeviceHandler();
                        bluetoothStatusChangeHandler(STATUS_BLUETOOTH_CONNECTING, "蓝牙正在建立连接!");
                    }else {
                        LogUtils.bluetooth("状态不符合,不做处理:" + BBCmsg);
                    }

                    break;

                case MSG_BLUETOOTH_CONNECTED:
                    //开始建立流
                    if(BBCmsg == MSG_BLUETOOTH_CONNECT){
                        Object o = msg.obj;
                        if(o != null){
                            BluetoothSocket mmSocket = (BluetoothSocket) o;
                            mConnectedHandler = new ConnectedHandler(mmSocket);
                            if(mConnectedHandler.connectedSuccess()){
                                //打开读数据
                                mBluetoothReadHandler.open();
                                BBCmsg = MSG_BLUETOOTH_CONNECTED;
                                bluetoothStatusChangeHandler(STATUS_BLUETOOTH_CONNECTED, "蓝牙连接成功!");
                            }else {
                                bluetoothErrorHandler(ERROR_BLUETOOTH_CONNECT, new RuntimeException("蓝牙连接异常!"));
                            }
                        }else {
                            bluetoothErrorHandler(ERROR_BLUETOOTH_CONNECT, new RuntimeException("蓝牙连接异常!"));
                        }
                    }else {
                        LogUtils.bluetooth("状态不符合,不做处理:" + BBCmsg);
                    }
                    break;

                case MSG_BLUETOOTH_CONNECT_TIMEOUT:


                    break;

                case MSG_BLUETOOTH_CLOSE:
                    closeBluetoothDeviceHandler();
                    BBCmsg = MSG_BLUETOOTH_CLOSE;
                    bluetoothStatusChangeHandler(STATUS_BLUETOOTH_CLOSE, "蓝牙关闭!");
                    break;
            }
        }
    }

    /**
     * 打开蓝牙
     */
    private boolean openBluetoothDeviceHandler() {
        if (mBluetoothAdapter == null) {
            //蓝牙模块不可用
            return false;
        }
        //确保蓝牙打开
        if (!mBluetoothAdapter.isEnabled()) {
            mBluetoothAdapter.enable();
            int MAX_COUNT = 10;
            int tryCount = 0;
            boolean enable = false;
            while(!enable){
                //检查是否打开完成
                enable = mBluetoothAdapter.isEnabled();
                if(tryCount > MAX_COUNT){
                    return false;
                }
                try {
                    tryCount++;
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        return true;
    }

    /**
     * 关闭蓝牙
     */
    public void closeBluetoothDeviceHandler() {
        //停止读数据
        mBluetoothReadHandler.close();
        LogUtils.bluetooth("停止蓝牙连接服务");
        if (mConnectedHandler != null) {
            mConnectedHandler.cancel();
            mConnectedHandler = null;
        }

        if (mConnectManagerThread != null) {
            mConnectManagerThread.cancel();
            mConnectManagerThread = null;
        }
    }

    /**
     * 连接远程蓝牙设备
     */
    public synchronized void connectBluetoothDeviceHandler() {
        closeBluetoothDeviceHandler();
        mConnectManagerThread = new ConnectManagerThread();
        mConnectManagerThread.start();
    }

    /**
     * 打开
     */
    public void open(){
        mBluetoothStatusChangeHandler.sendEmptyMessage(MSG_BLUETOOTH_START);
    }

    /**
     * 关闭
     */
    public void close(){
        LogUtils.bluetooth("发送蓝牙关闭信息!");
        clean();
        mBluetoothStatusChangeHandler.sendEmptyMessage(MSG_BLUETOOTH_CLOSE);
    }

    private class ConnectManagerThread extends Thread{
        //private final ConnectThread mSecureThread;
        private final ConnectThread mInSecureThread;

        ConnectManagerThread(){
            //mSecureThread = new ConnectThread(device, true);
            mInSecureThread = new ConnectThread(device, false);
        }

        public void cancel(){
            //mSecureThread.cancel();
            mInSecureThread.cancel();
        }

        @Override
        public void run() {
            //Future<BluetoothSocket> f1 = pool.submit(mSecureThread);
            Future<BluetoothSocket> f2 = pool.submit(mInSecureThread);

            try {
                //BluetoothSocket socket1 = f1.get();
                BluetoothSocket socket2 = f2.get(20, TimeUnit.SECONDS);
                LogUtils.bluetooth("获得SOCKET");
//                if(socket1 != null || socket2 != null){
//                    //启动链接流程
//                    Message message = Message.obtain();
//                    if(f1.get() != null){
//                        message.obj = f1.get();
//                    }else {
//                        message.obj = f2.get();
//                    }
//                    message.what = MSG_BLUETOOTH_CONNECTED;
//                    mBluetoothStatusChangeHandler.sendMessage(message);
//                }else {
//                    bluetoothErrorHandler(ERROR_BLUETOOTH_CONNECT, new RuntimeException("蓝牙服务建立失败!"));
//                }
                if(socket2 != null){
                    Message message = Message.obtain();
                    if(f2.get() != null){
                        message.obj = f2.get();
                        message.what = MSG_BLUETOOTH_CONNECTED;
                        mBluetoothStatusChangeHandler.sendMessage(message);
                    }else {
                        bluetoothErrorHandler(ERROR_BLUETOOTH_CONNECT, new RuntimeException("蓝牙服务建立失败!"));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                bluetoothErrorHandler(ERROR_BLUETOOTH_CONNECT, e);
            }
        }
    }

    private static final ExecutorService pool = Executors.newFixedThreadPool(2);
    private class ConnectThread implements Callable<BluetoothSocket> {
        private final BluetoothSocket mmSocket;
        private String mSocketType;

        public ConnectThread(BluetoothDevice device, boolean secure) {
            BluetoothSocket tmp = null;
            mSocketType = secure ? "Secure" : "Insecure";
            try {
                if (secure) {
                    tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);
                } else {
                    tmp = device.createInsecureRfcommSocketToServiceRecord(MY_UUID_INSECURE);
                }
            } catch (IOException e) {
                LogUtils.bluetooth("Socket Type: " + mSocketType + "create() failed  " + e);
            }
            mmSocket = tmp;
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                LogUtils.bluetooth("close error :" + e.getMessage());
                bluetoothErrorHandler(ERROR_BLUETOOTH_CLOSE, e);
            }
        }

        @Override
        public BluetoothSocket call() throws Exception {
            mBluetoothAdapter.cancelDiscovery();
            try {
                LogUtils.bluetooth("等待服务端响应  mmSocket :" + mmSocket);

                //开始连接
                mmSocket.connect();
                LogUtils.bluetooth("收到服务端响应");

                return mmSocket;

            } catch (IOException e) {
                e.printStackTrace();
                LogUtils.bluetoothError(e);
                cancel();
                bluetoothErrorHandler(ERROR_BLUETOOTH_CONNECT, e);
                return null;
            }
        }
    }

    private class ConnectedHandler {
        private static final int READ_MAX = 256;
        private final byte[] bytes = new byte[READ_MAX];
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;
        private final String mmDevice;

        public ConnectedHandler(BluetoothSocket socket) {
            mmSocket = socket;
            mmDevice = socket.getRemoteDevice().getName();
            InputStream tmpIn = null;
            OutputStream tmpOut = null;
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (Exception e) {
                e.printStackTrace();
            }
            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public boolean connectedSuccess(){
            return mmInStream != null && mmOutStream != null;
        }

        public void read() throws Exception {
            int readCount = mmInStream.read(bytes);
            if(onPushListener != null){
                byte[] datas = new byte[readCount];
                System.arraycopy(bytes, 0, datas, 0, readCount);
                onPushListener.onDecode(datas);
            }
        }

        public void write(byte[] buffer) throws Exception {
            mmOutStream.write(buffer);
            if(onBluetoothStatusChangeListener != null) {
                onBluetoothStatusChangeListener.bluetoothSendSuccess(buffer);
            }
        }

        public void cancel() {
            try {
                mmOutStream.close();
                mmInStream.close();
                mmSocket.close();
            } catch (Exception e) {
                e.printStackTrace();
                bluetoothErrorHandler(ERROR_BLUETOOTH_CLOSE, e);
            }
        }

        public String getMmDevice() {
            return mmDevice;
        }
    }

    /**
     * 发送消息
     */
    public void write(byte[] out) {
        try{
            if(mConnectedHandler != null) {
                mConnectedHandler.write(out);
            }
        }catch (Exception e){
            e.printStackTrace();
            bluetoothErrorHandler(ERROR_BLUETOOTH_WRITE, e);
        }
    }


   /**
    * 蓝牙状态变化
    */
    private static final int ERROR_BLUETOOTH_UNABLE = 1;            //蓝牙不可用异常
    private static final int ERROR_BLUETOOTH_CLOSE = 2;             //蓝牙关闭异常
    private static final int ERROR_BLUETOOTH_READ = 3;              //蓝牙读取异常
    private static final int ERROR_BLUETOOTH_WRITE = 4;             //蓝牙写异常
    private static final int ERROR_BLUETOOTH_CONNECT = 5;           //蓝牙连接异常

    private void bluetoothErrorHandler(int statusCount, Exception e){
        switch (statusCount){
            case ERROR_BLUETOOTH_UNABLE:
                if(onBluetoothErrorListener != null){
                    onBluetoothErrorListener.bluetoothUnableError(e);
                }
                break;

            case ERROR_BLUETOOTH_CONNECT:
                if(onBluetoothErrorListener != null){
                    onBluetoothErrorListener.bluetoothConnectError(e);
                }
                break;

            case ERROR_BLUETOOTH_READ:
                if(onBluetoothErrorListener != null){
                    onBluetoothErrorListener.bluetoothReadError(e);
                }
                break;

            case ERROR_BLUETOOTH_WRITE:
                if(onBluetoothErrorListener != null){
                    onBluetoothErrorListener.bluetoothWriteError(e);
                }
                break;

            case ERROR_BLUETOOTH_CLOSE:
                if(onBluetoothErrorListener != null){
                    onBluetoothErrorListener.bluetoothCloseError(e);
                }
                break;
            default:
                break;
        }
    }


    private static final int STATUS_BLUETOOTH_OPEN = 6;         //蓝牙打开
    private static final int STATUS_BLUETOOTH_CONNECTING = 7;    //蓝牙正在建立连接
    private static final int STATUS_BLUETOOTH_CONNECTED = 8;    //蓝牙连接建立
    private static final int STATUS_BLUETOOTH_CLOSE = 9;       //蓝牙关闭

    private void bluetoothStatusChangeHandler(int statusCount, String statusMessage){
        LogUtils.bluetooth(statusMessage + "  状态号:  " + statusCount);
        switch (statusCount){
            case STATUS_BLUETOOTH_OPEN:
                if(onBluetoothStatusChangeListener != null){
                    //获取本机蓝牙信息
                    String localAddress = mBluetoothAdapter.getAddress();
                    String localName = mBluetoothAdapter.getName();
                    LogUtils.bluetooth("本机蓝牙信息[address:" + localAddress + ",name:" + localName);
                    onBluetoothStatusChangeListener.bluetoothOpened(statusMessage, localAddress, localName);
                }
                break;

            case STATUS_BLUETOOTH_CONNECTED:
                if(onBluetoothStatusChangeListener != null){
                    LogUtils.bluetooth("蓝牙建立连接:" + mConnectedHandler.getMmDevice());
                    onBluetoothStatusChangeListener.bluetoothConnected(mConnectedHandler.getMmDevice());
                }
                break;

            case STATUS_BLUETOOTH_CLOSE:
                if(onBluetoothStatusChangeListener != null){
                    onBluetoothStatusChangeListener.bluetoothClosed(statusMessage);
                }
                break;

            default:
                break;
        }
    }

    public void setOnBluetoothStatusChangeListener(OnBluetoothStatusChangeListener onBluetoothStatusChangeListener) {
        this.onBluetoothStatusChangeListener = onBluetoothStatusChangeListener;
    }
    private volatile OnBluetoothStatusChangeListener onBluetoothStatusChangeListener;

    interface OnBluetoothStatusChangeListener {
        void bluetoothOpened(String message, String localAddress, String localName);
        void bluetoothConnected(String message);
        void bluetoothClosed(String message);
        void bluetoothSendSuccess(byte[] b);
    }

    public void setOnBluetoothErrorListener(OnBluetoothErrorListener onBluetoothErrorListener) {
        this.onBluetoothErrorListener = onBluetoothErrorListener;
    }

    private volatile OnBluetoothErrorListener onBluetoothErrorListener;

    interface OnBluetoothErrorListener{
        void bluetoothUnableError(Exception error);
        void bluetoothConnectError(Exception error);
        void bluetoothReadError(Exception error);
        void bluetoothWriteError(Exception error);
        void bluetoothCloseError(Exception error);

    }

    public void setOnPushListener(OnPushListener onPushListener) {
        this.onPushListener = onPushListener;
    }

    private volatile OnPushListener onPushListener;

    interface OnPushListener{
        void onDecode(byte[] datas);
    }

}

package com.bingo.assistant.common.remoteControl.bluetooth.client;


import android.bluetooth.BluetoothDevice;

import com.infrastructure.util.LogUtils;

public class BBCCommander implements
        BBC.OnBluetoothStatusChangeListener,
        BBC.OnBluetoothErrorListener,
        BBC.OnPushListener{

    private static final BBC BBC = new BBC();

    @Override
    public void bluetoothUnableError(Exception error) {
        LogUtils.bluetooth("蓝牙不能使用!");
        if(onBluetoothStatusChangeListener != null){
            onBluetoothStatusChangeListener.bluetoothUnable(error);
        }
    }

    @Override
    public void bluetoothConnectError(Exception error) {
        LogUtils.bluetooth(error + "蓝牙连接异常!:" + onBluetoothStatusChangeListener);
        if(onBluetoothStatusChangeListener != null){
            onBluetoothStatusChangeListener.bluetoothError(error);
        }
    }

    @Override
    public void bluetoothReadError(Exception error) {
        LogUtils.bluetooth(error + "蓝牙读异常!");
        if(onBluetoothStatusChangeListener != null){
            onBluetoothStatusChangeListener.bluetoothError(error);
        }
    }

    @Override
    public void bluetoothWriteError(Exception error) {
        LogUtils.bluetooth(error + "蓝牙写异常!");
        if(onBluetoothStatusChangeListener != null){
            onBluetoothStatusChangeListener.bluetoothError(error);
        }
    }

    @Override
    public void bluetoothCloseError(Exception error) {
        LogUtils.bluetooth("蓝牙关闭异常!");
        if(onBluetoothStatusChangeListener != null){
            onBluetoothStatusChangeListener.bluetoothError(error);
        }
    }

    private static class SingletonInstance {
        private static final BBCCommander mInstance = new BBCCommander();
    }

    public static BBCCommander getInstance(){
        return SingletonInstance.mInstance;
    }

    @Override
    public void bluetoothOpened(String message, String localAddress, String localName) {
        LogUtils.bluetooth("蓝牙打开!");
        if(onBluetoothStatusChangeListener != null){
            onBluetoothStatusChangeListener.bluetoothOpened(message);
        }
    }

    @Override
    public void bluetoothConnected(String device) {
        LogUtils.bluetooth("蓝牙建立连接!");
        if(onBluetoothStatusChangeListener != null){
            onBluetoothStatusChangeListener.bluetoothConnected(device);
        }
    }

    @Override
    public void bluetoothClosed(String message) {
        LogUtils.bluetooth("蓝牙关闭!");
        if(onBluetoothStatusChangeListener != null){
            onBluetoothStatusChangeListener.bluetoothClosed(message);
        }
    }

    @Override
    public void bluetoothSendSuccess(byte[] b) {
        LogUtils.bluetooth("蓝牙发送成功!");
        if(onBluetoothStatusChangeListener != null){
            onBluetoothStatusChangeListener.bluetoothSendSuccess(b);
        }
    }

    public void switchDevice(BluetoothDevice device){
        BBC.switchDevice(device);
    }

    public void init(){
        BBC.setOnBluetoothStatusChangeListener(this);
        BBC.setOnBluetoothErrorListener(this);
        BBC.setOnPushListener(this);
    }

    public void open(){
        BBC.open();
    }

    public void close(){
        BBC.close();
    }

    public void write(byte[] bytes){
        BBC.write(bytes);
    }

    @Override
    public void onDecode(byte[] datas) {
        if(mOnDecoder != null){
            mOnDecoder.onDecode(datas);
        }
    }

    public void setOnBluetoothStatusChangeListener(OnBluetoothStatusChangeListener onBluetoothStatusChangeListener) {
        this.onBluetoothStatusChangeListener = onBluetoothStatusChangeListener;
    }

    private volatile OnBluetoothStatusChangeListener onBluetoothStatusChangeListener;
    public interface OnBluetoothStatusChangeListener{
        void bluetoothUnable(Exception e);
        void bluetoothError(Exception e);
        void bluetoothOpened(String message);
        void bluetoothConnected(String message);
        void bluetoothClosed(String message);
        void bluetoothSendSuccess(byte[] b);
    }

    private volatile OnDecoder mOnDecoder;
    public void setOnDecoder(OnDecoder mOnDecoder) {
        this.mOnDecoder = mOnDecoder;
    }

    public interface OnDecoder {
        void onDecode(byte[] bytes);
    }
}




  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值