retrofit-helper 简洁的封装retrofit,优雅的取消请求

news2024/11/22 17:01:45

retrofit-helper

Retrofit是很多android开发者都在使用的Http请求库!他负责网络请求接口的封装,底层实现是OkHttp,它的一个特点是包含了特别多注解,方便简化你的代码量,CallAdapter.Factory 和Converter.Factory可以很灵活的扩展你的请求。我们在使用的时候还是需要封装一层便于我们使用,retrofit-helper的作用就是再次简化你的请求。

1. Retrofit-helper扩展了那些功能

img

2. 封装逻辑解析

2.1 RetrofitFactory全局管理retrofit实例

DEFAULT 静态变量管理默认常用的的retrofit对象,OTHERS 管理其他多个不同配置的retrofit

/**
 * 创建时间:2018/4/3
 * 编写人: chengxin
 * 功能描述:管理全局的Retrofit实例
 */
public final class RetrofitFactory {
    /**
     * 缓存不同配置的retrofit集合,如不同的url ,converter等
     */
    public static final Map<String, Retrofit> OTHERS = new ConcurrentHashMap<>(2);
    /**
     * 全局的Retrofit对象
     */
    public static volatile Retrofit DEFAULT;
private RetrofitFactory() {
}

public static &lt;T&gt; T create(Class&lt;T&gt; service) {
    //确保多线程的情况下retrofit不为空或者被修改了
    Retrofit retrofit = DEFAULT;
    Utils.checkState(retrofit != null, "DEFAULT == null");
    return retrofit.create(service);
}

/**
 * @param name 获取 OTHERS 中指定名字的retrofit
 */
public static &lt;T&gt; T create(String name, Class&lt;T&gt; service) {
    Utils.checkNotNull(name, "name == null");
    Retrofit retrofit = OTHERS.get(name);
    Utils.checkState(retrofit != null,
            String.format("retrofit named with '%s' was not found , have you put it in OTHERS ?", name));
    return retrofit.create(service);
}
}
2.2 Call2接口继承retrofit.Call 重载 enqueue(Callback callback)方法

enqueue(@Nullable Object tag, Callback2 callback2) 方法传入请求的tag标记此请求,tag标签就是取消请求所需要的

/**
 * 创建时间:2018/4/8
 * 编写人: chengxin
 * 功能描述:添加重载方法{@link Call2#enqueue(Object, Callback2)}方法
 */
public interface Call2<T> extends retrofit2.Call<T> {
    /**
     * @param tag       请求的tag,用于取消请求使用
     * @param callback2 请求的回调
     */
    void enqueue(@Nullable Object tag, Callback2<T> callback2);
@Override
Call2&lt;T&gt; clone();
}
2.3 Callback2 统一处理回调

请求开始、成功处理、失败处理、成功回调、失败回调、请求结束在此统一处理,各方法可以根据业务的不同自行重写,例如:可以重写parseResponse方法根据不通的http code做不同的提示描述 或者

重写parseThrowable方法处理各种Throwable

@UiThread
public abstract class Callback2<T> {
public abstract void onStart(Call2&lt;T&gt; call2);

@NonNull
public Result&lt;T&gt; parseResponse(Call2&lt;T&gt; call2, Response&lt;T&gt; response) {
    T body = response.body();
    if (response.isSuccessful()) {
        if (body != null) {
            return Result.success(body);
        } else {
            return Result.error(new HttpError("暂无数据", response));
        }
    }

    final String msg;
    switch (response.code()) {
        case 400:
            msg = "参数错误";
            break;
        case 401:
            msg = "身份未授权";
            break;
        case 403:
            msg = "禁止访问";
            break;
        case 404:
            msg = "地址未找到";
            break;
        default:
            msg = "服务异常";
    }
    return Result.error(new HttpError(msg, response));
}

/**
 * 统一解析Throwable对象转换为HttpError对象。如果为HttpError,
 * 则为{@link retrofit2.Converter#convert(Object)}内抛出的异常
 *
 * @param call2 call
 * @param t     Throwable
 * @return HttpError result
 */
@NonNull
public HttpError parseThrowable(Call2&lt;T&gt; call2, Throwable t) {
    if (t instanceof HttpError) {
        //用于convert函数直接抛出异常接收
        return (HttpError) t;
    } else if (t instanceof UnknownHostException) {
        return new HttpError("网络异常", t);
    } else if (t instanceof ConnectException) {
        return new HttpError("网络异常", t);
    } else if (t instanceof SocketException) {
        return new HttpError("服务异常", t);
    } else if (t instanceof SocketTimeoutException) {
        return new HttpError("响应超时", t);
    } else {
        return new HttpError("请求失败", t);
    }
}

public abstract void onError(Call2&lt;T&gt; call2, HttpError error);

public abstract void onSuccess(Call2&lt;T&gt; call2, T response);


/**
 * @param t        请求失败的错误信息
 * @param canceled 请求是否被取消了
 */
public abstract void onCompleted(Call2&lt;T&gt; call2, @Nullable Throwable t, boolean canceled);
}
2.4 HttpError 统一处理异常错误

HttpError类中有两个成员属性msg 被body,msg是保存错误的描述信息等,body可以保存异常的具体信息或者原始的json等,onError(Call2 call2, HttpError error)回调方法可以根据body的具体信息做二次处理。

/**
 * 通用的错误信息,一般请求是失败只需要弹出一些错误信息即可,like{@link retrofit2.HttpException}
 * Created by chengxin on 2017/6/22.
 */
public final class HttpError extends RuntimeException {
    private static final long serialVersionUID = -134024482758434333L;
    /**
     * 展示在前端的错误描述信息
     */
    public String msg;
/**
 * &lt;p&gt;
 * 请求失败保存失败信息,for example:
 * &lt;li&gt;BusiModel: {code:xxx,msg:xxx} 业务错误信息&lt;/li&gt;
 * &lt;li&gt;original json:  原始的json&lt;/li&gt;
 * &lt;li&gt;{@link retrofit2.Response}:错误响应体-&gt;Response&lt;?&gt;&lt;/li&gt;
 * &lt;li&gt;Throwable: 抛出的异常信息&lt;/li&gt;
 * &lt;/p&gt;
 */
@Nullable
public final transient Object body;

public HttpError(String msg) {
    this(msg, null);
}

public HttpError(String msg, @Nullable Object body) {
    super(msg);
    if (body instanceof Throwable) {
        initCause((Throwable) body);
    }
    //FastPrintWriter#print(String str)
    this.msg = msg != null ? msg : "null";
    this.body = body;
}

/**
 * 保证和msg一致
 */
@Override
public String getMessage() {
    return msg;
}

@Override
public String toString() {
    return "HttpError {msg="
            + msg
            + ", body="
            + body
            + '}';
}
}
2.5 ExecutorCallAdapterFactory返回Call2请求适配器

处理请求接口方法返回为Call2的请求适配器工厂类

public final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
public static final CallAdapter.Factory INSTANCE = new ExecutorCallAdapterFactory();

private ExecutorCallAdapterFactory() {
}

/**
 * Extract the raw class type from {@code type}. For example, the type representing
 * {@code List&lt;? extends Runnable&gt;} returns {@code List.class}.
 */
public static Class&lt;?&gt; getRawType(Type type) {
    return CallAdapter.Factory.getRawType(type);
}

@Override
public CallAdapter&lt;?, ?&gt; get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call2.class) {
        return null;
    }
    if (!(returnType instanceof ParameterizedType)) {
        throw new IllegalArgumentException(
                "Call return type must be parameterized as Call2&lt;Foo&gt; or Call2&lt;? extends Foo&gt;");
    }
    final Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType);

    final Executor callbackExecutor = retrofit.callbackExecutor();
    if (callbackExecutor == null) throw new AssertionError();

    return new CallAdapter&lt;Object, Call&lt;?&gt;&gt;() {
        @Override
        public Type responseType() {
            return responseType;
        }

        @Override
        public Call&lt;Object&gt; adapt(Call&lt;Object&gt; call) {
            return new ExecutorCallbackCall2&lt;&gt;(callbackExecutor, call);
        }
    };
}
}
2.6 ExecutorCallbackCall2 继承Call2代理OkHttpCall处理UI回调

装饰者模式代理OkHttpCall的所有方法,线程调度处理 Callback2 的回调方法在主线程执行

final class ExecutorCallbackCall2<T> implements Call2<T> {
    private final Executor callbackExecutor;
    private final Call<T> delegate;
/**
 * The executor used for {@link Callback} methods on a {@link Call}. This may be {@code null},
 * in which case callbacks should be made synchronously on the background thread.
 */
ExecutorCallbackCall2(Executor callbackExecutor, Call&lt;T&gt; delegate) {
    this.callbackExecutor = callbackExecutor;
    this.delegate = delegate;
}

@Override
public void enqueue(final Callback&lt;T&gt; callback) {
    throw new UnsupportedOperationException("please call enqueue(Object tag, Callback2&lt;T&gt; callback2)");
}

@Override
public void enqueue(@Nullable Object tag, final Callback2&lt;T&gt; callback2) {
    Utils.checkNotNull(callback2, "callback2==null");
    CallManager.getInstance().add(this, tag != null ? tag : "NO_TAG");
    callbackExecutor.execute(new Runnable() {
        @Override
        public void run() {
            if (!isCanceled()) {
                callback2.onStart(ExecutorCallbackCall2.this);
            }
        }
    });

    delegate.enqueue(new Callback&lt;T&gt;() {
        @Override
        public void onResponse(Call&lt;T&gt; call, final Response&lt;T&gt; response) {
            callbackExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    callResult(callback2, response, null);
                }
            });
        }

        @Override
        public void onFailure(Call&lt;T&gt; call, final Throwable t) {
            callbackExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    callResult(callback2, null, t);
                }
            });
        }
    });
}

@UiThread
private void callResult(Callback2&lt;T&gt; callback2, @Nullable Response&lt;T&gt; response, @Nullable Throwable failureThrowable) {
    try {
        if (!isCanceled()) {
            //1、获取解析结果
            Result&lt;T&gt; result;
            if (response != null) {
                result = callback2.parseResponse(this, response);
                Utils.checkNotNull(result, "result==null");
            } else {
                Utils.checkNotNull(failureThrowable, "failureThrowable==null");
                HttpError error = callback2.parseThrowable(this, failureThrowable);
                result = Result.error(error);
            }
            //2、回调成功失败
            if (result.isSuccess()) {
                callback2.onSuccess(this, result.body());
            } else {
                callback2.onError(this, result.error());
            }
        }
        callback2.onCompleted(this, failureThrowable, isCanceled());
    } finally {
        CallManager.getInstance().remove(this);
    }
}

@Override
public boolean isExecuted() {
    return delegate.isExecuted();
}

@Override
public Response&lt;T&gt; execute() throws IOException {
    return delegate.execute();
}

@Override
public void cancel() {
    delegate.cancel();
}

@Override
public boolean isCanceled() {
    return delegate.isCanceled();
}

@SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
@Override
public Call2&lt;T&gt; clone() {
    return new ExecutorCallbackCall2&lt;&gt;(callbackExecutor, delegate.clone());
}

@Override
public Request request() {
    return delegate.request();
}
}
2.7 CallManager统一管理请求,取消请求

全局保存所有的请求,添加 、删除请求,取消某个某些匹配tag的请求。可以在Activity 或Fragment的销毁方法中调用CallManager.getInstance().cancel( yourTag )

/**
 * 创建时间:2018/5/31
 * 编写人: chengxin
 * 功能描述:全局管理Call请求管理,just like {@link okhttp3.Dispatcher}
 */
public final class CallManager implements ActionManager<Call<?>> {
    @GuardedBy("this")
    private final List<CallTag> callTags = new ArrayList<>(4);
    private volatile static CallManager instance;
private CallManager() {
}

public static CallManager getInstance() {
    if (instance == null) {
        synchronized (CallManager.class) {
            if (instance == null) {
                instance = new CallManager();
            }
        }
    }
    return instance;
}

@Override
public synchronized void add(Call&lt;?&gt; call, Object tag) {
    Utils.checkState(!contains(call), "Call&lt;?&gt;  " + call + " is already added.");
    callTags.add(new CallTag(call, tag));
}

/**
 * 当call结束时移除
 *
 * @param call Retrofit Call
 */
@Override
public synchronized void remove(Call&lt;?&gt; call) {
    if (callTags.isEmpty())
        return;
    for (int index = 0; index &lt; callTags.size(); index++) {
        if (call == callTags.get(index).call) {
            //like okhttp3.Headers#removeAll(String name)
            //remove(int index) 方法优于 remove(Object o),无需再次遍历
            callTags.remove(index);
            break;
        }
    }
}

/**
 * 取消并移除对应tag的call,确保Call被取消后不再被引用,
 * 结合{@link #remove(Call)}方法双保险
 *
 * @param tag call对应的tag
 */
@Override
public synchronized void cancel(final @Nullable Object tag) {
    if (callTags.isEmpty())
        return;
    if (tag != null) {
        for (int index = 0; index &lt; callTags.size(); index++) {
            CallTag callTag = callTags.get(index);
            if (callTag.tag.equals(tag)) {
                callTag.call.cancel();
                callTags.remove(index);
                index--;
            }
        }
    } else {
        for (CallTag callTag : callTags) {
            callTag.call.cancel();
        }
        callTags.clear();
    }
}

@Override
public synchronized boolean contains(Call&lt;?&gt; call) {
    for (CallTag callTag : callTags) {
        if (call == callTag.call) {
            return true;
        }
    }
    return false;
}

/**
 * 保存call和tag
 */
final static class CallTag {
    private final Call&lt;?&gt; call;
    private final Object tag;

    CallTag(Call&lt;?&gt; call, Object tag) {
        Utils.checkNotNull(call == null, "call==null");
        Utils.checkNotNull(tag == null, "tag==null");
        this.call = call;
        this.tag = tag;
    }
}
}
2.8 ProgressInterceptor 拦截器监听下载和上传进度

继承okhttp3.Interceptor ,构造方法中传入ProgressListener监听进度

/**
 * 创建时间:2018/8/2
 * 编写人: chengxin
 * 功能描述:上传或下载进度监听拦截器
 */
public class ProgressInterceptor implements Interceptor {
private final ProgressListener mProgressListener;

public ProgressInterceptor(ProgressListener progressListener) {
    Utils.checkNotNull(progressListener, "progressListener==null");
    this.mProgressListener = progressListener;
}

@Override
public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RequestBody requestBody = request.body();
    //判断是否有上传需求
    if (requestBody != null &amp;&amp; requestBody.contentLength() &gt; 0) {
        Request.Builder builder = request.newBuilder();
        RequestBody newRequestBody = new ProgressRequestBody(requestBody, mProgressListener, request);
        request = builder.method(request.method(), newRequestBody).build();
    }

    Response response = chain.proceed(request);
    ResponseBody responseBody = response.body();
    if (responseBody != null &amp;&amp; responseBody.contentLength() &gt; 0) {
        Response.Builder builder = response.newBuilder();
        ResponseBody newResponseBody = new ProgressResponseBody(responseBody, mProgressListener, request);
        response = builder.body(newResponseBody).build();
    }
    return response;
}
}

2.9 HttpLoggingInterceptor 可以单独指定某个请求的日志级别

构造OkhttpClient时添加此拦截器,在请求的服务方法中添加注解

@Headers(“LogLevel:NONE”) 或 @Headers(“LogLevel:BASIC”) 或 @Headers(“LogLevel:HEADERS”) 或@Headers(“LogLevel:BODY”)

@FormUrlEncoded
@Headers("LogLevel:HEADERS")
@POST("user/login")
Call2<LoginInfo> getLogin(@Field("username") String username, @Field("password") String password);

3.实战

3.1 初始化全局Retrofit对象
Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://wanandroid.com/")
                .callFactory(new OkHttpClient.Builder()
                        .addNetworkInterceptor(httpLoggingInterceptor)
                        .build())
                //必须添加此adapter 用于构建处理回调
                .addCallAdapterFactory(ExecutorCallAdapterFactory.INSTANCE)
                //添加自定义json解析器 
                .addConverterFactory(GsonConverterFactory.create())
                .build();
 RetrofitFactory.DEFAULT = retrofit;

 //可以添加多个,如:
 RetrofitFactory.OTHERS.put("other",otherRetrofit);
3.2 添加请求服务接口

下面为登录的 post请求

@FormUrlEncoded
@POST("user/login")
Call2<LoginInfo> getLogin(@Field("username") String username, @Field("password") String password);
3.3 添加ILoadingView,用于开启和结束动画

Activity 或者Fragment 可以继承 ILoadingView接口实现开始和结束动画

public interface ILoadingView {
    /**
     * 显示加载
     */
    void showLoading();
/**
 * 隐藏加载
 */
void hideLoading();
}
3.4 添加AnimCallback 处理动画

这里重写parseThrowable处理一些Callback2中为未处理的异常

public abstract class AnimCallback<T> extends Callback2<T> {
    private ILoadingView mLoadingView;
public AnimCallback(@Nullable ILoadingView loadingView) {
    this.mLoadingView = loadingView;
}

@Override
public void onStart(Call2&lt;T&gt; call2) {
    if (mLoadingView != null)
        mLoadingView.showLoading();
}

@Override
public void onCompleted(Call2&lt;T&gt; call2, @Nullable Throwable t, boolean canceled) {
    if (canceled)
        return;
    if (mLoadingView != null)
        mLoadingView.hideLoading();
}

@NonNull
@Override
public HttpError parseThrowable(Call2&lt;T&gt; call2, Throwable t) {
    HttpError filterError;
    if (t instanceof JsonSyntaxException) {
        filterError = new HttpError("解析异常", t);
    } else {
        filterError = super.parseThrowable(call2, t);
    }
    return filterError;
}
}
3.5 发起请求
RetrofitFactory.create(ApiService.class)
        .getLogin("xxxxx", "123456")
        .enqueue(hashCode(), new AnimCallback<LoginInfo>(this) {
            @Override
            public void onError(Call2<LoginInfo> call2, HttpError error) {
                //处理失败
            }
        @Override
        public void onSuccess(Call2&lt;LoginInfo&gt; call2, LoginInfo response) {
           //处理成功 如保存登录信息等
        }
    });
 //在onDestor中取消未结束的请求
   @Override
    protected void onDestroy() {
        super.onDestroy();
        //hashCode() 能保证唯一性,取消当前页面所发起的所有请求,只要
        // enqueue(tag, callback2) 传入的是对应的hashCode() 即可
        CallManager.getInstance().cancel(hashCode());
    }

4.注意事项

4.1 构建retrofit是需要ExecutorCallAdapterFactory实例,否则无法处理返回为Call2的服务接口
4.2 Callback2的回调函数均在主线程执行,如果调用了Call2.cancel()方法,除了onCompleted()方法会执行外其他回调方法都不会执行

5.下载

implementation "com.xcheng:retrofit-helper:1.0.0"\
Copyright 2019 xchengDroid

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.
   
 

更多Android进阶指南 可以扫码 解锁 《Android十大板块文档》

1.Android车载应用开发系统学习指南(附项目实战)

2.Android Framework学习指南,助力成为系统级开发高手

3.2023最新Android中高级面试题汇总+解析,告别零offer

4.企业级Android音视频开发学习路线+项目实战(附源码)

5.Android Jetpack从入门到精通,构建高质量UI界面

6.Flutter技术解析与实战,跨平台首要之选

7.Kotlin从入门到实战,全方面提升架构基础

8.高级Android插件化与组件化(含实战教程和源码)

9.Android 性能优化实战+360°全方面性能调优

10.Android零基础入门到精通,高手进阶之路

敲代码不易,关注一下吧。ღ( ´・ᴗ・` ) 🤔

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/861893.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“崩溃”漏洞会影响英特尔 CPU 的使用寿命,可能会泄露加密密钥等

对于 CPU 安全漏洞来说&#xff0c;本周是重要的一周。昨天&#xff0c;不同的安全研究人员发布了两个不同漏洞的详细信息&#xff0c;一个影响多代英特尔处理器&#xff0c;另一个影响最新的 AMD CPU。“ Downfall ”和“ Inception ”&#xff08;分别&#xff09;是不同的错…

CEC2013(MATLAB):能量谷优化算法EVO求解CEC2013的28个函数

一、能量谷优化算法EVO 能量谷优化算法&#xff08;Energy valley optimizer&#xff0c;EVO&#xff09;是MahdiAzizi等人于2023年提出的一种新颖的元启发式算法&#xff0c;其灵感来自关于稳定性和不同粒子衰变模式的物理原理。能量谷优化算法&#xff08;Energy valley opt…

AI时代的较量,MixTrust能否略胜一筹?

人工智能的能力正在迅速接近人类&#xff0c;而在许多细分领域&#xff0c;已经超越了人类。虽然短期内这个突破是否会导致人工通用智能&#xff08;AGI&#xff09;还不清楚&#xff0c;但我们现在有的模型被训练成在数字交互中完美地模仿高能人类。尽管AGI仍不确定&#xff0…

时序预测 | MATLAB实现CNN-BiGRU-Attention时间序列预测

时序预测 | MATLAB实现CNN-BiGRU-Attention时间序列预测 目录 时序预测 | MATLAB实现CNN-BiGRU-Attention时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现CNN-BiGRU-Attention时间序列预测&#xff0c;CNN-BiGRU-Attention结合注意力机制时…

Java课题笔记~ Request请求

1.请求消息格式 客户端发送一个HTTP请求到服务器的请求消息包括以下格式&#xff1a; 请求行&#xff08;request line&#xff09;、请求头部&#xff08;header&#xff09;、空行和请求数据四个部分组成。下图给出了请求报文的一般格式。 GET请求&#xff1a; POST请求&am…

每日一学——OSI参考模型

OSI参考模型&#xff08;Open Systems Interconnection Reference Model&#xff09;是国际标准化组织&#xff08;ISO&#xff09;制定的一个网络通信协议的概念框架。它将网络通信划分为七个层次&#xff0c;每个层次负责不同的功能和任务&#xff0c;从物理层到应用层依次为…

【ThreadPoolTaskSchedule】实现手动开启关闭定时任务

最近在公司呢&#xff0c;有一个需求&#xff0c;大概意思就是需要我去调用远程接口&#xff0c;但如果出现异常或者响应失败的时候&#xff0c;就需要开启重试机制&#xff0c;直到返回成功的响应为止。我很疑惑&#xff0c;按理说这种情况通常都应该有一个最大重试次数吗&…

python编程英语词汇大全app,python常用单词中英对照

大家好&#xff0c;小编为大家解答python编程英语词汇大全 知乎的问题。很多人还不知道python编程英语词汇大全app&#xff0c;现在让我们一起来看看吧&#xff01; 第1天 editor[edtr]n. 编者&#xff0c;编辑&#xff1b;社论撰写人&#xff1b;编辑装置 setting[set]n. 环境…

《爬虫》爬取页面图片并保存

爬虫 前言代码效果 简单的爬取图片 前言 这几天打算整理与迁移一下博客。因为 CSDN 的 Markdown 编辑器很好用 &#xff0c;所以全部文章与相关图片都保存在 CSDN。而且 CSDN 支持一键导出自己的文章为 markdown 文件。但导出的文件中图片的连接依旧是 url 连接。为了方便将图…

封装一个常用的Enum

前言 项目里面经常用到一些下拉框 尝试封装一个比较好用的Enum 文件目录 ├── utils.ts ├── index.txs └── package.josn代码 utils.ts class Enum {static keys: string[];static values: Enum[];/*** call this function after declare all staic enum variable*…

【效率提升-Perl脚本】根据Verilog文件自动生成tb文件

文章目录 Verilog端口文件&#xff08;仅做示范用&#xff09;对应的tb文件相应代码 在数字IC设计过程中&#xff0c;根据顶层生成testbench时存在很多重复性工作&#xff0c;因此为了提高工作效率&#xff0c;特地开发此脚本。 相应的python脚本见链接&#xff1a; 【效率提升…

跨境商城app源代码开发--Java、H5用户端

随着全球电子商务的快速发展&#xff0c;跨境贸易已经成为一种新的商业趋势。在这个背景下&#xff0c;开发一个跨境商城APP&#xff0c;提供一站式跨境贸易服务&#xff0c;具有非常重要的意义。本文将详细阐述使用Java和H5技术进行跨境商城APP用户端源代码开发的步骤。 一、…

帮源头厂家“跑市场、谈终端、拿订单” 郑州:找准发力点,入局“预制菜”

过去5年&#xff0c;国内预制菜行业大火&#xff0c;复合增速高达95%&#xff0c;全国相关企业逼近6万家&#xff0c;面对这盘万亿级的大棋&#xff0c;郑州近日终于出招了&#xff0c;而且&#xff0c;一出就是大招—— 经相关部门批准&#xff0c;位于郑东新区的“郑菜直供园…

学C的第三十三天【C语言文件操作】

相关代码gitee自取&#xff1a; C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a; 学C的第三十二天【动态内存管理】_高高的胖子的博客-CSDN博客 1 . 为什么要使用文件 以前面写的通讯录为例&#xff0c;当通讯录运行起来的时候&#xff0c;可以给通讯录中增加、删…

LeetCode 1572. 矩阵对角线元素的和

【LetMeFly】1572.矩阵对角线元素的和 力扣题目链接&#xff1a;https://leetcode.cn/problems/matrix-diagonal-sum/ 给你一个正方形矩阵 mat&#xff0c;请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1&…

JDK1.8 jvisualvm远程连接linux服务器tomcat8

JDK1.8 jvisualvm远程连接linux服务器tomcat jvisualvm工具 1、 可以通过官网单独下载 2、 JDK安装的bin路径下Java VisualVM.exe应用程序打开软件截图 linux服务器上tomcat8配置 只需要修改tomcat8 bin目录下的catalina.sh文件在catalina.sh文件添加以下信息 注意IP地址为…

PPO和文本生成

策略梯度 策略梯度&#xff08;Policy Gradient&#xff09;方法梯度的计算如下&#xff1a; E ( a t , s t ) ∈ π θ [ A ^ t ∇ θ log ⁡ π θ ( a t ∣ s t ) ] \mathbb E_{(a_t,s_t) \in \pi_\theta}[\hat A_t \nabla_ \theta \log \pi_\theta(a_t | s_t)] E(at​,st…

了解IL汇编跳转语句

il代码&#xff0c; .assembly extern mscorlib {}.assembly Test{.ver 1:0:1:0}.module test.exe.method static void main() cil managed{.maxstack 5.entrypointldstr "Enter First Number"call void [mscorlib]System.Console::WriteLine (string)call string …

低代码平台 数据库字段值不重复

在开发过程中&#xff0c;要求表里某字段值唯一 一、场景 在单据&#xff0c;要求某字段值不重复 查看数据模型&#xff1a; 查看单据&#xff1a; 二、问题 区域编码&#xff0c;区域名称不重复 三、解决方案 1&#xff09;数据库加索引 2&#xff09;书写保存后存储过…

Python(七十八)字符串的常用操作——字符串大小写转换操作

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…