当前位置: 首页 > article >正文

Android版Kotlin版RxJava2+Retrofit2+OkHttp3的基础、封装和项目中的使用

@POST(“top250”)

Call getTop250 (@Field(“start”) int start , @Field(“count”) int count);

}

使用 POST 方式时需要注意两点:

  • 必须加上 @FormUrlEncoded标签,否则会抛异常。

  • 必须要有参数,否则会抛异常, 源码抛异常的地方如下:

if (isFormEncoded && !gotField) {

throw methodError(“Form-encoded method must contain at least one @Field.”);

}

4.用 Retrofit 创建 接口实例 MoiveService 并且调用接口中的方法进行网络请求:

异步方式请求:

//获取接口实例

MovieService movieService = retrofit.create(MovieService.class);

//调用方法得到一个Call

Call call = movieService.getTop250(0,20);

//进行网络请求

call.enqueue(new Callback() {

@Override

public void onResponse(Call call, Response response) {

mMovieAdapter.setMovies(response.body().subjects);

mMovieAdapter.notifyDataSetChanged();

}

@Override

public void onFailure(Call call, Throwable t) {

t.printStackTrace();

}

});

同步方式请求: 返回一个Response

Response response = call.execute();

二,配合RxJava 使用:
  1. 更改定义的接口,返回值不再是一个 Call ,而是返回的一个 Observble:

public interface MovieService {

//获取豆瓣Top250 榜单

@GET(“top250”)

Observable getTop250(@Query(“start”) int start, @Query(“count”)int count);

}

2.创建 Retrofit 的时候添加如下代码:

addCallAdapterFactory(RxJava2CallAdapterFactory.create())

3.添加转换器Converter(将 json 转为 JavaBean):

addConverterFactory(GsonConverterFactory.create())

举实际项目中使用的例子:

retrofit = new Retrofit.Builder()

.client(okHttpBuilder.build())

.addConverterFactory(GsonConverterFactory.create())

.addCallAdapterFactory(RxJava2CallAdapterFactory.create())

.baseUrl(BASE_URL)

.build();

4.Activity 或者 Fragment 中传入 DisposableObserver 建立订阅关系:

Subscription subscription = movieService.getTop250(0,20)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new DisposableObserver() {

@Override

public void onComplete() {

}

@Override

public void onError(Throwable e) {

}

@Override

public void onNext(MovieSubject movieSubject) {

mMovieAdapter.setMovies(movieSubject.subjects);

mMovieAdapter.notifyDataSetChanged();

}

});

5.加入RxJava的好处:

  • 加入 RxJava 后的网络请求,返回不再是一个 Call ,而是一个 Observable。

  • 在Activity / Fragment 中传入一个Subscriber 建立订阅关系,就可以在 onNext 中处理结果了。

  • RxJava 的好处是帮我处理 线程之间的切换,我们可以在指定 订阅的在哪个线程,观察在哪个线程。

  • 可以 通过操作符 进行数据变换。

  • 整个过程都是链式的,简化逻辑。其中FlatMap 操作符 还可以解除多层嵌套的问题。

RxJava 很强大,能帮我处理很多复杂的场景,如果熟练使用的话,那么能提升我们的开发效率。

三,加入 OkHttp 配置:

通过OkHttpClient 可以配置很多东西,比如 链接超时时间,缓存,拦截器 等等。代码如下:

OkHttpClient.Builder builder = new OkHttpClient.Builder();

builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//连接 超时时间

builder.writeTimeout(DEFAULT_TIME_OUT,TimeUnit.SECONDS);//写操作 超时时间

builder.readTimeout(DEFAULT_TIME_OUT,TimeUnit.SECONDS);//读操作 超时时间

builder.retryOnConnectionFailure(true);//错误重连

// 添加公共参数拦截器

BasicParamsInterceptor basicParamsInterceptor = new BasicParamsInterceptor.Builder()

.addHeaderParam(“userName”,“”)//添加公共参数

.addHeaderParam(“device”,“”)

.build();

builder.addInterceptor(basicParamsInterceptor);

// 创建Retrofit

mRetrofit = new Retrofit.Builder()

.client(builder.build())

.addCallAdapterFactory(RxJava2CallAdapterFactory.create())

.addConverterFactory(GsonConverterFactory.create())

.baseUrl(ApiConfig.BASE_URL)

.build();

列举项目中用到的如下:

//项目中设置头信息

Interceptor headerInterceptor = new Interceptor() {

@Override

public Response intercept(Chain chain) throws IOException {

Request originalRequest = chain.request();

Request.Builder requestBuilder = originalRequest.newBuilder()

.addHeader(“Accept-Encoding”, “gzip”)

.addHeader(“Accept”, “application/json”)

.addHeader(“Content-Type”, “application/json; charset=utf-8”)

.method(originalRequest.method(), originalRequest.body());

requestBuilder.addHeader(“Authorization”, "Bearer " + BaseConstant.TOKEN);//添加请求头信息,服务器进行token有效性验证

Request request = requestBuilder.build();

return chain.proceed(request);

}

};

okHttpBuilder.addInterceptor(headerInterceptor);

//项目中创建Retrofit

retrofit = new Retrofit.Builder()

.client(okHttpBuilder.build())

.addConverterFactory(GsonConverterFactory.create())

.addCallAdapterFactory(RxJava2CallAdapterFactory.create())

.baseUrl(BASE_URL)

.build();

httpService = retrofit.create(HttpService.class);

封装篇
一,创建一个 统一生成接口实例的管理类 RetrofitServiceManager

创建了一个 RetrofitServiceManager 类,该类采用 单例模式,在 私有的 构造方法中,生成了 Retrofit 实例,并配置了OkHttpClient 和一些 公共配置。

提供了一个create()方法,生成 接口实例,接收 Class泛型。

代码如下:

public class RetrofitServiceManager {

private static final int DEFAULT_TIME_OUT = 5;//超时时间 5s

private static final int DEFAULT_READ_TIME_OUT = 10;

private Retrofit mRetrofit;

private RetrofitServiceManager(){

// 创建 OKHttpClient

OkHttpClient.Builder builder = new OkHttpClient.Builder();

builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//连接超时时间

builder.writeTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS);//写操作 超时时间

builder.readTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS);//读操作超时时间

// 添加公共参数拦截器

HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder()

.addHeaderParams(“paltform”,“android”)

.addHeaderParams(“userToken”,“1234343434dfdfd3434”)

.addHeaderParams(“userId”,“123445”)

.build();

builder.addInterceptor(commonInterceptor);

// 创建Retrofit

mRetrofit = new Retrofit.Builder()

.client(builder.build())

.addCallAdapterFactory(RxJava2CallAdapterFactory.create())

.addConverterFactory(GsonConverterFactory.create())

.baseUrl(ApiConfig.BASE_URL)

.build();

}

private static class SingletonHolder{

private static final RetrofitServiceManager INSTANCE = new RetrofitServiceManager();

}

/**

  • 获取RetrofitServiceManager

  • @return

*/

public static RetrofitServiceManager getInstance(){

return SingletonHolder.INSTANCE;

}

/**

  • 获取对应的Service

  • @param service Service 的 class

  • @param

  • @return

*/

public T create(Class service){

return mRetrofit.create(service);

}

}

接口实例Service都可以用这个来生成,代码如下:

mMovieService = RetrofitServiceManager.getInstance().create(MovieService.class);

二,创建接口,通过第一步获取实例

有了可以获取接口实例的方法,然后创建一个接口,代码如下:

public interface MovieService{

//获取豆瓣Top250 榜单

@GET(“top250”)

Observable getTop250(@Query(“start”) int start, @Query(“count”) int count);

@FormUrlEncoded

@POST(“/x3/weather”)

Call getWeather(@Field(“cityId”) String cityId, @Field(“key”) String key);

}

三,创建一个业务Loader ,如XXXLoder,获取Observable并处理相关业务

创建 Loader 的原因:每一个Api 都写一个接口很麻烦,因此就把 请求逻辑 封装在 一个业务Loader 里面,一个 Loader 里面可以处理多个Api 接口。代码如下:

public class MovieLoader extends ObjectLoader {

private MovieService mMovieService;

public MovieLoader(){

mMovieService = RetrofitServiceManager.getInstance().create(MovieService.class);

}

/**

  • 获取电影列表

  • @param start

  • @param count

  • @return

*/

public Observable<List> getMovie(int start, int count){

return observe(mMovieService.getTop250(start , count)).map(new Func1<MovieSubject, List>() {

@Override

public List call(MovieSubject movieSubject) {

return movieSubject.subjects;

}

});

}

public Observable getWeatherList(String cityId,String key){

return observe(mMovieService.getWeather(cityId , key)).map(new Func1<String , String>() {

@Override

public String call(String s) {

//可以处理对应的逻辑后在返回

return s;

}

});

}

public interface MovieService{

//获取豆瓣Top250 榜单

@GET(“top250”)

Observable getTop250(@Query(“start”) int start, @Query(“count”)int count);

@FormUrlEncoded

@POST(“/x3/weather”)

Call getWeather(@Field(“cityId”) String cityId, @Field(“key”) String key);

}

}

创建一个MovieLoader,构造方法中生成了mHttpService,而 Service 中可以定义和业务相关的多个api,比如:例子中的HttpService中,

可以定义和电影相关的多个api,获取电影列表、获取电影详情、搜索电影等api,就不用定义多个接口了。

MovieLoader 是从 ObjectLoader 中继承下来的,ObjectLoader 提取了一些公共的操作。代码如下:

/**

  • 将一些重复的操作提出来,放到父类以免Loader 里每个接口都有重复代码

*/

public class ObjectLoader {

/**

  • @param observable

  • @param

  • @return

*/

protected Observable observe(Observable observable){

return observable

.subscribeOn(Schedulers.io())

.unsubscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread());

}

}

四,Activity/Fragment 中的调用

创建Loader实例:

mMovieLoader = new MovieLoader();

通过Loader 调用方法获取结果,代码如下:

/**

  • 获取电影列表

*/

private void getMovieList(){

mMovieLoader.getMovie(0,10).subscribe(new Action1<List>() {

@Override

public void call(List movies) {

mMovieAdapter.setMovies(movies);

mMovieAdapter.notifyDataSetChanged();

}

}, new Action1() {

@Override

public void call(Throwable throwable) {

Log.e(“TAG”,“error message:”+throwable.getMessage());

}

});

}

五,统一处理结果和错误

1.统一处理请求结果:

现实项目中,所有接口的返回结果都是同一格式,如:

{

“status”: 200,

“message”: “成功”,

“data”: {}

}

在请求api 接口的时候,只关心想要的数据,也就上面的 data{ },其他的东西不太关心,请求失败 的时候可以根据 status 判断进行 错误处理。

包装返回结果:首先需要根据服务端定义的 JSON 结构创建一个 BaseResponse 类,代码如下:

/**

  • 网络请求结果 基类

*/

public class BaseResponse {

public int status;

public String message;

public T data;

public boolean isSuccess(){

return status == 200;

}

}

有了统一的格式数据后,我们需要 剥离出data{ }返回给 上层调用者,创建一个 PayLoad 类,代码如下:

/**

  • 剥离 最终数据

*/

public class PayLoad implements Func1<BaseResponse{

@Override

public T call(BaseResponse tBaseResponse) {//获取数据失败时,包装一个Fault 抛给上层处理错误

if(!tBaseResponse.isSuccess()){

throw new Fault(tBaseResponse.status,tBaseResponse.message);

}

return tBaseResponse.data;

}

}

PayLoad 继承自 Func1,接收一个BaseResponse , 就是接口返回的 JSON 数据结构,返回的是 T,就是data{ },判断是否请求成功,请求成功 返回Data,请求失败 包装成一个 Fault 返回给上层统一处理错误。

在Loader类里面获取结果后,通过map 操作符剥离数据。代码如下:

public Observable<List> getMovie(int start, int count){

return observe(mMovieService.getTop250(start,count))

.map(new PayLoad<BaseResponse<List>());

}

2.统一处理错误:

在PayLoad 类里面,请求失败时,抛出了一个Fault 异常给上层,我在Activity/Fragment 中拿到这个异常,然后判断错误码,进行异常处理。在onError () 中添加。

对应 错误码 处理 相应的错误,代码如下:

public void call(Throwable throwable) {

Log.e(“TAG”,“error message:”+throwable.getMessage());

if(throwable instanceof Fault){

Fault fault = (Fault) throwable;

if(fault.getErrorCode() == 404){

//错误处理

}else if(fault.getErrorCode() == 500){

//错误处理

}else if(fault.getErrorCode() == 501){

//错误处理

}

}

}

六,添加公共参数

实际项目中,每个接口都有一些基本的相同的参数,我们称之为公共参数,比如:userId、userToken、userName、deviceId等等,我们不必每个接口都去写,可以写一个拦截器,在拦截器里面拦截请求,为每个请求都添加相同的公共参数。

拦截器代码如下:

/*

  • 拦截器

  • 向请求头里添加公共参数

*/

public class HttpCommonInterceptor implements Interceptor {

private Map<String,String> mHeaderParamsMap = new HashMap<>();

public HttpCommonInterceptor() {

}

@Override

public Response intercept(Chain chain) throws IOException {

Log.d(“HttpCommonInterceptor”,“add common params”);

Request oldRequest = chain.request();

// 添加新的参数,添加到url 中

/*HttpUrl.Builder authorizedUrlBuilder = oldRequest.url().newBuilder()

.scheme(oldRequest.url().scheme())

.host(oldRequest.url().host());*/

// 新的请求

Request.Builder requestBuilder = oldRequest.newBuilder();

requestBuilder.method(oldRequest.method(), oldRequest.body());

//添加公共参数,添加到header中

if(mHeaderParamsMap.size() > 0){

for(Map.Entry<String,String> params:mHeaderParamsMap.entrySet()){

requestBuilder.header(params.getKey(),params.getValue());

}

}

Request newRequest = requestBuilder.build();

return chain.proceed(newRequest);

}

public static class Builder{

HttpCommonInterceptor mHttpCommonInterceptor;

public Builder(){

mHttpCommonInterceptor = new HttpCommonInterceptor();

}

public Builder addHeaderParams(String key, String value){

mHttpCommonInterceptor.mHeaderParamsMap.put(key,value);

return this;

}

public Builder addHeaderParams(String key, int value){

return addHeaderParams(key, String.valueOf(value));

}

public Builder addHeaderParams(String key, float value){

return addHeaderParams(key, String.valueOf(value));

}

public Builder addHeaderParams(String key, long value){

return addHeaderParams(key, String.valueOf(value));

}

public Builder addHeaderParams(String key, double value){

return addHeaderParams(key, String.valueOf(value));

}

public HttpCommonInterceptor build(){

return mHttpCommonInterceptor;

}

}

}

以上就是添加公共参数的拦截器,在 RetrofitServiceManager 类里面加入OkHttpClient 配置就好了。

代码如下:

// 添加公共参数拦截器

HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder()

.addHeaderParams(“paltform”,“android”)

.addHeaderParams(“userToken”,“1234343434dfdfd3434”)

.addHeaderParams(“userId”,“123445”)

.build();

builder.addInterceptor(commonInterceptor);

项目使用篇 ----->插入广告!本项目来源于金融研习社App,金融理财类的在线教育

项目是基于RxJava1

1.引入依赖:

compile ‘com.google.code.gson:gson:2.6.2’//导入Gson 库

//导入RxJava 和 RxAndroid

compile ‘io.reactivex.rxjava2:rxandroid:2.0.2’

compile ‘io.reactivex.rxjava2:rxjava:2.x.y’

compile ‘com.squareup.retrofit2:retrofit:2.3.0’//导入retrofit

compile ‘com.squareup.retrofit2:converter-gson:2.3.0’//转换器,请求结果转换成Model

compile ‘com.squareup.retrofit2:adapter-rxjava2:2.3.0’//配合Rxjava 使用

compile ‘com.squareup.okhttp3:logging-interceptor:3.8.1’//添加HttpLoggingInterceptor进行调试

2.创建一个HttpService接口:

public interface HttpService {

/**

  • 获取用户详细资料

*/

@POST(“api/XXX/GetUserAllDetails”)

Observable getUserAllDetails(@Body GetUserAllDetailsRequestBean bean);

/**

  • @param apkUrl 下载地址

*/

@GET()

@Streaming

Call downloadNewApk(@Url String apkUrl);

/**

  • 获取推广大使分享图片

*/

@GET(“api/XXX/InvitedImage”)

Observable getInvitedImage(@QueryMap Map<String, Object> map);

}

3.创建http请求类,并在里面初始化并配置Retrofit和OkHttp:

public class HttpMethods {

public String TAG = “HttpMethods”;

public static final String CACHE_NAME = “xxx”;

public static String BASE_URL = URLConstant.BASE_URL;

private static final int DEFAULT_CONNECT_TIMEOUT = 30;

private static final int DEFAULT_WRITE_TIMEOUT = 30;

private static final int DEFAULT_READ_TIMEOUT = 30;

private Retrofit retrofit;

private HttpService httpService;

/**

  • 请求失败重连次数

*/

private int RETRY_COUNT = 0;

private OkHttpClient.Builder okHttpBuilder;

//构造方法私有

private HttpMethods() {

//手动创建一个OkHttpClient并设置超时时间

okHttpBuilder = new OkHttpClient.Builder();

/**

  • 设置缓存

*/

File cacheFile = new File(ApplicationContext.context.getExternalCacheDir(), CACHE_NAME);

Cache cache = new Cache(cacheFile, 1024 * 1024 * 50);

Interceptor cacheInterceptor = new Interceptor() {

@Override

public Response intercept(Chain chain) throws IOException {

Request request = chain.request();

if (!NetUtil.isNetworkConnected()) {

request = request.newBuilder()

.cacheControl(CacheControl.FORCE_CACHE)

.build();

}

Response response = chain.proceed(request);

if (!NetUtil.isNetworkConnected()) {

int maxAge = 0;

// 有网络时 设置缓存超时时间0个小时

response.newBuilder()

.header(“Cache-Control”, “public, max-age=” + maxAge)

.removeHeader(CACHE_NAME)// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效

.build();

} else {

// 无网络时,设置超时为4周

int maxStale = 60 * 60 * 24 * 28;

response.newBuilder()

.header(“Cache-Control”, “public, only-if-cached, max-stale=” + maxStale)

.removeHeader(CACHE_NAME)

.build();

}

return response;

}

};

okHttpBuilder.cache(cache).addInterceptor(cacheInterceptor);

/**

  • 设置头信息

*/

Interceptor headerInterceptor = new Interceptor() {

@Override

public Response intercept(Chain chain) throws IOException {

Request originalRequest = chain.request();

Request.Builder requestBuilder = originalRequest.newBuilder()

.addHeader(“Accept-Encoding”, “gzip”)

.addHeader(“Accept”, “application/json”)

.addHeader(“Content-Type”, “application/json; charset=utf-8”)

.method(originalRequest.method(), originalRequest.body());

requestBuilder.addHeader(“Authorization”, "Bearer " + BaseConstant.TOKEN);//添加请求头信息,服务器进行token有效性验证

Request request = requestBuilder.build();

return chain.proceed(request);

}

};

okHttpBuilder.addInterceptor(headerInterceptor);

// if (BuildConfig.DEBUG) {

HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {

@Override

public void log(String message) {

Logger.d(message);

}

});

loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

//设置 Debug Log 模式

okHttpBuilder.addInterceptor(loggingInterceptor);

// }

/**

  • 设置超时和重新连接

*/

okHttpBuilder.connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS);

okHttpBuilder.readTimeout(DEFAULT_WRITE_TIMEOUT, TimeUnit.SECONDS);

okHttpBuilder.writeTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS);

//错误重连

okHttpBuilder.retryOnConnectionFailure(true);

retrofit = new Retrofit.Builder()

.client(okHttpBuilder.build())

.addConverterFactory(GsonConverterFactory.create())//json转换成JavaBean

.addCallAdapterFactory(RxJava2CallAdapterFactory.create())

.baseUrl(BASE_URL)

.build();

httpService = retrofit.create(HttpService.class);

}

//在访问HttpMethods时创建单例

private static class SingletonHolder {

private static final HttpMethods INSTANCE = new HttpMethods();

}

//获取单例

public static HttpMethods getInstance() {

return SingletonHolder.INSTANCE;

}

/**

  • 获取retrofit

*/

public Retrofit getRetrofit() {

return retrofit;

}

public void changeBaseUrl(String baseUrl) {

retrofit = new Retrofit.Builder()

.client(okHttpBuilder.build())

.addConverterFactory(GsonConverterFactory.create())

.addCallAdapterFactory(RxJava2CallAdapterFactory.create())

.baseUrl(baseUrl)

.build();

httpService = retrofit.create(HttpService.class);

}

/**

  • 获取httpService

*/

public HttpService getHttpService() {

return httpService;

}

/**

  • 设置订阅 和 所在的线程环境

*/

public void toSubscribe(Observable o, DisposableObserver s) {

o.subscribeOn(Schedulers.io())

.unsubscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.retry(RETRY_COUNT)//请求失败重连次数

.subscribe(s);

}

}

4.设置回调:

调用者自己对请求数据进行处理 成功时 通过result是否等于1分别回调onSuccees和onFault,默认处理了401错误转登录。

public class OnSuccessAndFaultSub extends DisposableObserver implements ProgressCancelListener {

/**

  • 是否需要显示默认Loading

*/

private boolean showProgress = true;

private OnSuccessAndFaultListener mOnSuccessAndFaultListener;

private Context context;

private WaitProgressDialog progressDialog;

/**

  • @param mOnSuccessAndFaultListener 成功回调监听

*/

public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener) {

this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;

}

/**

  • @param mOnSuccessAndFaultListener 成功回调监听

  • @param context 上下文

*/

public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener, Context context) {

this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;

this.context = context;

progressDialog = new WaitProgressDialog(context, this);

}

/**

  • @param mOnSuccessAndFaultListener 成功回调监听

  • @param context 上下文

  • @param showProgress 是否需要显示默认Loading

*/

public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener, Context context, boolean showProgress) {

this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;

this.context = context;

progressDialog = new WaitProgressDialog(context, this);

this.showProgress = showProgress;

}

private void showProgressDialog() {

if (showProgress && null != progressDialog) {

progressDialog.show();

}

}

private void dismissProgressDialog() {

if (showProgress && null != progressDialog) {

progressDialog.dismiss();

}

}

/**

  • 订阅开始时调用

  • 显示ProgressDialog

*/

@Override

public void onStart() {

showProgressDialog();

}

/**

  • 完成,隐藏ProgressDialog

*/

@Override

public void onComplete() {

dismissProgressDialog();

progressDialog = null;

}

/**

  • 对错误进行统一处理

  • 隐藏ProgressDialog

*/

@Override

public void onError(Throwable e) {

try {


http://www.kler.cn/a/535375.html

相关文章:

  • langchain教程-11.RAG管道/多轮对话RAG
  • 一个可以在浏览器console内运行的极简爬虫,可列出网页内指定关键词的所有句子。
  • PCA9685舵机控制板使用
  • 问卷数据分析|SPSS之分类变量描述性统计
  • 在 Vue Router 中,params和query的区别?
  • Java 大视界 -- Java 大数据在智能教育中的应用与个性化学习(75)
  • 【工具篇】ChatGPT:开启人工智能新纪元
  • React 打印插件 -- react-to-print
  • C++中的pair,pair和map的结合
  • 接口对象封装思想及实现-笔记
  • Servlet笔记(下)
  • 数据结构与算法学习笔记----博弈论
  • [转]Java面试近一个月的面试总结
  • ElasticSearch业务场景与面试题
  • PCA9685舵机控制板使用
  • OpenBMC:通过qemu-system-arm运行编译好的image
  • Windows编程:下载与安装 Visual Studio 2010
  • 深度学习 - 神经网络的原理
  • 基于多重算法的医院增强型50G全光网络设计与实践:构建智慧医疗新基石(下)
  • SpringAI介绍及本地模型使用方法
  • 【实战篇】巧用 DeepSeek,让 Excel 数据处理更高效
  • 【Ubuntu】ARM交叉编译开发环境解决“没有那个文件或目录”问题
  • DeepSeek模型构建与训练
  • 云计算——AWS Solutions Architect – Associate(saa)1、什么是云,AWS介绍
  • 网络爬虫js逆向之异步栈跟栈案例
  • Flink-WordCount源码解析