Android网络编程Demo

1、前言
      前一段时间答应了师弟们写一个Android网络请求工具,由于项目的原因一直拖着。今天抽空写一个Demo演示一下,顺便复习一下Android的基本知识点。如有不足之处,请多多指点,谢谢(^_^)

2、HttpUrlConnection与HttpClient的区别
     目前android实现网络请求有两种方式分别是HttpUrlConnection和HttpClient。
(1)HttpUrlConnection是java的核心类(这个类能在Android和java之间都能使用)
HttpClient是Apache的开源项目,在Android之中已经默认集成此项目,在标准的java中不能直接使用(如果要使用需要引入Apache的项目包)

(2)HttpUrlConnection默认支持GZIP压缩,HttpClient默认不支持

根据官方指引Android版本2.2以后推荐使用HttpUrlConnection。嘿嘿!作为一名Android开发人员,官方的大腿还是得抱紧的啊(开玩笑>_<)

3、NetworkRequestTool的使用方法

按照惯例还是看一看项目结构,有两个java文件,不过重点是NetworkReuqestTool文件,我对HttpUrlConnection的基本操作做了封装,基本满足日常网络操作,需要使用的同学,直接将此文件Copy到自己的项目中即可。



(1)get请求的使用
先new一个实例,需要传入一个接口,必须重写三个方法,分别是networkSuccess,networkFailure,networkError。
其中networkSuccess方法传进来两个参数,resultString的访问指定地址成功之后返回的结果值,serverHeaderFieldBeanList (注意!这个值有可能为空,建议在使用之前判断是否为空)包含了服务器传回来的Cookies值和Headers值等等。

networkError方法,会传入一个errorString字符串。会大概告知错误的原因。

得到实例之后,调用getNetworkRequest传入一个网络地址(必传)和一个请求头参数(可以为空)。即可完成网络操作。
NetworkRequestTool networkRequestTool = new NetworkRequestTool(new NetworkRequestTool.NetworkCallbackListener() {
    @Override
    public void networkSuccess(String resultString, List<NetworkRequestTool.ServerHeaderFieldBean> serverHeaderFieldBeanList) {

        for (int i = 0; i < serverHeaderFieldBeanList.size(); i++) {
            NetworkRequestTool.ServerHeaderFieldBean serverHeaderFieldBean = serverHeaderFieldBeanList.get(i);

            Log.e("----------->", serverHeaderFieldBean.getKey() + "   :  " + serverHeaderFieldBean.getValue());
        }


        txt.setText(resultString);
        Log.e("----------->", resultString);
    }

    @Override
    public void networkFailure() {
        Log.e("----------->", "网络失败");
    }

    @Override
    public void networkError(String errorString) {

Toast.makeText(getApplication(), errorString, Toast.LENGTH_SHORT).show();
    }
});
networkRequestTool.getNetworkRequest("http://www.baidu.com", null);


(2)post请求的使用
这个基本和上面的用法差不多,不过主要讲讲调用post请求的postNetworkRequest方法。
这个方法有两种类型,分别如下
第一种类型

public void postNetworkRequest(String urlString, HashMap<String, String> requestParamsMap, HashMap<String, String> jsonBody, HashMap<String, String> setRequestPropertyMap)

第二种类型
public void postNetworkRequest(String urlString, String requestParamsString, String jsonBody, HashMap<String, String> setRequestPropertyMap)

两种方法都是有四个参数需要传入
第一个,当然是指定的网络地址。
第二个,是(键值对&)格式的请求参数。
第三个,是json格式的键值对。
第四个,是设置请求头参数(一般可以不设置,传一个null)。

在第一种类型中,其实是将requestParamsMap,jsonBody做了进步一个的处理将其边为字符串,再调用第二种类型的方法。
例如:
HashMap<String, String> map = new HashMap<>();
map.put("username", "C002563");
map.put("password", "888");

传入给第一种类型的方法。里面的源码将会将这些键值对拼接成一个字符串username=C002563&password=888,再调用第二个方法,将其传入。
jsonBody也是如此,将这些键值对拼接成一个json格式{"username":"C002563","password":"888"},再调用第二个方法,将其传入。

HashMap<String, String> map = new HashMap<>();
map.put("username", "C002563");
map.put("password", "888");

NetworkRequestTool networkRequestTool = new NetworkRequestTool(new NetworkRequestTool.NetworkCallbackListener() {
    @Override
    public void networkSuccess(String jsonString, List<NetworkRequestTool.ServerHeaderFieldBean> serverHeaderFieldBeanList) {
        txt.setText(jsonString);
        Log.e("----------->", jsonString);
    }

    @Override
    public void networkFailure() {

    }

    @Override
    public void networkError(String errorString) {

    }
});
networkRequestTool.postNetworkRequest("网络地址", map, null, null);

当然你也可以这么写,不用HashMap了,咋们自己拼接了字符串,传进去


new NetworkRequestTool(new NetworkRequestTool.NetworkCallbackListener() {
    @Override
    public void networkSuccess(String jsonString, List<NetworkRequestTool.ServerHeaderFieldBean> serverHeaderFieldBeanList) {
        txt.setText(jsonString);
        Log.e("----------->", jsonString);
    }

    @Override
    public void networkFailure() {

    }

    @Override
    public void networkError(String errorString) {
        Toast.makeText(getApplication(), errorString, Toast.LENGTH_SHORT).show();
    }
}).postNetworkRequest("网络地址", "username=C002563&password=888", null, null);

(3)post包含JSON格式的使用
这个例子的,我演示了一下请求头的设置以及服务器返回给我的Heardes和Cookies。
请求头和JSON格式,都是自己new一个HashMap,写入自己键值对。即可

主要讲讲serverHeaderFieldBeanList的使用。源码里面写了一个静态的实体类用来存放服务器返回的Headers和Cookies的键值对。
先判断是否为空(因为有些网络请求,服务器可能没有返回这些参数值。)由于ServerHeaderFieldBean实体类是在NetworkRequestTool里面,因此需要NetwrokRequestTool.ServerHeadersFieldBean。接着循环打印这些参数出来(你也可以根据需求实现自己的操作)。
//目标地址
String url = "http://web.jingoal.com/module/common/Login.cjson?client=win&puse=win";

//设置请求头参数
HashMap<String, String> setRequestPropertyMap = new HashMap<>();
//要求服务器返回JSON格式给我
setRequestPropertyMap.put("Content-Type", "application/json; charset=utf-8");

//设置请求json数据
HashMap<String, String> jsonBodyMap = new HashMap<>();
jsonBodyMap.put("login_name", "admin@3028216");
jsonBodyMap.put("old_token", "");
jsonBodyMap.put("pswd", "3dd635a808ddb6dd4b6731f7c409d53dd4b14df2");

NetworkRequestTool networkRequestTool = new NetworkRequestTool(new NetworkRequestTool.NetworkCallbackListener() {
    @Override
    public void networkSuccess(String jsonString, List<NetworkRequestTool.ServerHeaderFieldBean> serverHeaderFieldBeanList) {
        //判断是否为空
        if (serverHeaderFieldBeanList != null) {
            //获取服务器返回的Headers和Cookies
            for (int i = 0; i < serverHeaderFieldBeanList.size(); i++) {
                NetworkRequestTool.ServerHeaderFieldBean serverHeaderFieldBean = serverHeaderFieldBeanList.get(i);

                Log.e("----------->我的测试", serverHeaderFieldBean.getKey() + "   :  " + serverHeaderFieldBean.getValue());
            }
        }

        txt.setText(jsonString);
        Log.e("----------->", jsonString);
    }

    @Override
    public void networkFailure() {
        Log.e("----------->", "网络失败");
    }

    @Override
    public void networkError(String errorString) {
        Log.e("----------->", "网络错误");
    }
    //在这里设置参数
});

networkRequestTool.postNetworkRequest(url, null, jsonBodyMap, setRequestPropertyMap);

至此,整个类的基本操作就结束了。

接下来看看Demo的效果图


4、NetworkRequestTool源码
这个类的源码,设计比较简单,不再多说了。
package sms.edward.per.myapplication;

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

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * description:网络请求工具
 *
 * 此Demo博客地址:http://blog.csdn.net/u012814441/article/details/49643865
 * <p/>
 * author:Edward
 * <p/>
 * 2015/10/26
 */
public class NetworkRequestTool {
    private final String CONNECTION_NETWORK_FAILURE = "网络连接失败,请检查网络连接!";
    private final String URL_PARSE_FAILURE = "URL解析错误";
    private final String NETWORK_RESPONSE_FAILURE = "网络响应失败";
    //连接时间,默认(5秒)
    private int connTime = 5;
    //读取时间,默认(5秒)
    private int readTime = 5;
    //返回数据成功
    private int CONNECTION_SUCCEED = 200;
    //编码类型,默认为UTF-8
    private String encodingType = "utf-8";
    //成功获取返回结果值
    private final int GET_RESULT_STRING_SUCCEED = 1;
    //获取返回结果值失败
    private final int GET_RESULT_STRING_FAILURE = 0;
    //网络错误
    private final int ERROR = 2;
    //回调监听接口
    private NetworkCallbackListener networkCallbackListener;
    //线程池变量
    private ExecutorService executorService;
    //是否开启线程池
    private boolean isOpenThreadPool = true;
    //线程变量
    private Thread thread;
    //
    private List<ServerHeaderFieldBean> serverHeaderFieldList;

    //设置编码格式
    public void setEncodingType(String encodingType) {
        this.encodingType = encodingType;
    }

    //设置连接时间
    public void setConnTime(int connTime) {
        if (connTime > 0) {
            this.connTime = connTime;
        } else {
            Log.e("------------->", "设置连接时间不能少于0");
        }
    }

    //设置读取时间
    public void setReadTime(int readTime) {
        if (readTime > 0) {
            this.readTime = readTime;
        } else {
            Log.e("------------->", "设置读取时间不能少于0");
        }
    }

    //设置回调接口
    public void setNetworkCallbackListener(NetworkCallbackListener networkCallbackListener) {
        this.networkCallbackListener = networkCallbackListener;
    }

    /**
     * 构造方法
     *
     * @param networkCallbackListener
     */
    public NetworkRequestTool(NetworkCallbackListener networkCallbackListener) {
        this(5, 5, 5, true, networkCallbackListener);
    }

    /**
     * 构造方法
     *
     * @param connTime
     * @param readTime
     * @param threadPoolNumber
     * @param isOpenThreadPool
     * @param networkCallbackListener
     */
    public NetworkRequestTool(int connTime, int readTime, int threadPoolNumber, boolean isOpenThreadPool, NetworkCallbackListener networkCallbackListener) {
        this.connTime = connTime;
        this.readTime = readTime;
        this.isOpenThreadPool = isOpenThreadPool;
        this.networkCallbackListener = networkCallbackListener;
        //初始化线程池(默认为5)
        executorService = Executors.newFixedThreadPool(threadPoolNumber);
    }

    /**
     * get网络请求(异步)没有包含设置请求头参数
     */
    public void getNetworkRequest(String url) {
        getNetworkRequest(url, null);
    }

    /**
     * get网络请求(异步)包含设置请求头参数
     */
    public void getNetworkRequest(String url, HashMap<String, String> setRequestPropertyMap) {
        if (TextUtils.isEmpty(url)) {
            Log.e("---------->", "url不能为空");
            return;
        }
        //是否使用线程池
        if (isOpenThreadPool) {
            executorService.submit(new GetThreadRunnable(url, setRequestPropertyMap));
        } else {
            thread = new Thread(new GetThreadRunnable(url, setRequestPropertyMap));
            //开启一个线程
            thread.start();
        }
    }

    /**
     * post网络请求(异步)
     *
     * @param urlString             访问地址
     * @param requestParamsMap      设置请求参数(字符串)
     * @param jsonBody              设置josn格式参数(字符串)
     * @param setRequestPropertyMap 设置请求头参数
     */
    public void postNetworkRequest(String urlString, HashMap<String, String> requestParamsMap, HashMap<String, String> jsonBody, HashMap<String, String> setRequestPropertyMap) {
        if (TextUtils.isEmpty(urlString)) {
            Log.e("---------->", "url不能为空");
            return;
        }
        //将HashMap的requestParamsMap拼接成一个字符串
        String resultRequestString = combineRequestParams(requestParamsMap);
        //将HashMap的jsonBody拼接成一个字符串。
        String resultJsonString = combineJsonBody(jsonBody);
        //调用
        postNetworkRequest(urlString, resultRequestString, resultJsonString, setRequestPropertyMap);
    }

    /**
     * post网络请求(异步)
     *
     * @param urlString             访问地址
     * @param requestParamsString   设置请求参数(HashMap)
     * @param jsonBody              设置josn格式参数(HashMap)
     * @param setRequestPropertyMap 设置请求头参数
     */
    public void postNetworkRequest(String urlString, String requestParamsString, String jsonBody, HashMap<String, String> setRequestPropertyMap) {
        if (isOpenThreadPool) {
            executorService.submit(new PostThreadRunnable(urlString, requestParamsString, jsonBody, setRequestPropertyMap));
        } else {
            thread = new Thread(new PostThreadRunnable(urlString, requestParamsString, jsonBody, setRequestPropertyMap));
            //开启一个线程
            thread.start();
        }
    }

    /**
     * GET的线程操作
     */
    public class GetThreadRunnable implements Runnable {
        private String urlString;
        private HashMap<String, String> setRequestPropertyMap;

        public GetThreadRunnable(String urlString, HashMap<String, String> setRequestPropertyMap) {
            this.urlString = urlString;
            this.setRequestPropertyMap = setRequestPropertyMap;
        }

        @Override
        public void run() {
            try {
                URL url = new URL(urlString);
                HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
                // 忽略缓存
                httpURLConnection.setUseCaches(false);
                //设置读取时间
                httpURLConnection.setReadTimeout(readTime * 1000);
                //设置连接时间
                httpURLConnection.setConnectTimeout(connTime * 1000);
                //提出Post请求
                httpURLConnection.setRequestMethod("GET");

                //设置请求头参数(请求参数必须写在Http正文之内)
                if (setRequestPropertyMap != null) {
                    setRequestProperty(httpURLConnection, setRequestPropertyMap);
                }

                //获取服务器返回Header
                getServerHeaderField(httpURLConnection);

                int responseCode = httpURLConnection.getResponseCode();

                if (responseCode == CONNECTION_SUCCEED) {
                    //此处才是发送数据的真正操作
                    InputStream inputStream = httpURLConnection.getInputStream();

                    String responseResult = inputStreamConvertString(inputStream);
                    if (!TextUtils.isEmpty(responseResult)) {
//                        Log.e("----------->网络请求工具最终返回值", responseResult);
                        handler.sendMessage(handler.obtainMessage(GET_RESULT_STRING_SUCCEED, responseResult));
                    } else {
                        handler.sendEmptyMessage(GET_RESULT_STRING_FAILURE);
                    }
                } else {
                    handler.sendMessage(handler.obtainMessage(ERROR, NETWORK_RESPONSE_FAILURE));
                }

            } catch (MalformedURLException e) {
                Log.e("--------------->", "Url字符串解析错误!");
                handler.sendMessage(handler.obtainMessage(ERROR, URL_PARSE_FAILURE));
            } catch (IOException e) {
                Log.e("--------------->", "网络连接失败,请检查网络连接!");
                handler.sendMessage(handler.obtainMessage(ERROR, CONNECTION_NETWORK_FAILURE));
            }
        }
    }

    /**
     * 获取服务器返回的Header(不可覆盖)
     *
     * @param httpURLConnection
     */
    private void getServerHeaderField(HttpURLConnection httpURLConnection) {
        //实例化
        serverHeaderFieldList = new ArrayList<>();
        String key = null;
        for (int i = 1; (key = httpURLConnection.getHeaderFieldKey(i)) != null; i++) {
            ServerHeaderFieldBean searverHeaderFieldBean = new ServerHeaderFieldBean();

            searverHeaderFieldBean.setKey(key);
            searverHeaderFieldBean.setValue(httpURLConnection.getHeaderField(key));
            Log.e("----->服务器HeaderField", key + ":" + httpURLConnection.getHeaderField(key));

            serverHeaderFieldList.add(searverHeaderFieldBean);
        }
    }

    /**
     * POST的线程操作
     */
    public class PostThreadRunnable implements Runnable {
        private String urlString;
        private String requestParamsString;
        private String jsonBody;
        private HashMap<String, String> setRequestPropertyMap;

        public PostThreadRunnable(String urlString, String requestParamsString, String jsonBody, HashMap<String, String> setRequestPropertyMap) {
            this.urlString = urlString;
            this.jsonBody = jsonBody;
            this.requestParamsString = requestParamsString;
            this.setRequestPropertyMap = setRequestPropertyMap;
        }

        @Override
        public void run() {
            try {
                URL url = new URL(urlString);
                HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
                httpURLConnection.setDoOutput(true);
                httpURLConnection.setDoInput(true);
                // 忽略缓存
                httpURLConnection.setUseCaches(false);
                //设置读取时间
                httpURLConnection.setReadTimeout(readTime * 1000);
                //设置连接时间
                httpURLConnection.setConnectTimeout(connTime * 1000);
                //提出Post请求
                httpURLConnection.setRequestMethod("POST");

                //设置请求头参数(请求参数必须写在Http正文之内)
                if (setRequestPropertyMap != null) {
                    setRequestProperty(httpURLConnection, setRequestPropertyMap);
                }

                //开启一个输出流(getOutputStream方法已经在里面调用connect方法)
                DataOutputStream dataOutputStream = new DataOutputStream(httpURLConnection.getOutputStream());
                //设置请求参数
                if (requestParamsString != null) {
                    dataOutputStream.write(requestParamsString.getBytes(encodingType));
                }

                //设置实体JSON参数
                if (jsonBody != null) {
                    dataOutputStream.write(jsonBody.getBytes(encodingType));
                    Log.e("------------>设置JSON:", jsonBody);
                }

                dataOutputStream.flush();
                dataOutputStream.close();

                //获取服务器返回Header
                getServerHeaderField(httpURLConnection);

                //得到网络响应码
                int responseCode = httpURLConnection.getResponseCode();

                if (responseCode == CONNECTION_SUCCEED) {
                    //此处才是发送数据的真正操作
                    InputStream inputStream = httpURLConnection.getInputStream();

                    String responseResult = inputStreamConvertString(inputStream);
                    if (!TextUtils.isEmpty(responseResult)) {
//                        Log.e("----------->网络请求工具最终返回值", responseResult);
                        handler.sendMessage(handler.obtainMessage(GET_RESULT_STRING_SUCCEED, responseResult));
                    } else {
                        handler.sendEmptyMessage(GET_RESULT_STRING_FAILURE);
                    }
                } else {
                    handler.sendMessage(handler.obtainMessage(ERROR, NETWORK_RESPONSE_FAILURE));
                }

            } catch (MalformedURLException e) {
                Log.e("--------------->", "Url字符串解析错误!");
                handler.sendMessage(handler.obtainMessage(ERROR, URL_PARSE_FAILURE));
            } catch (IOException e) {
                Log.e("--------------->", "网络连接失败,请检查网络连接!");
                handler.sendMessage(handler.obtainMessage(ERROR, CONNECTION_NETWORK_FAILURE));
            }
        }
    }

    /**
     * 将输入流转换为字符串
     *
     * @param inputStream
     * @return
     * @throws IOException
     */
    public String inputStreamConvertString(InputStream inputStream) throws IOException {
        if (inputStream == null) {
            return null;
        }
        //将输入流暂时写进缓冲区中
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        int length = 0;
        byte[] by = new byte[1024];
        while ((length = bis.read(by)) != -1) {
            bos.write(by, 0, length);
        }
        bos.flush();
        String result = new String(bos.toByteArray(), encodingType);

        bos.close();
        bis.close();

        return result;
    }

    /**
     * 拼接请求参数
     *
     * @return
     */
    public String combineRequestParams(HashMap<String, String> requestParamsMap) {
        if (requestParamsMap == null) {
            return null;
        }
        Iterator<Entry<String, String>> iterator = requestParamsMap.entrySet().iterator();

        StringBuffer stringBuffer = new StringBuffer();

        while (iterator.hasNext()) {
            Map.Entry<String, String> tempMap = iterator.next();

            stringBuffer.insert(0, tempMap.getKey() + "=" + tempMap.getValue() + "&");
        }
        //删除最后一个&
        stringBuffer.deleteCharAt(stringBuffer.length() - 1);

        System.out.println(stringBuffer.toString());

        Log.e("-------------->拼接请求参数:", stringBuffer.toString());
        return stringBuffer.toString();
    }

    /**
     * 拼接字符串
     *
     * @param jsonBody
     * @return
     */
    public String combineJsonBody(HashMap<String, String> jsonBody) {
        if (jsonBody == null) {
            return null;
        }
        Iterator<Entry<String, String>> iterator = jsonBody.entrySet().iterator();

        StringBuffer stringBuffer = new StringBuffer();

        stringBuffer.append("{");

        while (iterator.hasNext()) {
            Map.Entry<String, String> map = iterator.next();
            // 将键值对插入到第1个字符之后(从0开始)
            stringBuffer.insert(1, "\"" + map.getKey() + "\":\"" + map.getValue() + "\",");
        }
        //删除最后一个逗号
        stringBuffer.deleteCharAt(stringBuffer.length() - 1);
        stringBuffer.append("}");
        Log.e("------------>拼接请求JSON参数", stringBuffer.toString());

        return stringBuffer.toString();
    }


    /**
     * 设置请求头参数(不能被覆盖)注意请求头参数的设置,因为它有可能决定了服务器返回值的类型
     *
     * @param httpURLConnection
     */
    private void setRequestProperty(HttpURLConnection httpURLConnection, HashMap<String, String> setRequestPropertyMap) {
        Iterator<Entry<String, String>> iterator = setRequestPropertyMap.entrySet().iterator();
        //设置遍历了一遍Map
        while (iterator.hasNext()) {
            Map.Entry<String, String> map = iterator.next();

            Log.e("------------>设置请求头:", map.getKey() + "    " + map.getValue());
            httpURLConnection.setRequestProperty(map.getKey(), map.getValue());
        }
    }

    /**
     * Handler处理子线程返回的数据
     */
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //获取子线程发送过来的字符串
            String resultString = (String) msg.obj;
            switch (msg.what) {
                case GET_RESULT_STRING_SUCCEED:
                    //将子线程得到的结果值回调出去
                    networkCallbackListener.networkSuccess(resultString, serverHeaderFieldList);
                    break;
                case GET_RESULT_STRING_FAILURE:
                    networkCallbackListener.networkFailure();
                    break;
                case ERROR:
                    networkCallbackListener.networkError(resultString);
                    break;
            }
        }
    };

    /**
     * 回调接口
     */
    public interface NetworkCallbackListener {
        //网络连接成功
        void networkSuccess(String returnString, List<ServerHeaderFieldBean> serverHeaderFieldBeanList);

        //网络连接失败
        void networkFailure();

        //网络错误
        void networkError(String errorString);
    }

    /**
     * HeaderField实体类,用来保存服务器返回的Headers和Cookies
     */
    public static class ServerHeaderFieldBean {
        private String key;
        private String value;

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }
}

5、结束
     谢谢各位。

源码请戳这里Android网络编程Demo





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值