okhttp

一.okhttp介绍

okhttp是一个第三方类库,用于android中请求网络。
这是一个开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献(该公司还贡献了Picasso和LeakCanary) 。
用于替代HttpUrlConnection和Apache HttpClient(android API23里已移除HttpClient)。

二.okhttp使用

1.依赖

使用okhttp要导入依赖

implementation 'com.squareup.okhttp3:okhttp:3.12.1'//okhttp依赖

2.okhttp做get请求json

使用时分为四步

  1. 获取client客户端对象
  2. 完成request请求
  3. client发送requset获取call对象
  4. call调用enqueue获取响应结果

其中enqueue方法参数为一个Callback对象
onFailure为请求失败时的方法
onResponse为请求成功时的方法

//1.获取client对象
OkHttpClient client = new OkHttpClient.Builder()
        .connectTimeout(60 * 1000, TimeUnit.SECONDS)//连接超时时间
        .readTimeout(60 * 1000, TimeUnit.SECONDS)//读取超时时间
        .writeTimeout(60 * 1000, TimeUnit.SECONDS)//写入超时时间
        .build();
//2.request请求
Request request = new Request.Builder()
        .url("http://www.qubaobei.com/ios/cf/dish_list.php?stage_id=1&limit=20&page=1")//请求地址
        .get()//请求方式
        .build();
//3.client发起request请求  ->  call连接
Call call = client.newCall(request);
//4.加入队列 ->获取响应
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, final IOException e) {//请求失败
        handler.post(new Runnable() {
            @Override
            public void run() {
                String message = e.getMessage();
                Toast.makeText(MainActivity.this, "请求失败" + message, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {//请求成功
        final String string = response.body().string();//将响应体转换为string格式
        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, "" + string, Toast.LENGTH_SHORT).show();
            }
        });
    }
});

3.okhttp做网络下载MP4

在写入SD卡的时候需要动态请求权限

requestPermissions(new String[]{
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
},101);
//1.获取client对象
OkHttpClient client = new OkHttpClient.Builder()
        .connectTimeout(60 * 1000, TimeUnit.SECONDS)//连接超时时间
        .readTimeout(60 * 1000, TimeUnit.SECONDS)//读取超时时间
        .writeTimeout(60 * 1000, TimeUnit.SECONDS)//写入超时时间
        .build();
//2.request请求
Request request = new Request.Builder()
        .url("http://uvideo.spriteapp.cn/video/2019/0512/56488d0a-7465-11e9-b91b-1866daeb0df1_wpd.mp4")//请求地址
        .get()//请求方式
        .build();
//3.client发起request请求  ->  call连接
Call call = client.newCall(request);
//4.加入队列 ->获取响应
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, final IOException e) {//请求失败
        handler.post(new Runnable() {
            @Override
            public void run() {
                String message = e.getMessage();
                Toast.makeText(MainActivity.this, "请求失败" + message, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {//请求成功
        InputStream is = response.body().byteStream();//将response转化为输入流
        FileOutputStream fos = new FileOutputStream("/sdcard/Download/houzi.mp4");
        byte[] bys = new byte[1024];
        int len = 0;
        int count = 0;//记录下载的总量
        long l = response.body().contentLength();//获取需要下载的总量
        while ((len = is.read(bys)) != -1) {
            fos.write(bys, 0, len);
            count += len;
            final int progress = (int) (count * 100 / l);//获取百分比
            handler.post(new Runnable() {
                @Override
                public void run() {
                    pb.setProgress(progress);
                }
            });
        }
        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, "下载成功", Toast.LENGTH_SHORT).show();
            }
        });
    }
});

4.okhttp做post请求参数为form格式

(1).post说明

这里边post方法需要一个请求体
RequestBody是一个抽象类

public abstract class RequestBody

所以我们需要用它的实现子类或调用 .create方法

public final class FormBody extends RequestBody

请求体有两种形式

  1. form
FormBody formBody = new FormBody.Builder()
        .add("username", "xiabei")
        .add("password", "123456")
        .add("repassword", "123456")
        .build();
  1. json
HashMap<String, String> map = new HashMap<>();
map.put("phoneNum", "17696945424");
map.put("userPassWord", "123456");
String s = new Gson().toJson(map);
RequestBody body = RequestBody.create(MediaType.parse("application/json"), s);//请求体
(2).form
1>注册
//1.获取client对象
OkHttpClient client = new OkHttpClient.Builder()
        .connectTimeout(60 * 1000, TimeUnit.SECONDS)//连接超时时间
        .readTimeout(60 * 1000, TimeUnit.SECONDS)//读取超时时间
        .writeTimeout(60 * 1000, TimeUnit.SECONDS)//写入超时时间
        .build();
//POST
//参数:username,password,repassword
FormBody formBody = new FormBody.Builder()
        .add("username", "xiabei")
        .add("password", "123456")
        .add("repassword", "123456")
        .build();
//2.request请求
Request request = new Request.Builder()
        .url("https://www.wanandroid.com/user/register")//请求地址
        .post(formBody)
        .build();
//3.client发起request请求  ->  call连接
Call call = client.newCall(request);
//4.加入队列 ->获取响应
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, final IOException e) {//请求失败
        handler.post(new Runnable() {
            @Override
            public void run() {
                String message = e.getMessage();
                Toast.makeText(MainActivity.this, "请求失败" + message, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {//请求成功
        final String string = response.body().string();//将响应体转换为string格式
        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, "" + string, Toast.LENGTH_SHORT).show();
            }
        });
    }
});
2>登陆
//1.获取client对象
OkHttpClient client = new OkHttpClient.Builder()
        .connectTimeout(60 * 1000, TimeUnit.SECONDS)//连接超时时间
        .readTimeout(60 * 1000, TimeUnit.SECONDS)//读取超时时间
        .writeTimeout(60 * 1000, TimeUnit.SECONDS)//写入超时时间
        .build();
//POST
//参数:username,password
FormBody formBody = new FormBody.Builder()//请求体
        .add("username", "xiabei")
        .add("password", "123456")
        .build();
//2.request请求
Request request = new Request.Builder()
        .url("https://www.wanandroid.com/user/login")//请求地址
        .post(formBody)
        .build();
//3.client发起request请求  ->  call连接
Call call = client.newCall(request);
//4.加入队列 ->获取响应
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, final IOException e) {//请求失败
        handler.post(new Runnable() {
            @Override
            public void run() {
                String message = e.getMessage();
                Toast.makeText(MainActivity.this, "请求失败" + message, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {//请求成功
        final String string = response.body().string();//将响应体转换为string格式
        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, "" + string, Toast.LENGTH_SHORT).show();
            }
        });
    }
});
(3).json
1>.注册
//1.获取client对象
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(60 * 1000, TimeUnit.SECONDS)//连接超时时间
                .readTimeout(60 * 1000, TimeUnit.SECONDS)//读取超时时间
                .writeTimeout(60 * 1000, TimeUnit.SECONDS)//写入超时时间
                .build();
        //POST
//        {
//  "phoneNum": 0,
//  "userPassWord": "string"
//}

        HashMap<String, String> map = new HashMap<>();
        map.put("phoneNum", "17696945424");
        map.put("userPassWord", "123456");
        String s = new Gson().toJson(map);
        RequestBody body = RequestBody.create(MediaType.parse("application/json"), s);//请求体
        Toast.makeText(this, "" + s, Toast.LENGTH_SHORT).show();
        //2.request请求
        Request request = new Request.Builder()
                .url("http://172.81.227.127:8055//videouser/register")//请求地址
                .post(body)
                .build();
        //3.client发起request请求  ->  call连接
        Call call = client.newCall(request);
        //4.加入队列 ->获取响应
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {//请求失败
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        String message = e.getMessage();
                        Toast.makeText(MainActivity.this, "请求失败" + message, Toast.LENGTH_SHORT).show();
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {//请求成功
                final String string = response.body().string();//将响应体转换为string格式
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this, "" + string, Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
2>.登陆
//1.获取client对象
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(60 * 1000, TimeUnit.SECONDS)//连接超时时间
                .readTimeout(60 * 1000, TimeUnit.SECONDS)//读取超时时间
                .writeTimeout(60 * 1000, TimeUnit.SECONDS)//写入超时时间
                .build();
        //POST
//        {
//  "phoneNum": 0,
//  "userPassWord": "string"
//}

        HashMap<String, String> map = new HashMap<>();
        map.put("phoneNum", "17696945424");
        map.put("userPassWord", "123456");
        String s = new Gson().toJson(map);
        RequestBody body = RequestBody.create(MediaType.parse("application/json"), s);//请求体
        Toast.makeText(this, "" + s, Toast.LENGTH_SHORT).show();
        //2.request请求
        Request request = new Request.Builder()
                .url("http://172.81.227.127:8055//videouser/login")//请求地址
                .post(body)
                .build();
        //3.client发起request请求  ->  call连接
        Call call = client.newCall(request);
        //4.加入队列 ->获取响应
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {//请求失败
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        String message = e.getMessage();
                        Toast.makeText(MainActivity.this, "请求失败" + message, Toast.LENGTH_SHORT).show();
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {//请求成功
                final String string = response.body().string();//将响应体转换为string格式
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this, "" + string, Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });

三.小案例

  1. 利用okHttp实现登陆
  2. 登陆后获取短视频用列表展示
  3. 点击item实现跳转播放短视频
  4. 可下载到本地

首先是登陆界面

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="20dp"
    tools:context=".LoginActivity">

    <EditText
        android:id="@+id/et_username"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入用户名" />

    <EditText
        android:id="@+id/et_password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入密码" />

  <LinearLayout
      android:layout_width="wrap_content"
      android:layout_height="wrap_content">
      <Button
          android:id="@+id/but_login"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="登陆" />
      <Button
          android:id="@+id/but_register"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="注册" />
  </LinearLayout>

</LinearLayout>

然后登陆的Activity代码

public class LoginActivity extends AppCompatActivity {
    private EditText etUsername;
    private EditText etPassword;
    private Button butLogin;

    private Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);

        setContentView(R.layout.activity_login);

        initViews();
    }

    private void initViews() {
        etUsername = (EditText) findViewById(R.id.et_username);
        etPassword = (EditText) findViewById(R.id.et_password);
        butLogin = (Button) findViewById(R.id.but_login);

        butLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //1.获取client对象
                OkHttpClient client = new OkHttpClient.Builder()
                        .connectTimeout(60 * 1000, TimeUnit.SECONDS)//连接超时时间
                        .readTimeout(60 * 1000, TimeUnit.SECONDS)//读取超时时间
                        .writeTimeout(60 * 1000, TimeUnit.SECONDS)//写入超时时间
                        .build();
                //POST
                //参数:username,password
                FormBody formBody = new FormBody.Builder()//请求体
                        .add("username", etUsername.getText().toString())
                        .add("password", etPassword.getText().toString())
                        .build();
                //2.request请求
                Request request = new Request.Builder()
                        .url("https://www.wanandroid.com/user/login")//请求地址
                        .post(formBody)
                        .build();
                //3.client发起request请求  ->  call连接
                Call call = client.newCall(request);
                //4.加入队列 ->获取响应
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, final IOException e) {//请求失败
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                String message = e.getMessage();
                                Toast.makeText(LoginActivity.this, "请求失败" + message, Toast.LENGTH_SHORT).show();
                            }
                        });
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {//请求成功
                        final String string = response.body().string();//将响应体转换为string格式
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                if (string.indexOf("0") != -1) {
                                    Toast.makeText(LoginActivity.this, "登陆成功", Toast.LENGTH_SHORT).show();
                                    startActivity(new Intent(LoginActivity.this, VideoActivity.class));
                                } else {
                                    Toast.makeText(LoginActivity.this, "登陆失败,用户名或密码不正确", Toast.LENGTH_SHORT).show();
                                }
                            }
                        });
                    }
                });
            }
        });
    }
}

然后第二个界面需要获取短视频
封装一个短视频实体类

public class VideoEntity {

    /**
     * vedioUrl : http://172.81.227.127:8088/fileDownload?fileName=VID_20191217_070408.mp4
     * vedioId : 1
     * userId : 949
     * coverImg : http://172.81.227.127:8088/fileDownload?fileName=VID_20191217_070408.mp4
     */

    private String vedioUrl;
    private int vedioId;
    private int userId;
    private String coverImg;

    @Override
    public String toString() {
        return "VideoEntity{" +
                "vedioUrl='" + vedioUrl + '\'' +
                ", vedioId=" + vedioId +
                ", userId=" + userId +
                ", coverImg='" + coverImg + '\'' +
                '}';
    }

    public String getVedioUrl() {
        return vedioUrl;
    }

    public void setVedioUrl(String vedioUrl) {
        this.vedioUrl = vedioUrl;
    }

    public int getVedioId() {
        return vedioId;
    }

    public void setVedioId(int vedioId) {
        this.vedioId = vedioId;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getCoverImg() {
        return coverImg;
    }

    public void setCoverImg(String coverImg) {
        this.coverImg = coverImg;
    }
}

适配器

public class MyAdapter extends BaseAdapter {
    private List<VideoEntity> datas;
    private Context context;

    private LayoutInflater layoutInflater;

    public MyAdapter(List<VideoEntity> datas, Context context) {
        this.datas = datas;
        this.context = context;
        this.layoutInflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        return datas.size();
    }

    @Override
    public Object getItem(int position) {
        return datas.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder = null;
        if (convertView == null) {
            convertView = layoutInflater.inflate(R.layout.item, null);
            viewHolder = new ViewHolder();
            viewHolder.imgShow = convertView.findViewById(R.id.img_show);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        Glide.with(context)
                .load(datas.get(position).getCoverImg())
                .into(viewHolder.imgShow);
        return convertView;
    }

    class ViewHolder {
        private ImageView imgShow;
    }
}

item中仅有一个imageView做展示

视频列表的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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".VideoActivity">
    <GridView
        android:numColumns="2"
        android:id="@+id/gv_show"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </GridView>
</LinearLayout>

视频列表的Activity代码

public class VideoActivity extends AppCompatActivity {
    private GridView gvShow;

    private Handler handler = new Handler();
    private List<VideoEntity> datas = new ArrayList<>();
    private MyAdapter myAdapter;

    private static final String TAG = "VideoActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_video);

        initViews();
        initDatas();
    }

    private void initDatas() {
        OkHttpClient client = new OkHttpClient.Builder().build();

        HashMap<String, String> map = new HashMap<>();
        map.put("page", "0");
        map.put("userId", "0");
        final String s = new Gson().toJson(map);
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), s);
        Request request = new Request.Builder()
                .url("http://172.81.227.127:8055/videovalues/selrecommendvideos")
                .post(requestBody)
                .build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(VideoActivity.this, "请求失败", Toast.LENGTH_SHORT).show();
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                final String string = response.body().string();
                final List<VideoEntity> list = new Gson().fromJson(string, new TypeToken<ArrayList<VideoEntity>>() {
                }.getType());
                datas.addAll(list);
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        myAdapter.notifyDataSetChanged();
                    }
                });
            }
        });
    }

    private void initViews() {
        gvShow = (GridView) findViewById(R.id.gv_show);
        myAdapter = new MyAdapter(datas, this);
        gvShow.setAdapter(myAdapter);
        gvShow.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent intent = new Intent(VideoActivity.this, PlayActivity.class);
                intent.putExtra("Url", datas.get(position).getVedioUrl());
                startActivity(intent);
            }
        });
    }
}

最后是播放界面的布局文件及Activity代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="match_parent"
    tools:context=".PlayActivity">

    <VideoView
        android:id="@+id/vv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <Button
        android:id="@+id/but_down"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:layout_marginRight="80dp"
        android:layout_marginBottom="80dp"
        android:text="下载" />

</RelativeLayout>
public class PlayActivity extends AppCompatActivity {
    private VideoView vv;
    private Button butDown;
    private String Url;
    private int index = 0;
    private Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);

        setContentView(R.layout.activity_play);
        requestPermissions(new String[]{
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
        }, 101);

        initDatas();
        initViews();
    }

    private void initDatas() {
        Url = getIntent().getStringExtra("Url");
    }

    private void initViews() {

        vv = (VideoView) findViewById(R.id.vv);
        butDown = (Button) findViewById(R.id.but_down);


        vv.setVideoPath(Url);
        vv.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                vv.start();
            }
        });

        butDown.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //1.获取client对象
                OkHttpClient client = new OkHttpClient.Builder()
                        .connectTimeout(60 * 1000, TimeUnit.SECONDS)//连接超时时间
                        .readTimeout(60 * 1000, TimeUnit.SECONDS)//读取超时时间
                        .writeTimeout(60 * 1000, TimeUnit.SECONDS)//写入超时时间
                        .build();
                //2.request请求
                Request request = new Request.Builder()
                        .url("http://uvideo.spriteapp.cn/video/2019/0512/56488d0a-7465-11e9-b91b-1866daeb0df1_wpd.mp4")//请求地址
                        .get()//请求方式
                        .build();
                //3.client发起request请求  ->  call连接
                Call call = client.newCall(request);
                //4.加入队列 ->获取响应
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, final IOException e) {//请求失败
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                String message = e.getMessage();
                                Toast.makeText(PlayActivity.this, "请求失败" + message, Toast.LENGTH_SHORT).show();
                            }
                        });
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {//请求成功
                        InputStream is = response.body().byteStream();//将response转化为输入流
                        FileOutputStream fos = new FileOutputStream("/sdcard/Download/video"+(index++)+".mp4");
                        byte[] bys = new byte[1024];
                        int len = 0;
                        while ((len = is.read(bys)) != -1) {
                            fos.write(bys, 0, len);
                        }
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(PlayActivity.this, "下载成功", Toast.LENGTH_SHORT).show();
                            }
                        });
                    }
                });
            }
        });


    }
}

记得加SD卡权限和动态获取权限!!

效果展示
在这里插入图片描述
要开心加油

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值