安卓项目实战之强大的网络请求框架okGo使用详解(一):实现get,post基本网络请求,下载上传进度监听以及对Callback自定义的深入理解

2023-11-14

1.添加依赖

//必须使用
compile 'com.lzy.net:okgo:3.0.4'

//以下三个选择添加,okrx和okrx2不能同时使用,一般选择添加最新的rx2支持即可
compile 'com.lzy.net:okrx:1.0.2'
compile 'com.lzy.net:okrx2:2.0.2'  
compile 'com.lzy.net:okserver:2.0.5'

OkHttp
okgo使用的okhttp的版本是最新的3.8.0版本,okGo框架的GitHub地址:https://github.com/jeasonlzy/okhttp-OkGo
OkRx2
是针对RxJava2,将OkGO扩展的项目,使用的RxJava2的版本如下:

compile 'io.reactivex.rxjava2:rxjava:2.1.0'

OkRx2主要功能:

1.可以很完美结合RxJava做网络请求
2.在使用上比Retrofit更简单方便,门槛更低,灵活性更高
3.网络请求和RxJava调用可以做成一条链试,写法优雅
4.使用Converter接口,支持任意类型的数据自动解析
5.OkRx是扩展的OkGo,所以OkGo包含的所有功能和写法,OkRx全部支持。

OkServer
也是对okGo的扩展,包括两个核心入口类:
1.OkDownload是统一的下载管理,支持断点下载功能,该类详细文档查看
2.OkUpload是统一的上传管理,该类详细文档查看

2.全局的配置

一般在Aplication,或者基类中配置,只需要调用一次即可,配置示例如下:

public class GApp extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        initOkGo();  // 全局配置okGo
    }

    private void initOkGo() {
        //---------这里给出的是示例代码,告诉你可以这么传,实际使用的时候,根据需要传,不需要就不传-------------//
        HttpHeaders headers = new HttpHeaders();
        headers.put("commonHeaderKey1", "commonHeaderValue1");    //header不支持中文,不允许有特殊字符
        headers.put("commonHeaderKey2", "commonHeaderValue2");
        HttpParams params = new HttpParams();
        params.put("commonParamsKey1", "commonParamsValue1");     //param支持中文,直接传,不要自己编码
        params.put("commonParamsKey2", "这里支持中文参数");
        //----------------------------------------------------------------------------------------//

        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        //log相关
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor("OkGo");
        loggingInterceptor.setPrintLevel(HttpLoggingInterceptor.Level.BODY);        //log打印级别,决定了log显示的详细程度
        loggingInterceptor.setColorLevel(Level.INFO);                               //log颜色级别,决定了log在控制台显示的颜色
        builder.addInterceptor(loggingInterceptor);                                 //添加OkGo默认debug日志
        //第三方的开源库,使用通知显示当前请求的log,不过在做文件下载的时候,这个库好像有问题,对文件判断不准确
        //builder.addInterceptor(new ChuckInterceptor(this));

        //超时时间设置,默认60秒
        builder.readTimeout(OkGo.DEFAULT_MILLISECONDS, TimeUnit.MILLISECONDS);      //全局的读取超时时间
        builder.writeTimeout(OkGo.DEFAULT_MILLISECONDS, TimeUnit.MILLISECONDS);     //全局的写入超时时间
        builder.connectTimeout(OkGo.DEFAULT_MILLISECONDS, TimeUnit.MILLISECONDS);   //全局的连接超时时间

        //自动管理cookie(或者叫session的保持),以下几种任选其一就行
        //builder.cookieJar(new CookieJarImpl(new SPCookieStore(this)));            //使用sp保持cookie,如果cookie不过期,则一直有效
        builder.cookieJar(new CookieJarImpl(new DBCookieStore(this)));              //使用数据库保持cookie,如果cookie不过期,则一直有效
        //builder.cookieJar(new CookieJarImpl(new MemoryCookieStore()));            //使用内存保持cookie,app退出后,cookie消失

        //https相关设置,以下几种方案根据需要自己设置
        //方法一:信任所有证书,不安全有风险
        HttpsUtils.SSLParams sslParams1 = HttpsUtils.getSslSocketFactory();
        //方法二:自定义信任规则,校验服务端证书
        //HttpsUtils.SSLParams sslParams2 = HttpsUtils.getSslSocketFactory(new SafeTrustManager());
        //方法三:使用预埋证书,校验服务端证书(自签名证书)
        //HttpsUtils.SSLParams sslParams3 = HttpsUtils.getSslSocketFactory(getAssets().open("srca.cer"));
        //方法四:使用bks证书和密码管理客户端证书(双向认证),使用预埋证书,校验服务端证书(自签名证书)
        //HttpsUtils.SSLParams sslParams4 = HttpsUtils.getSslSocketFactory(getAssets().open("xxx.bks"), "123456", getAssets().open("yyy.cer"));
        builder.sslSocketFactory(sslParams1.sSLSocketFactory, sslParams1.trustManager);
        //配置https的域名匹配规则,详细看demo的初始化介绍,不需要就不要加入,使用不当会导致https握手失败
        //builder.hostnameVerifier(new SafeHostnameVerifier());

        // 其他统一的配置
        OkGo.getInstance().init(this)                           // 必须调用初始化
                .setOkHttpClient(builder.build())               // 底层okhttp框架的参数设置
                // okGo框架的特有设置
                .setCacheMode(CacheMode.NO_CACHE)               //全局统一缓存模式,默认不使用缓存,可以不传
                .setCacheTime(CacheEntity.CACHE_NEVER_EXPIRE)   //全局统一缓存时间,默认永不过期,可以不传
                .setRetryCount(3)                               //全局统一超时重连次数,默认为三次,那么最差的情况会请求4次(一次原始请求,三次重连请求),不需要可以设置为0
                .addCommonHeaders(headers)                      //全局公共头
                .addCommonParams(params);                       //全局公共参数
    }

    /**
     * 这里只是我谁便写的认证规则,具体每个业务是否需要验证,以及验证规则是什么,请与服务端或者leader确定
     * 重要的事情说三遍,以下代码不要直接使用,只是提供一个自定义认证规则的实现案例供参考,请自行注释掉
     */
    private class SafeTrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            try {
                for (X509Certificate certificate : chain) {
                    certificate.checkValidity(); //检查证书是否过期,签名是否通过等
                }
            } catch (Exception e) {
                throw new CertificateException(e);
            }
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }

    /**
     * 这里只是我谁便写的https的域名匹配规则,具体每个业务验证规则是什么,请与服务端或者leader确定
     * 重要的事情说三遍,以下代码不要直接使用,此处只是提供自定义例子以供参考,请自行注释掉
     */
    private class SafeHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            //验证主机名是否匹配
            //return hostname.equals("server.jeasonlzy.com");
            return true;
        }
    }
}

如果你什么都不想自己写,那么下面一行代码就够了:

OkGo.getInstance().init(this);

即采用框架默认配置。

3.执行网络请求

关于使用okGo进行网络请求时所支持的所有配置见下,这里演示的是一次普通请求所有能配置的参数,真实使用时不需要配置这么多,按自己的需要选择性的使用即可:
无论做什么请求,第一行的泛型一定要加!!!
在这里插入图片描述关于上面每个配置的解释:
1,第一行的泛型ServerMode一定要特别注意,这个表示你请求到的网络的数据类型是什么,必须指定,否则无法解析网络数据。
2,.post(url):这个表示当前请求是post请求,当然一共支持GET,HEAD,OPTIONS,POST,PUT,DELETE, PATCH, TRACE这8种请求方式,你只需要改改这个方法名就行了,很方便。
3,.params():添加参数的时候,最后一个isReplace为可选参数,默认为true,即代表相同key的时候,后添加的会覆盖先前添加的,为false时不会覆盖,图上只有两个参数的写法:
在这里插入图片描述
4,.tag(this):请求的tag,用于标识当前的请求,方便后续取消对应的请求,如果你不需要取消请求,也可以不用设置,取消请求的写法:
通常我们在Activity中做网络请求,当Activity销毁时要取消请求否则会发生内存泄露,那么就可以在onDestory()里面写如下代码:
在这里插入图片描述前两个方法用于取消okHttpClient是我们在全局配置的情况下(okHttpClient的配置也就是application中builder部分的配置)。
后两个方法用于取消okHttpClient是我们通过调用.client()传入的外部配置(见第12小点处说明),而非application中我们的配置的情况。
5, .isMultipart():该方法表示是否强制使用multipart/form-data表单上传,因为该框架在有文件的时候,无论你是否设置这个参数,默认都是multipart/form-data格式上传,但是如果参数中不包含文件,默认使用application/x-www-form-urlencoded格式上传,如果你的服务器要求无论是否有文件,都要使用表单上传,那么可以用这个参数设置为true。
6,.isSpliceUrl():该方法表示是否强制将params的参数拼接到url后面,默认false不拼接。一般来说,post、put等有请求体的方法应该把参数都放在请求体中,不应该放在url上,但是有的服务端可能不太规范,url和请求体都需要传递参数,那么这时候就使用该参数,他会将你所有使用.params()方法传递的参数,自动拼接在url后面。
7,.retryCount():该方法是配置超时重连次数,也可以在全局初始化的时候设置,默认使用全局的配置,即为3次,你也可以在这里为你的这个请求特殊配置一个,并不会影响全局其他请求的超时重连次数。
8,.cacheKey() .cacheTime() .cacheMode():这三个是缓存相关的配置,详细请看缓存介绍
9,.headers():该方法是传递服务端需要的请求头
记住任何时候,任何地方,以下这两行设置代码永远不起作用:

headers("Content-Type", "你自己设置的值")
headers("Content-Length", 一个数值)

Content-Type为框架根据上传类型智能识别并添加,其中的对应规则见文章末尾的表格,Content-Length永远自动根据你上传的内容的真实大小自动添加,不可修改。

10,.params():该方法传递键值对参数,所有up开头的方法不能与params()方法混用,如果混用,将按up方法的行为来,所有params()设置的参数将丢失。
11,.addUrlParams() .addFileParams() .addFileWrapperParams():这里是支持一个key传递多个文本参数,也支持一个key传递多个文件参数。

12,每个请求都有一个.client()方法可以传递一个OkHttpClient对象,表示当前这个请求将使用外界传入的这个OkHttpClient对象,其他的请求还是使用全局的保持不变。那么至于这个OkHttpClient你想怎么配置,或者配置什么东西,那就随意了是不,改个超时时间,加个拦截器什么的统统都是可以的,看你心情喽,一般该种情况使用很少,但是也会存在。
如果你的当前请求使用的是你传递的OkHttpClient对象的话,那么当你调用OkGo.getInstance().cancelTag(tag)的时候,是取消不了这个请求的,因为OkGo只能取消使用全局配置的请求,不知道你这个请求是用你自己的OkHttpClient的,如果一定要取消,可以是使用OkGo提供的重载方法,具体看取消请求的其他重载方法。

4,在回调中处理response,如下:

@Override
 public void onSuccess(Response<String> response) {
        handleResponse(response);  // 该方法能够显示成功时的请求状态,请求头,响应数据以及响应头信息
        String  result = response.body();  // 获取数据
  }
  
// 或者

@Override
 public void onSuccess(Response<LzyResponse<ServerModel>> response) {
        handleResponse(response);  // 该方法能够显示成功时的请求状态,请求头,响应数据以及响应头信息
        LzyResponse<ServerModel>  result = response.body();  // 只获取数据
        ServerModel model = response.body().results;
  }

 @Override
 public void onError(Response<LzyResponse<ServerModel>> response) {
        handleError(response); // 该方法能够显示失败时的请求状态,请求头,响应数据以及响应头信息
 }

其中handleResponse(response);和handleError(response);的方法定义如下:


protected <T> void handleResponse(T data) {
        Response<T> response = new Response<>();
        response.setBody(data);
        handleResponse(response);
}


protected <T> void handleResponse(Response<T> response) {
        StringBuilder sb;
        Call call = response.getRawCall();
        if (call != null) {
            requestState.setText("请求成功  请求方式:" + call.request().method() + "\n" + "url:" + call.request().url());

            Headers requestHeadersString = call.request().headers();
            Set<String> requestNames = requestHeadersString.names();
            sb = new StringBuilder();
            for (String name : requestNames) {
                sb.append(name).append(" : ").append(requestHeadersString.get(name)).append("\n");
            }
            requestHeaders.setText(sb.toString());
        } else {
            requestState.setText("--");
            requestHeaders.setText("--");
        }
        T body = response.body();
        if (body == null) {
            responseData.setText("--");
        } else {
            if (body instanceof String) {
                responseData.setText((String) body);
            } else if (body instanceof List) {
                sb = new StringBuilder();
                List list = (List) body;
                for (Object obj : list) {
                    sb.append(obj.toString()).append("\n");
                }
                responseData.setText(sb.toString());
            } else if (body instanceof Set) {
                sb = new StringBuilder();
                Set set = (Set) body;
                for (Object obj : set) {
                    sb.append(obj.toString()).append("\n");
                }
                responseData.setText(sb.toString());
            } else if (body instanceof Map) {
                sb = new StringBuilder();
                Map map = (Map) body;
                Set keySet = map.keySet();
                for (Object key : keySet) {
                    sb.append(key.toString()).append(" : ").append(map.get(key)).append("\n");
                }
                responseData.setText(sb.toString());
            } else if (body instanceof File) {
                File file = (File) body;
                responseData.setText("数据内容即为文件内容\n下载文件路径:" + file.getAbsolutePath());
            } else if (body instanceof Bitmap) {
                responseData.setText("图片的内容即为数据");
            } else {
                responseData.setText(Convert.formatJson(body));
            }
        }

        okhttp3.Response rawResponse = response.getRawResponse();
        if (rawResponse != null) {
            Headers responseHeadersString = rawResponse.headers();
            Set<String> names = responseHeadersString.names();
            sb = new StringBuilder();
            sb.append("url : ").append(rawResponse.request().url()).append("\n\n");
            sb.append("stateCode : ").append(rawResponse.code()).append("\n");
            for (String name : names) {
                sb.append(name).append(" : ").append(responseHeadersString.get(name)).append("\n");
            }
            responseHeader.setText(sb.toString());
        } else {
            responseHeader.setText("--");
        }
    }



    protected <T> void handleError() {
        Response<T> response = new Response<>();
        handleResponse(response);
    }


    protected <T> void handleError(Response<T> response) {
        if (response == null) return;
        if (response.getException() != null) response.getException().printStackTrace();
        StringBuilder sb;
        Call call = response.getRawCall();
        if (call != null) {
            requestState.setText("请求失败  请求方式:" + call.request().method() + "\n" + "url:" + call.request().url());

            Headers requestHeadersString = call.request().headers();
            Set<String> requestNames = requestHeadersString.names();
            sb = new StringBuilder();
            for (String name : requestNames) {
                sb.append(name).append(" : ").append(requestHeadersString.get(name)).append("\n");
            }
            requestHeaders.setText(sb.toString());
        } else {
            requestState.setText("--");
            requestHeaders.setText("--");
        }

        responseData.setText("--");
        okhttp3.Response rawResponse = response.getRawResponse();
        if (rawResponse != null) {
            Headers responseHeadersString = rawResponse.headers();
            Set<String> names = responseHeadersString.names();
            sb = new StringBuilder();
            sb.append("stateCode : ").append(rawResponse.code()).append("\n");
            for (String name : names) {
                sb.append(name).append(" : ").append(responseHeadersString.get(name)).append("\n");
            }
            responseHeader.setText(sb.toString());
        } else {
            responseHeader.setText("--");
        }
    }

CallBack

Callback一共有以下几个回调,除onSuccess必须实现以外,其余均可以按需实现:
在这里插入图片描述
由于Callback接口实现了Converter接口,所以这个接口里面的方法也必须实现:
在这里插入图片描述为了避免每一次自定义callback回调时都要重写这8个方法,okgo框架帮我们已经内置预先定义好了一个抽象回调类AbsCallback,该抽象类中将除onSuccess()和convertResponse()方法之外的其余6个方法均已定义为非抽象方法,这样在我们需要自定义实现接口CallBack的实现类的时候,就不用直接实现CallBack接口重写8个方法了,而是间接继承抽象类AbsCallback,这样我们只需重写onSuccess()和convertResponse()这两个方法,
其他6个方法此时允许我们在需要的时候再去重写。
以后我们自定义的CallBack抽象类均通过继承AbsCallback来定义。

自定义的指定泛型的Callback在继承AbsCallback抽象类重写convertResponse(response)方法的逻辑:其实就是要先创建该泛型对应的Converter的转换器实现类的对象,在底层调用的其实是转换器类对象中重写的convertResponse方法的实现,该方法中的逻辑就是对response的数据内容进行解析逻辑,返回泛型指定的类型,所以我们还需要针对不同的自定义泛型的Callback实现Convert<泛型>来具体实现转换器类,在该类中提供对convertResponse方法的实现,然后就可以在callback的回调方法中调用转换器类对象的convertResponse方法完成请求结果数据的解析,然后以指定泛型返回。

一句话总结即就是两个convertResponse方法之间的调用:自定义Callback重写的convertResponse方法实现调用了我们额外定义的返回指定泛型类型的实现Converter接口的转换器类中的convertResponse方法。

例如我们来看框架帮我们内置的StringCallback抽象类的实现,代码如下:

public abstract class StringCallback extends AbsCallback<String> {

    private StringConvert convert;   // 转换器对象

    public StringCallback() {
        convert = new StringConvert();
    }

    @Override
    public String convertResponse(Response response) throws Throwable {
        String s = convert.convertResponse(response);
        response.close();
        return s;
    }
}

我们可以看到重写的convertResponse方法中调用了针对泛型为String定义的转换器StringConvert对象convert的convertResponse方法,而这个额外的StringConvert转换器类定义代码如下:

public class StringConvert implements Converter<String> {

    @Override
    public String convertResponse(Response response) throws Throwable {
        ResponseBody body = response.body();
        if (body == null) return null;
        return body.string();
    }
}

上面的8个回调方法中,针对我们每次自定义的Callback抽象类都同时默认提供一种convertResponse方法的实现方式,在确保convertResponse方法解析正确的前提下,这样我们就只需考虑其他7个跟请求有关的方法,并且这些回调方法中的泛型可以直接替换成本次请求我们指定的泛型类型,而且在这7个方法中,我们必须回调的只有onsuccess方法,其余回调方法均以通过AbsCallback转成了非抽象方法,我们按需实现即可,例如StringCallback的某次请求代码如下:
在这里插入图片描述其中的onSuccess方法是必须要回调的,此处的onError方法是我们按需回调的,此时回调就简洁多了,不像之前上面使用JsonCallback那样一下子回调了8个方法。

按照以上的思路我们可以自定义一个JsonCallback抽象类,将网络返回的JSON数据自动解析成对象。

注意

对于我们定义的转换器类中convertResponse方法的解析逻辑,如果解析逻辑写的有问题无法正常转换得到我们指定的泛型对象,那么通过查看源码我们发现,此时同样会回调onError方法,虽然网络请求没有问题,但是解析转换有问题同样会回调onError方法,如果网络请求成功,并且解析转换逻辑正确那么就会回调onSuccess方法。
执行error方法有以下三种情况:
1,网络请求错误
2,网络请求正确,转换器类中解析转换逻辑错误
3,网络请求正确,转换器书写正确,能拿到正确返回的数据,但是是服务器端返回给我们的错误提示信息,比如用户在登录时的用户名不存在,密码错误等

自定义Callback的高级用法:

  1. DialogCallback
    我们经常需要在网络请求前显示一个loading,请求结束后取消loading,这是个重复的工作,我们完全可以自定义一个Callback,让这个Callback帮我们完成这个工作,那么我们就需要用到Callback中的两个回调方法,onStart()和onFinish(),详细源码如下:
    在这里插入图片描述
  2. EncriptCallback
    有时候我们需要在请求前,将我们的参数都加密或者签名,然而加密也是个重复的工作,我们没必要每次都写,也可以放在自定义的Callback中,源码如下:
    在这里插入图片描述在这里插入图片描述

okGo框架默认内置的四种Callback的使用

okGo框架默认已经帮我们定义了四种Callback的抽象类,其中AbsCallback类中方法均为空实现,只做我们自定义时继承的父类,除此之外没有其他作用,我们着重来看另外三个:StringCallback,BitmapCallback,FileCallback(着重讲解),这三个类都提供了一种对于convertResponse方法的解析实现,可以直接拿来用,下面分别来讲:

StringCallback

使用场景:服务器端返回的数据为字符串时,我们可以如下使用:
在这里插入图片描述然后在onSuccess方法中获取到返回的字符串值,然后如果是json字符串,则使用Gson解析等操作。

BitmapCallback

如果你知道你的请求数据是图片,可以使用这样的方法:
在这里插入图片描述

FileCallback:基本的文件下载功能,包括下载进度监听

如果你要下载文件,可以这么写。
FileCallback具有三个重载的构造方法,分别是

FileCallback():空参构造
FileCallback(String destFileName):可以额外指定文件下载完成后的文件名
FileCallback(String destFileDir, String destFileName):可以额外指定文件的下载目录和下载完成后的文件名

文件目录如果不指定,默认下载的目录为 sdcard/download/,文件名如果不指定,则按照以下规则命名:

1.首先检查用户是否传入了文件名,如果传入,将以用户传入的文件名命名
2.如果没有传入,那么将会检查服务端返回的响应头是否含有Content-Disposition=attachment;filename=FileName.txt该种形式的响应头,如果有,则按照该响应头中指定的文件名命名文件,如FileName.txt
3.如果上述响应头不存在,则检查下载的文件url,例如:http://image.baidu.com/abc.jpg,那么将会自动以abc.jpg命名文件
4.如果url也把文件名解析不出来,那么最终将以"unknownfile_" + System.currentTimeMillis()命名文件

在这里插入图片描述
这里面有个新对象叫Progress,他保存了当前进度的很多信息,源码中的定义如下,字段很多,所以如果你需要什么进度信息,直接就能取出来,如下:
在这里插入图片描述

okGo基本的上传功能

1,上传String类型的文本
一般此种用法用于与服务器约定的数据格式,当使用该方法时,params中的参数设置是无效的,所有参数均需要通过需要上传的文本中指定,此外,额外指定的header参数仍然保持有效。

默认会携带以下请求头

Content-Type: text/plain;charset=utf-8

如果你对请求头有自己的要求,可以使用这个重载的形式,传入自定义的content-type

// 比如上传xml数据,这里就可以自己指定请求头 
upString("这是要上传的长文本数据!", MediaType.parse("application/xml"))

在这里插入图片描述2,上传JSON类型的文本
该方法与upString没有本质区别,只是数据格式是json,一般来说,需要自己创建一个实体bean或者一个map,把需要的参数设置进去,然后通过三方的Gson或者fastjson转换成json对象,最后直接使用该方法提交到服务器。

默认会携带以下请求头,请不要手动修改,okgo也不支持自己修改
Content-Type: application/json;charset=utf-8
在这里插入图片描述3,上传文件
上传文件支持文件与参数一起同时上传,也支持一个key上传多个文件,以下方式可以任选

特别要注意的是
1). 很多人会说需要在上传文件到时候,要把Content-Type修改掉,变成multipart/form-data,就像下面这样的。其实在使用OkGo的时候,只要你添加了文件,这里的的Content-Type不需要你手动设置,OkGo自动添加该请求头,同时,OkGo也不允许你修改该请求头。

Content-Type: multipart/form-data; boundary=f6b76bad-0345-4337-b7d8-b362cb1f9949

2). 如果没有文件,那么OkGo将自动使用以下请求头,同样OkGo也不允许你修改该请求头。

Content-Type: application/x-www-form-urlencoded

3). 如果你的服务器希望你在没有文件的时候依然使用multipart/form-data请求,那么可以使用.isMultipart(true)这个方法强制修改,一般来说是不需要强制的。
在这里插入图片描述
有人说他有很多文件,不知道数量,又要一个文件对应一个key该怎么办,其实很简单,把调用链断开,用循环添加就行了嘛。
在这里插入图片描述4,取消请求
之前讲到,我们为每个请求前都设置了一个参数tag,取消就是通过这个tag来取消的。通常我们在Activity中做网络请求,当Activity销毁时要取消请求否则会发生内存泄露,那么就可以在onDestory()里面写如下代码:

注意以下取消的请求不要全部用,自己按需要写,我只是写出来,告诉你有这么几个方法。
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

安卓项目实战之强大的网络请求框架okGo使用详解(一):实现get,post基本网络请求,下载上传进度监听以及对Callback自定义的深入理解 的相关文章

  • 检测到设备正在振动?

    我使用下面的代码来振动设备 public void vibrator try Vibrator vibrator Vibrator getSystemService Context VIBRATOR SERVICE vibrator vib
  • Android - 如何一次只允许播放一个 MediaPlayer 实例?

    我正在尝试创建一个简单的 Sound board Android 应用程序 使用 ListView 项目作为按钮 顺便说一句 我是一个新手程序员 我的想法是 我按下一个按钮 就会播放一个特定的声音文件 如果我在播放声音时按下任何按钮 它应该
  • 导航组件重复 NavArgs 的问题

    我有一个片段 class SomeFragment private val args by navArgs
  • Android SoundPool 堆限制

    我正在使用 SoundPool 加载多个声音剪辑并播放它们 据我所知 它的功能 100 正确 但在 load 调用期间 我的日志中充斥着以下内容 06 09 11 30 26 110 ERROR AudioCache 23363 Heap
  • 在自定义对象中创建时粘性服务不会重新启动

    我有一个具有绑定服务的单例对象 我希望它重新启动 当我从启动器启动应用程序时 单例对象将初始化并绑定到这个现有的服务实例 以下是在单例中创建和绑定服务的代码 public class MyState private static MySta
  • 如何从 SQLite 获取记录总数

    我正在尝试从 Sqlite DB 获取行的总数 以下是我想要做的代码片段 我不知道我在这里做错了什么 public static int getTotalCount Context context Cursor c null try c g
  • Android Studio 在编译时未检测到支持库

    由于 Android Studio 将成为 Android 开发的默认 IDE 因此我决定将现有项目迁移到 Android studio 中 项目结构似乎不同 我的项目中的文件夹层次结构如下 Complete Project gt idea
  • Flutter 深度链接

    据Flutter官方介绍深层链接页面 https flutter dev docs development ui navigation deep linking 我们不需要任何插件或本机 Android iOS 代码来处理深层链接 但它并没
  • 从 android 简单上传到 S3

    我在网上搜索了从 android 上传简单文件到 s3 的方法 但找不到任何有效的方法 我认为这是因为缺乏具体步骤 1 https mobile awsblog com post Tx1V588RKX5XPQB TransferManage
  • 使用 Matrix.setPolyToPoly 选择位图上具有 4 个点的区域

    我正在 Android 上使用位图 在使用 4 个点选择位图上的区域时遇到问题 并非所有 4 点组都适合我 在某些情况下 结果只是一个空白位图 而不是裁剪后的位图 如图所示 并且 logcat 中没有任何错误 甚至是内存错误 这是我用来进行
  • Android构建apk:控制MANIFEST.MF

    Android 构建 APK 假设一个 apk 包含一个库 jar 例如 foo jar 该库具有 META INF MANIFEST MF 这对于它的运行很重要 但在APK中有一个包含签名数据的MANIFEST MF 并且lib jar
  • 调节麦克风录音音量

    我们正在尝试调整录音时的音量级别 麦克风似乎非常敏感 会接收到很多静电 我们查看了 setVolumeControlStream 但找不到传入其中来控制麦克风的流 将您的音频源设置为 MIC using MediaRecorder Audi
  • Android 设备上的静默安装

    我已经接受了一段时间了 在 Android 上静默安装应用程序是不可能的 也就是说 让程序安装捆绑为 APK 的应用程序 而不提供标准操作系统安装提示并完成应用程序安装程序活动 但现在我已经拿到了 Appbrain 快速网络安装程序的副本
  • 下载后从谷歌照片库检索图像

    我正在发起从图库中获取照片的意图 当我在图库中使用 Nexus 谷歌照片应用程序时 一切正常 但如果图像不在手机上 在 Google Photos 在线服务上 它会为我下载 选择图像后 我将图像发送到另一个活动进行裁剪 但在下载的情况下 发
  • 保护 APK 中的字符串

    我正在使用 Xamarin 的 Mono for Android 开发一个 Android 应用程序 我目前正在努力使用 Google Play API 添加应用内购买功能 为此 我需要从我的应用程序内向 Google 发送公共许可证密钥
  • Android 如何聚焦当前位置

    您好 我有一个 Android 应用程序 可以在谷歌地图上找到您的位置 但是当我启动该应用程序时 它从非洲开始 而不是在我当前的城市 国家 位置等 我已经在developer android com上检查了信息与位置问题有关 但问题仍然存在
  • 在webview android中加载本地html文件

    我正在尝试在 android 的 webview 中加载 html 文件的内容 但是 它给了我 网页不可用错误 如果我尝试使用谷歌或雅虎等网站 它们就会起作用 html文件位于src gt main gt assests gt index
  • 如何将图像从 Android 应用程序上传到网络服务器的特定文件夹中

    如何将图像从 android 移动到 Web 服务器上的指定文件夹 这是我的安卓代码 package com example bitmaptest import java io ByteArrayOutputStream import ja
  • 无法运行我的应用程序,要求选择 Android SDK

    今天我已经安装了Android Studio 金丝雀 1 现在我无法运行我的应用程序 将出现以下对话框 我已经通过 文件 gt 项目结构 gt Android SDK 位置 设置了正确的 SDK 位置 期待您的帮助来解决这个问题 警告对话框
  • 找到 Android 浏览器中使用的 webkit 版本?

    有没有办法知道某些特定手机上的 Android 浏览器使用的是哪个版本的 webkit 软件 如果有一个您可以浏览以获取该信息的 URL 那就太好了 但任何其他方式也很好 如果你知道 webkit 版本 你就知道 html5 支持多少 至少

随机推荐