RxJava+Retrofit使用,四年Android面试遇到的问题整理

}

public abstract void onSucceed(T result);

public abstract void onFail(int code, String msg);

//应用中具体实现的是下面这个onFail方法

private void onFail(HttpThrowable httpThrowable) {

onFail(httpThrowable.errorType, httpThrowable.message);

}

}

import cn.yumakeji.lib_common.global.AppGlobals;

import io.reactivex.rxjava3.functions.Consumer;

/**

  • 网络返回,错误解析

*/

public class ThrowableCallback implements Consumer {

@Override

public void accept(Throwable throwable) throws Throwable {

if (throwable instanceof Exception) {

onError(ThrowableHandler.handleThrowable(throwable));

} else {

onError(new HttpThrowable(HttpThrowable.UNKNOWN, “未知错误”, throwable));

}

}

//应用中具体实现的是下面这个onError方法

public void onError(HttpThrowable httpThrowable) {

Toast.makeText(AppGlobals.getApplication().getApplicationContext(),

httpThrowable.message, Toast.LENGTH_LONG).show();

}

}

  • 请求后台访问数据的接口类

/**

  • 请求后台访问数据的接口类

*/

public interface ServiceApi {

/**

  • Get方法 @Query

*/

@GET(“home/paintingList”)

Observable<Result> getSearchContent(@Query(“searchContent”) String text, @Query(“size”) int size);

/**

  • Get方法 @Query

*/

@GET(“home/paintingList”)

Observable<Result> getSearchContent2(@Query(“searchContent”) String text, @Query(“size”) int size);

@POST(“applets/code2Session”)

Observable<Result> postCode2Session(@Body Map<String, Object> code);

}

  • BaseActivity中绑定生命周期

public abstract class BaseActivity extends AppCompatActivity {

/**

  • 绑定生命周期

*/

protected LifecycleProvider<Lifecycle.Event> rxLifecycle = AndroidLifecycle.createLifecycleProvider(this);

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

}

@Override

protected void onDestroy() {

super.onDestroy();

}

}

  • 调用

package cc.network.rxjava;

import android.content.Intent;

import android.nfc.Tag;

import android.os.Bundle;

import android.util.ArrayMap;

import android.util.Log;

import android.view.View;

import android.widget.AdapterView;

import android.widget.ArrayAdapter;

import android.widget.ListView;

import android.widget.Toast;

import androidx.lifecycle.Lifecycle;

import com.alibaba.fastjson.JSON;

import com.yumakeji.rxjava.network.RetrofitClient;

import com.yumakeji.rxjava.network.bean.Result;

import com.yumakeji.rxjava.network.callback.ConsumerCallback;

import com.yumakeji.rxjava.network.callback.ObserverCallback;

import com.yumakeji.rxjava.network.callback.ThrowableCallback;

import com.yumakeji.rxjava.network.error.HttpThrowable;

import com.yumakeji.rxjava.network.handler.RetryWithDelay;

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.CopyOnWriteArrayList;

import java.util.concurrent.TimeUnit;

import cc.network.rxjava.rxjava.ServiceApi;

import cc.network.rxjava.textbean.LoginBean;

import cc.network.rxjava.textbean.Top250Bean;

import cc.network.rxjava.viewmodel.RxjavaViewModel;

import cn.yumakeji.jetpackroomstudy.R;

import cn.yumakeji.lib_common.global.AppGlobals;

import cn.yumakeji.lib_common.utils.AppUtils;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;

import io.reactivex.rxjava3.annotations.NonNull;

import io.reactivex.rxjava3.core.Observable;

import io.reactivex.rxjava3.core.ObservableEmitter;

import io.reactivex.rxjava3.core.ObservableOnSubscribe;

import io.reactivex.rxjava3.core.ObservableSource;

import io.reactivex.rxjava3.core.Observer;

import io.reactivex.rxjava3.disposables.Disposable;

import io.reactivex.rxjava3.functions.Action;

import io.reactivex.rxjava3.functions.BiFunction;

import io.reactivex.rxjava3.functions.Consumer;

import io.reactivex.rxjava3.functions.Function;

import io.reactivex.rxjava3.schedulers.Schedulers;

/**

  • 防止多次点击

  • https://www.jianshu.com/p/ea45670a364f,

  • Retrofit

  • https://blog.csdn.net/huangxiaoguo1/article/details/65627293

  • Rxjava

  • https://www.jianshu.com/p/092452f287db

  • 不继承RxAppCompatActivity的情况下使用RxLifeCycle

  • https://blog.csdn.net/kevinscsdn/article/details/78980010

*/

public class RxjavaActivity extends BaseActivity {

private ListView mListView;

private List strings = new CopyOnWriteArrayList<>();

private final String TAG = “Rxjava”;

private RxjavaViewModel viewModel;

@Override

public void onDetachedFromWindow() {

super.onDetachedFromWindow();

getLifecycle().removeObserver(viewModel);

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_rxjava);

mListView = findViewById(R.id.list_item);

strings.add("Get方法 @Query ");

strings.add("Post方法 @Body ");

strings.add(“使用Rxjava切换线程”);

strings.add(“Rxjava(一)emitter”);

strings.add(“Rxjava(二)变换操作符Map”);

strings.add(“Rxjava(三)FlatMap”);

strings.add(“Rxjava(四)concatMap”);

strings.add(“Rxjava(五)接口顺序调用”);

strings.add(“Rxjava(六)接口顺序调用(内部发生错误)”);

strings.add(“Rxjava(七)Zip来打包”);

}

/**

  • 去体验Retrofit

  • @param view

*/

public void onRetrofitClick(View view) {

startActivity(new Intent(this, RetrofitActivity.class));

}

@Override

public void onAttachedToWindow() {

super.onAttachedToWindow();

viewModel = new RxjavaViewModel(this);

getLifecycle().addObserver(viewModel);

mListView.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, strings));

mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

@Override

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

switch (position) {

case 0://Get方法 @Query

RetrofitClient.getInstance()

.create(ServiceApi.class)// 创建服务

.getSearchContent(“”, 5)//调用接口

.subscribeOn(Schedulers.io())// 指定被观察者的操作在io线程中完成

.doOnSubscribe(disposable -> viewModel.showProgressDialog(“正在请求中”))

.observeOn(AndroidSchedulers.mainThread())//指定观察者接收到数据,然后在Main线程中完成

.retryWhen(new RetryWithDelay(1, 1))//遇到错误时重试,第一个参数为重试几次,第二个参数为重试的间隔

.doAfterTerminate(() -> viewModel.hintProgressDialog())

.compose(rxLifecycle.bindUntilEvent(Lifecycle.Event.ON_DESTROY))//生命周期

.subscribe(new ConsumerCallback() {

@Override

public void onSucceed(Top250Bean result) {

// 成功获取数据

Toast.makeText(AppGlobals.getApplication().getApplicationContext(),

“成功了” + result, Toast.LENGTH_LONG).show();

}

@Override

public void onError(int code, String msg) {

// 获取数据失败

Toast.makeText(AppGlobals.getApplication().getApplicationContext(),

msg, Toast.LENGTH_LONG).show();

}

}, new ThrowableCallback() {

@Override

public void onError(HttpThrowable httpThrowable) {

// 获取数据失败

Log.i(TAG, httpThrowable.message);

}

});

break;

case 1://POST方法 @Body

ArrayMap<String, Object> map = new ArrayMap<>();

map.put(“jsCode”, “033sHaaK03pRwa2Bic9K0EmcaK0sHaaq”);

RetrofitClient.getInstance()

.create(ServiceApi.class)

.postCode2Session(map)

.subscribeOn(Schedulers.io())

.doOnSubscribe(disposable -> viewModel.showProgressDialog(“正在请求中”))

.observeOn(AndroidSchedulers.mainThread())

.compose(rxLifecycle.bindUntilEvent(Lifecycle.Event.ON_DESTROY))

.retryWhen(new RetryWithDelay(1, 1))//遇到错误时重试,第一个参数为重试几次,第二个参数为重试的间隔

.doAfterTerminate(() -> viewModel.hintProgressDialog())

.subscribe(new ObserverCallback() {

@Override

public void onSucceed(LoginBean result) {

// 成功获取数据

Toast.makeText(AppGlobals.getApplication().getApplicationContext(),

“成功了” + result, Toast.LENGTH_LONG).show();

}

@Override

public void onFail(int code, String msg) {

// 获取数据失败

Toast.makeText(AppGlobals.getApplication().getApplicationContext(),

msg, Toast.LENGTH_LONG).show();

}

@Override

public void onComplete() {

super.onComplete();

}

});

break;

case 2://使用Rxjava切换线程

Observable.empty().observeOn(Schedulers.io())

.doOnComplete(new Action() {

@Override

public void run() throws Throwable {

Log.i(TAG, “我是子线程:” + AppUtils.isMainThread());

Observable.timer(3, TimeUnit.SECONDS)

.observeOn(AndroidSchedulers.mainThread())

.doOnComplete(() -> msgManagement(10))

.subscribe();

Observable.empty().observeOn(AndroidSchedulers.mainThread())

.doOnComplete(() -> msgManagement(20)

).subscribe();

}

}).subscribe();

break;

case 3:

//Rxjava(一)emitter 发射器

/**

  • 借鉴:给初学者的RxJava2.0教程(一)

  • https://www.jianshu.com/p/464fa025229e

  • 规则:

  • 1、上游可以发送无限个onNext, 下游也可以接收无限个onNext.

  • 2、当上游发送了一个onComplete后, 上游onComplete之后的事件将会继续发送, 而下游收到onComplete事件之后将不再继续接收事件.

  • 3、当上游发送了一个onError后, 上游onError之后的事件将继续发送, 而下游收到onError事件之后将不再继续接收事件.

  • 4、上游可以不发送onComplete或onError.

  • 5、最为关键的是onComplete和onError必须唯一并且互斥, 即不能发多个onComplete, 也不能发多个onError,

  • 也不能先发一个onComplete, 然后再发一个onError, 反之亦然

  • 注: 关于onComplete和onError唯一并且互斥这一点, 是需要自行在代码中进行控制, 如果你的代码逻辑中违背了这个规则,

  • **并不一定会导致程序崩溃. ** 比如发送多个onComplete是可以正常运行的, 依然是收到第一个onComplete就不再接收了,

  • 但若是发送多个onError, 则收到第二个onError事件会导致程序会崩溃.

*/

Observable.create(new ObservableOnSubscribe() {

@Override

public void subscribe(@NonNull ObservableEmitter emitter) throws Throwable {

Log.e(TAG, “是否是主线程=>” + AppUtils.isMainThread());

emitter.onNext(“huangxiaoguo1”);

emitter.onNext(“huangxiaoguo2”);

emitter.onNext(“huangxiaoguo3”);

emitter.onComplete();

}

})

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Observer() {

private Disposable mDisposable;

/**

  • 不带任何参数的subscribe() 表示下游不关心任何事件,你上游尽管发你的数据去吧, 老子可不管你发什么.

  • 带有一个Consumer参数的方法表示下游只关心onNext事件, 其他的事件我假装没看见,

  • @param d

*/

@Override

public void onSubscribe(@NonNull Disposable d) {

Log.d(TAG, “subscribe”);

mDisposable = d;

}

@Override

public void onNext(@NonNull String s) {

/**

  • 当调用它的dispose()方法时, 它就会将两根管道切断, 从而导致下游收不到事件.

  • 调用dispose()并不会导致上游不再继续发送事件, 上游会继续发送剩余的事件

*/

Log.e(TAG, “onNext 是否是主线程 ==>” + AppUtils.isMainThread());

Log.d(TAG, “onNext==>” + s);

if (s.equals(“huangxiaoguo2”)) {

mDisposable.dispose();

}

}

@Override

public void onError(@NonNull Throwable e) {

Log.d(TAG, “onError”);

}

@Override

public void onComplete() {

Log.d(TAG, "onComplete ");

}

});

break;

case 4:

//Rxjava(二)变换操作符Map

Observable.create(new ObservableOnSubscribe() {

@Override

public void subscribe(@NonNull ObservableEmitter emitter) throws Throwable {

Log.e(TAG, “subscribe 是否是主线程 ==>” + AppUtils.isMainThread());

emitter.onNext(1);

emitter.onNext(2);

emitter.onNext(3);

}

})

.subscribeOn(Schedulers.io())

.map(new Function<Integer, String>() {

@Override

public String apply(Integer integer) throws Throwable {

Log.e(TAG, “map 是否是主线程 ==>” + AppUtils.isMainThread());

return “我是在map中处理过了的===>” + String.valueOf(integer);

}

})

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Consumer() {

@Override

public void accept(String s) throws Throwable {

Log.e(TAG, “accept 是否是主线程 ==>” + AppUtils.isMainThread());

Log.i(TAG, “accept===>” + s);

}

});

break;

case 5:

//flatMap 并不保证事件的顺序

Observable.create(new ObservableOnSubscribe() {

@Override

public void subscribe(@NonNull ObservableEmitter emitter) throws Throwable {

emitter.onNext(1);

emitter.onNext(2);

emitter.onNext(3);

}

}).flatMap(new Function<Integer, ObservableSource>() {

@Override

public ObservableSource apply(Integer integer) throws Throwable {

ArrayList strings = new ArrayList<>();

for (int i = 0; i < 3; i++) {

strings.add("I am value " + integer);

}

return Observable.fromIterable(strings).delay(2, TimeUnit.SECONDS);

}

}).subscribe(new Consumer() {

@Override

public void accept(String s) throws Throwable {

Log.d(TAG, s);

}

});

break;

case 6:

//concatMap 保证顺序

Observable.create(new ObservableOnSubscribe() {

@Override

public void subscribe(@NonNull ObservableEmitter emitter) throws Throwable {

emitter.onNext(1);

emitter.onNext(2);

emitter.onNext(3);

}

}).concatMap(new Function<Integer, ObservableSource>() {

@Override

public ObservableSource apply(Integer integer) throws Throwable {

ArrayList strings = new ArrayList<>();

for (int i = 0; i < 3; i++) {

strings.add("I am value " + integer);

}

return Observable.fromIterable(strings).delay(2, TimeUnit.SECONDS);

}

}).subscribe(new Consumer() {

@Override

public void accept(String s) throws Throwable {

Log.d(TAG, s);

}

});

break;

case 7:

//Rxjava(五)接口顺序调用

RetrofitClient.getInstance()

.create(ServiceApi.class)// 创建服务

.getSearchContent(“”, 5)//调用接口

.subscribeOn(Schedulers.io())// 指定被观察者的操作在io线程中完成

.doOnSubscribe(disposable -> viewModel.showProgressDialog(“正在请求中”))

.observeOn(AndroidSchedulers.mainThread())//指定观察者接收到数据,然后在Main线程中完成

.doOnNext(new ConsumerCallback() {

@Override

public void onSucceed(Top250Bean result) {

Log.e(TAG, result.toString());

}

@Override

public void onError(int code, String msg) {

}

})

.observeOn(Schedulers.io())

.flatMap(new Function<Result, Observable<Result>>() {

@Override

public Observable<Result> apply(Result result) throws Throwable {

Log.e(TAG, result.getMessage());

ArrayMap<String, Object> map = new ArrayMap<>();

map.put(“jsCode”, “033sHaaK03pRwa2Bic9K0EmcaK0sHaaq”);

return RetrofitClient.getInstance().create(ServiceApi.class).postCode2Session(map);

}

})

.retryWhen(new RetryWithDelay(1, 1))//遇到错误时重试,第一个参数为重试几次,第二个参数为重试的间隔

.compose(rxLifecycle.bindUntilEvent(Lifecycle.Event.ON_DESTROY))//生命周期

.observeOn(AndroidSchedulers.mainThread())

.doAfterTerminate(() -> viewModel.hintProgressDialog())

.subscribe(new ConsumerCallback() {

@Override

public void onSucceed(LoginBean result) {

Log.e(TAG, result.toString());

}

@Override

public void onError(int code, String msg) {

Log.e(TAG, msg);

}

}, new ThrowableCallback() {

@Override

public void onError(HttpThrowable httpThrowable) {

super.onError(httpThrowable);

}

});

break;

case 8:

//Rxjava(六)接口顺序调用(内部发生错误)

ArrayMap<String, Object> map1 = new ArrayMap<>();

map1.put(“jsCode”, “033sHaaK03pRwa2Bic9K0EmcaK0sHaaq”);

RetrofitClient.getInstance()

.create(ServiceApi.class)// 创建服务

.postCode2Session(map1)//调用接口

.subscribeOn(Schedulers.io())// 指定被观察者的操作在io线程中完成

.doOnSubscribe(disposable -> viewModel.showProgressDialog(“正在请求中”))

.observeOn(AndroidSchedulers.mainThread())//指定观察者接收到数据,然后在Main线程中完成

.doOnNext(new ConsumerCallback() {

@Override

public void onSucceed(LoginBean result) {

Log.e(TAG, result.toString());

}

@Override

public void onError(int code, String msg) {

Log.e(TAG, msg);

}

})

.observeOn(Schedulers.io())

.flatMap(new Function<Result, Observable<Result>>() {

@Override

public Observable<Result> apply(Result result) throws Throwable {

if (!result.isOk()) {

return null;

}

return RetrofitClient.getInstance().create(ServiceApi.class).getSearchContent(“”, 5);

}

})

.retryWhen(new RetryWithDelay(1, 1))//遇到错误时重试,第一个参数为重试的间隔,第二个参数为重试几次

.compose(rxLifecycle.bindUntilEvent(Lifecycle.Event.ON_DESTROY))//生命周期

.observeOn(AndroidSchedulers.mainThread())

.doAfterTerminate(() -> viewModel.hintProgressDialog())

.subscribe(new ConsumerCallback() {

@Override

public void onSucceed(Top250Bean result) {

Log.e(TAG, result.toString());

}

@Override

public void onError(int code, String msg) {

Log.e(TAG, msg);

}

}, new ThrowableCallback() {

@Override

public void onError(HttpThrowable httpThrowable) {

super.onError(httpThrowable);

}

});

break;

case 9:

//Rxjava(七)Zip来打包请求

Observable.zip(RetrofitClient.getInstance()

.create(ServiceApi.class)

.getSearchContent(“”, 1)

.subscribeOn(Schedulers.io()),

RetrofitClient.getInstance()

.create(ServiceApi.class)

.getSearchContent2(“”, 1)

.subscribeOn(Schedulers.io()),

new BiFunction<Result, Result, Top250Bean>() {

@Override

public Top250Bean apply(Result result, Result result2) throws Throwable {

Top250Bean data1 = JSON.parseObject(JSON.toJSONString(result.data), Top250Bean.class);

Top250Bean data2 = JSON.parseObject(JSON.toJSONString(result.data), Top250Bean.class);

data1.getRecords().addAll(data2.getRecords());

return data1;

}

}).observeOn(AndroidSchedulers.mainThread())

.subscribe(new Consumer() {

@Override

public void accept(Top250Bean result) throws Throwable {

Log.e(TAG, result.toString());

Log.e(TAG, result.getRecords().size()+“—”);

}

});

break;

}

}

});

}

/**

  • 代替Handler

  • @param what

*/

private void msgManagement(int what) {

if (what == 10) {

Log.i(TAG, “我是代替Handler 有延时” + AppUtils.isMainThread());

} else {

Log.i(TAG, “我是代替Handler 没有延时” + AppUtils.isMainThread());

}

}

}

  • MVVM中管理Rxjava的生命周期

可直接使用开源框架:rxjava-RxLife

//使用rxlife对viewModel生命周期执行支持(rxlifecycle在google jetpack的videModel中不太好用,针对的是MVP)

api ‘com.ljx.rxlife3:rxlife-rxjava:3.0.0’

注意: 一定要在Activity/Fragment通过以下方式获取ViewModel对象,否则RxLife接收不到生命周期的回调

viewModel = new ViewModelProvider(this).get(RxjavaViewModel.class);

Observable.interval(2, TimeUnit.SECONDS)

.observeOn(AndroidSchedulers.mainThread())

.to(RxLife.to(this))

.subscribe(new Consumer() {

@Override

public void accept(Long aLong) throws Throwable {

Log.e(“huangxiaoguo”, “接口轮询”);

}

});


其他设计到的类
  • error

package com.yumakeji.rxjava.network.error;

public class HttpThrowable extends Exception {

public int errorType;

public String message;

public Throwable throwable;

/**

  • 未知错误

*/

public static final int UNKNOWN = 1000;

/**

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

学习分享,共勉

Android高级架构师进阶之路

题外话,我在阿里工作多年,深知技术改革和创新的方向,Android开发以其美观、快速、高效、开放等优势迅速俘获人心,但很多Android兴趣爱好者所需的进阶学习资料确实不太系统,完整。今天我把我搜集和整理的这份学习资料分享给有需要的人,若有关Android学习进阶可以与我在Android终极开发交流群一起讨论交流。 点击这里前往我的Git领取资料 的同时,还可以加入一个好的学习交流圈,何乐而不为呢?加入我们和我们一起吧!!

  • Android进阶知识体系学习脑图

  • Android进阶高级工程师学习全套手册

  • 对标Android阿里P7,年薪50w+学习视频

  • 大厂内部Android高频面试题,以及面试经历

public class HttpThrowable extends Exception {

public int errorType;

public String message;

public Throwable throwable;

/**

  • 未知错误

*/

public static final int UNKNOWN = 1000;

/**

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-tfR2fwvN-1710672471753)]
[外链图片转存中…(img-P8MLuNBf-1710672471753)]
[外链图片转存中…(img-ARuXqrQZ-1710672471754)]
[外链图片转存中…(img-pYwt3sfT-1710672471754)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-4bqeO5hb-1710672471755)]

学习分享,共勉

Android高级架构师进阶之路

题外话,我在阿里工作多年,深知技术改革和创新的方向,Android开发以其美观、快速、高效、开放等优势迅速俘获人心,但很多Android兴趣爱好者所需的进阶学习资料确实不太系统,完整。今天我把我搜集和整理的这份学习资料分享给有需要的人,若有关Android学习进阶可以与我在Android终极开发交流群一起讨论交流。 点击这里前往我的Git领取资料 的同时,还可以加入一个好的学习交流圈,何乐而不为呢?加入我们和我们一起吧!!

  • Android进阶知识体系学习脑图

[外链图片转存中…(img-rya87jfw-1710672471755)]

  • Android进阶高级工程师学习全套手册

[外链图片转存中…(img-2WoB4QKu-1710672471756)]

  • 对标Android阿里P7,年薪50w+学习视频

[外链图片转存中…(img-LZljm2LR-1710672471756)]

  • 大厂内部Android高频面试题,以及面试经历

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值