androidandroid 网络请求求有哪些

我们知道大多数的 Android 应用程序都是通过和服务器进行交互来获取数据的。如果使用 HTTP 协议来发送和接收网络数据,就免不了使用 HttpURLConnection 和 HttpClient,而 Android 中主要提供了上述两种方式来进行 HTTP 操作。并且这两种方式都支持 HTTPS 协议、以流的形式进行上传和下载、配置超时时间、IPv6、以及连接池等功能。
但是 Googl e发布 6.0 版本的时候声明原生剔除 HttpClient,但是笔者认为 HttpClient 会提供相应的 jar 包做支持,毕竟 Google 对向下兼容这方面一直都做的很好,相信在选择网络功能的时候我们会选自己喜欢的方法。
HttpURLConnection
接着我们来看一下如何使用 HttpURLConnection 来处理简单的网络请求。
// Get方式请求
public static void requestByGet() throws Exception {
String path = &10.128.7.34:3000/name=helloworld&password=android&;
// 新建一个URL对象
URL url = new URL(path);
// 打开一个HttpURLConnection连接
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
// 设置连接超时时间
urlConn.setConnectTimeout(6 * 1000);
// 开始连接
urlConn.connect();
// 判断请求是否成功
if (urlConn.getResponseCode() == HTTP_200) {
// 获取返回的数据
byte[] data = readStream(urlConn.getInputStream());
Log.i(TAG_GET, new String(data, &UTF-8&));
Log.i(TAG_GET, &Get方式请求失败&);
// 关闭连接
urlConn.disconnect();
// Post方式请求
public static void requestByPost() throws Throwable {
String path = &http://10.128.7.34:3000/login&;
// 请求的参数转换为byte数组
String params = &name+ URLEncoder.encode(&helloworld&, &UTF-8&)
+ &&password=& + URLEncoder.encode(&android&, &UTF-8&);
byte[] postData = params.getBytes();
// 新建一个URL对象
URL url = new URL(path);
// 打开一个HttpURLConnection连接
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
// 设置连接超时时间
urlConn.setConnectTimeout(5 * 1000);
// Post请求必须设置允许输出
urlConn.setDoOutput(true);
// Post请求不能使用缓存
urlConn.setUseCaches(false);
// 设置为Post请求
urlConn.setRequestMethod(&POST&);
urlConn.setInstanceFollowRedirects(true);
// 配置请求Content-Type
urlConn.setRequestProperty(&Content-Type&,
&application/x-www-form-urlencode&);
// 开始连接
urlConn.connect();
// 发送请求参数
DataOutputStream dos = new DataOutputStream(urlConn.getOutputStream());
dos.write(postData);
dos.flush();
dos.close();
// 判断请求是否成功
if (urlConn.getResponseCode() == HTTP_200) {
// 获取返回的数据
byte[] data = readStream(urlConn.getInputStream());
Log.i(TAG_POST, new String(data, &UTF-8&));
Log.i(TAG_POST, &Post方式请求失败&);
关于HttpURLConnection的相关开源工程
由于 Google 已经表明了推荐广大开发者使用 HttpURLConnection,那么想必是有一定原因的。
这里就推荐大伙都很熟悉的开源项目 xUtils 的“弟弟“,xUtils3。xUtils 包含了 view 注入,图片处理,数据库操作以及网络请求封装,xUtils 使用的网络请求封装是基于 HttpClient 的。xUtils3 使用的网络请求是基于 HttpURLConnection,我们着重说明一下xUtils3。
RequestParams params = new RequestParams(&http://10.128.7.34:3000/upload&);
// 加到url里的参数, http://xxxx/s?wd=xUtils
params.addQueryStringParameter(&wd&, &xUtils&);
// 添加到请求 body 体的参数, 只有 POST, PUT, PATCH, DELETE 请求支持.
// params.addBodyParameter(&wd&, &xUtils&);
// 使用 multipart 表单上传文件
params.setMultipart(true);
params.addBodyParameter(
new File(&/sdcard/test.jpg&),
null); // 如果文件没有扩展名, 最好设置contentType 参数.
params.addBodyParameter(
new FileInputStream(new File(&/sdcard/test2.jpg&)),
&image/jpeg&,
// 测试中文文件名
&你+& \& 好.jpg&); // InputStream 参数获取不到文件名, 最好设置, 除非服务端不关心这个参数.
x.http().post(params, new Callback.CommonCallback&String&() {
public void onSuccess(String result) {
Toast.makeText(x.app(), result, Toast.LENGTH_LONG).show();
public void onError(Throwable ex, boolean isOnCallback) {
Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
public void onCancelled(CancelledException cex) {
Toast.makeText(x.app(), &cancelled&, Toast.LENGTH_LONG).show();
public void onFinished() {
具有 cache 功能
RequestParams params = new RequestParams(&http://10.128.7.34:3000/upload&);
params.wd = &xUtils&;
// 默认缓存存活时间, 单位:毫秒.(如果服务没有返回有效的max-age 或 Expires)
params.setCacheMaxAge(1000 * 60);
Callback.Cancelable cancelable
// 使用 CacheCallback, xUtils 将为该请求缓存数据.
= x.http().get(params, new Callback.CacheCallback&String&() {
private boolean hasError =
private String result =
public boolean onCache(String result) {
// 得到缓存数据, 缓存过期后不会进入这个方法.
// 如果服务端没有返回过期时间, 参考params.setCacheMaxAge(maxAge)方法.
// * 客户端会根据服务端返回的 header 中 max-age 或 expires 来确定本地缓存是否给 onCache 方法.
如果服务端没有返回 max-age 或 expires, 那么缓存将一直保存, 除非这里自己定义了返回false的
逻辑, 那么xUtils将请求新数据, 来覆盖它.
// * 如果信任该缓存返回 true, 将不再请求网络;
返回 false 继续请求网络, 但会在请求头中加上ETag, Last-Modified等信息,
如果服务端返回 304, 则表示数据没有更新, 不继续加载数据.
this.result =
// true: 信任缓存数据, 不在发起网络请求; false 不信任缓存数据.
public void onSuccess(String result) {
// 注意: 如果服务返回 304 或 onCache 选择了信任缓存, 这里将不会被调用,
// 但是 onFinished 总会被调用.
this.result =
public void onError(Throwable ex, boolean isOnCallback) {
hasError =
Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
if (ex instanceof HttpException) { // 网络错误
HttpException httpEx = (HttpException)
int responseCode = httpEx.getCode();
String responseMsg = httpEx.getMessage();
String errorResult = httpEx.getResult();
} else { // 其他错误
public void onCancelled(CancelledException cex) {
Toast.makeText(x.app(), &cancelled&, Toast.LENGTH_LONG).show();
public void onFinished() {
if (!hasError && result != null) {
// 成功获取数据
Toast.makeText(x.app(), result, Toast.LENGTH_LONG).show();
post请求直接更改 post 方式,以及需要提交的参数即可,篇幅问题这里就不在一一列举了。通过以上代码可以看到,我们在使用 xUtils 来请求网络的时候会非常的方便,只用在回调函数里面做对应的代码逻辑处理即可,不用关心线程问题,极大的解放了我们的生产力。
Android Stuido Gradle使用方法如下:
&compile 'org.xutils:xutils:3.1.+'
在 Android 2.3 及以上版本,使用的是 HttpURLConnection,而在Android 2.2 及以下版本,使用的是 HttpClient。鉴于现在的手机行业发展速度,我们已经不考虑 Android2.2 了。
简单提供一些 Volley 的实例:
//简单的 GET 请求
mQueue = Volley.newRequestQueue(getApplicationContext());
mQueue.add(new JsonObjectRequest(Method.GET, url, null,
new Listener() {
public void onResponse(JSONObject response) {
Log.d(TAG, &response : & + response.toString());
}, null));
mQueue.start();
//对 ImageView 的操作
ImageListener listener = ImageLoader.getImageListener(imageView, android.R.drawable.ic_launcher, android.R.drawable.ic_launcher);
mImageLoader.get(url, listener);
//对 ImageView 网络加载的处理
mImageView.setImageUrl(url, imageLoader);
当然我们也可以定制自己的 request
mRequestQueue.add( new GsonRequest(url, ListResponse.class, null,
new Listener() {
public void onResponse(ListResponse response) {
appendItemsToList(response.item);
notifyDataSetChanged();
HttpClient
同样,我们来看一下 HttpClient 的简单请求。
// HttpGet 方式请求
public static void requestByHttpGet() throws Exception {
String path = &http://10.128.7.34:3000/name=helloworld&password=android&;
// 新建 HttpGet 对象
HttpGet httpGet = new HttpGet(path);
// 获取 HttpClient 对象
HttpClient httpClient = new DefaultHttpClient();
// 获取 HttpResponse 实例
HttpResponse httpResp = httpClient.execute(httpGet);
// 判断是够请求成功
if (httpResp.getStatusLine().getStatusCode() == HTTP_200) {
// 获取返回的数据
String result = EntityUtils.toString(httpResp.getEntity(), &UTF-8&);
Log.i(TAG_HTTPGET, &HttpGet 方式请求成功,返回数据如下:&);
Log.i(TAG_HTTPGET, result);
Log.i(TAG_HTTPGET, &HttpGet 方式请求失败&);
// HttpPost 方式请求
public static void requestByHttpPost() throws Exception {
String path = &https://reg.163.com/login&;
// 新建 HttpPost 对象
HttpPost httpPost = new HttpPost(path);
// Post 参数
List&NameValuePair& params = new ArrayList&NameValuePair&();
params.add(new BasicNameValuePair(&name&, &helloworld&));
params.add(new BasicNameValuePair(&password&, &android&));
// 设置字符集
HttpEntity entity = new UrlEncodedFormEntity(params, HTTP.UTF_8);
// 设置参数实体
httpPost.setEntity(entity);
// 获取 HttpClient 对象
HttpClient httpClient = new DefaultHttpClient();
// 获取 HttpResponse 实例
HttpResponse httpResp = httpClient.execute(httpPost);
// 判断是够请求成功
if (httpResp.getStatusLine().getStatusCode() == HTTP_200) {
// 获取返回的数据
String result = EntityUtils.toString(httpResp.getEntity(), &UTF-8&);
Log.i(TAG_HTTPGET, &HttpPost 方式请求成功,返回数据如下:&);
Log.i(TAG_HTTPGET, result);
Log.i(TAG_HTTPGET, &HttpPost 方式请求失败&);
关于 HttpClinet 的相关开源工程
HttpClient 也拥有这大量优秀的开源工程,afinal、xUtils 以及AsyncHttpClient,也可以为广大开发者提供已经造好的轮子。由于xUtils 是基于 afinal 重写的,现在 xUtils3 也有替代 xUtils 的趋势,所以我们在这就简单介绍一下 AsyncHttpClient。
AsyncHttpClient
见名知意,AsyncHttpClient 对处理异步 Http 请求相当擅长,并通过匿名内部类处理回调结果,Http 异步请求均位于非 UI 线程,不会阻塞 UI 操作,通过线程池处理并发请求处理文件上传、下载、响应结果自动打包 JSON 格式。使用起来会很方便。
AsyncHttpClient client = new AsyncHttpClient();
//当然这里也可以换成 new JsonHttpResponseHandler(),我们就能直接获得 JSON 数据了。
client.get(&http://www.google.com&, new AsyncHttpResponseHandler() {
public void onStart() {
// called before request is started
public void onSuccess(int statusCode, Header[] headers, byte[] response) {
// called when response HTTP status is &200 OK&
public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
// called when response HTTP status is &4XX& (eg. 401, 403, 404)
public void onRetry(int retryNo) {
// called when request is retried
//POST 请求
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
params.put(&key&, &value&);
params.put(&more&, &data&);
//同上,这里一样可以改成处理 JSON 数据的方法
client.get(&http://www.google.com&, params, new
TextHttpResponseHandler() {
public void onSuccess(int statusCode, Header[] headers, String response) {
System.out.println(response);
public void onFailure(int statusCode, Header[] headers, String responseBody, Throwable error) {
Log.d(&ERROR&, error);
经过上面的代码发现,AsyncHttpClient 使用起来也是异常简洁,主要靠回调方法来处理成功或失败之后的逻辑。仔细想想,xUtils 的处理方式和这个处理方式很类似,看来好用设计还是很受人青睐的。
如果两种网络请求都想使用怎么办?那么 OkHttp 是一个最佳解决方案了。
OkHttp 在网络请求方面的口碑很好,就连 Google 自己也有使用OkHttp。虽然 Google6.0 中剔除了 HttpClient 的 Api,但是由于OkHttp 的影响力以及其强大的功能,使用 OkHttp 无需重写您程序中的网络代码。同时最重要的一点 OkHttp 实现了几乎和java.net.HttpURLConnection 一样的 API。如果您用了 Apache HttpClient,则 OkHttp 也提供了一个对应的 okhttp-apache 模块。足以说明 OkHttp 的强大,下面是一些例子。
一般的 get 请求
一般的 post 请求
基于 Http 的文件上传
支持请求回调,直接返回对象、对象集合
支持 session 的保持
简单代码实例
//GET 请求
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
Response response = client.newCall(request).execute();
return response.body().string();
//POST 请求
public static final MediaType JSON
= MediaType.parse(&application/ charset=utf-8&);
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.post(body)
Response response = client.newCall(request).execute();
return response.body().string();
Android Studio Gradle 使用方式:
compile 'com.squareup.okhttp:okhttp:2.7.0'
Android 开发应用少不了使用网络,移动互联时代,抢占终端入口变得异常重要,那么我们在开发过程中,不管使用哪一种网络请求,HttpURLConnection 或者是 HttpClient,都可以满足我们和服务器的沟通。
可是发布的 App 到用户手中后,有用 WIFI 的,有用流量的,网络环境多样,我们怎么能知道用户在什么样的情况下访问服务器的流畅度呢?
答案很简单,只要集成了,即可轻松知晓网络交互情况,轻松了解用户在使用App的过程中哪里容易出问题,并对症下药。
,监控网络请求及网络错误,提升用户留存。访问 感受更多体验,想阅读更多技术文章,请访问 OneAPM 。
阅读(...) 评论()
OneAPM - 端到端的云解决方案!他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)Android的网络请求框架 - 简书
Android的网络请求框架
现在很多的app都是依赖网络服务的应用。跟服务器通讯我们就要用到网络请求。网络请求的框架有很多:原生自己封装的,Volley,OKHttp,Spring for android,Retrofit...等等。当然今天不讨论这些框架的使用或者好处,而是讨论网络服务的app的框架要如何搭建。
整体的流程大致是这样:
用户操作-&业务处理-&网络请求-&数据解析-&业务处理-&界面改变。
如果分层的话,大致是:
视图层 -& 业务处理层 -& 网络请求层
那咱们还得弄清楚一些原则:
既然分层了,咱们首先要搞清楚每一层该做什么事情。
视图层: 跟用户交互以及界面展示方面的,例如请求弹出个进度框,请求成功或者失败要通知用户,界面刷新展示,用户体验就靠这层了。
业务处理层:请求数据装载 (例如:登录 Basic Auth,视图层提供输入框里面的用户名和密码, 这里转化成服务器识别的参数要求),业务处理,得到网络请求返回的数据处理 (例如:缓存, 校验等)
**网络请求层: **就是调用各个框架请求网络,负责和服务器交互
那检查手机网络应该放在那一层呢?我这里就不说了。
表层可以依赖底层,底层不能依赖表层。
不要跨层依赖。
层与层的依赖关系要尽量减少耦合
这个得自己用其他框架实现,依赖注入,代理,事件总线,Base基类等等,今天就不讨论这个。
讲到这,其实咱们的框架是这样的:
1~6的过程中,可能有些步骤是耗时,且在不同的线程中执行。但是他们的步奏是一定是1-6顺序执行完。
当然上面是顺利的流程,当然也有可能出现其他的情况,1~6的过程中取消任务或者出现异常。
ok,那我们下面来讨论下不正常的情况
执行过程取消任务,我们的框架应该是支持任一过程中取消任务执行。
取消任务触发条件可能是用户操作或者页面退出等,那任务就没有必要执行下去,就要取消。达到的效果就是任一点取消任务,后面的过程将不会执行,这里的3,4可能是线程执行的过程,如果有可能可以中断线程。
每一个过程都要可能产生异常,异常处理原则就交给有能力处理的层去处理,例如网络异常一般都是抛到UI层,然后提示用户。
讨论那么多,那代码如何实现呢?我这边主要讲一下我的思路,如果有其他更好的方法,请告知。
以前的思想是回调的方式,即底层执行的时候,需要调用者提供handler,就是实现回调接口。
现在的思路是,就像滚雪球一样,底层给出核心的Excutor对象,往上抛,每经过一层,包上这一层的业务逻辑,由最外层执行操作。好处我觉得可扩展,代码优雅,低耦合。
网络请求部分:
我用的是 Retrofit + Rxjava,用OkHttp的Interceptor实现网络请求的切面编程,例如前面提到的Basic Auth等。
public interface XXXDao {
@GET("http://120.155.93.85:8080/rs/{name}")
Observable&List&Canteen&& getCanteen(@Path("name") String name, @Query("data") String date);
@GET("http://120.55.93.85:8080/rs/canteens")
Observable&retrofit2.Response&List&Canteen&&& getCanteen1();
比较简单吧,过程被框架封装了,这个咱们不管它,主要盯着它的返回,第一个小雪球: Observable&??&,它后面需要2个处理数据,Acton1&??&,Action&Throwable& 看名字就知道一个是处理异常的,一个是处理数据的。不懂的朋友看Rxjava.
正常和异常都处理了,那如何在这一层取消任务呢?
这里用的是Interceptor来拦截掉你要取消的任务。这里使用前后2个拦截动作,如下图:
思路有了,如何代码实现呢?先看看拦截需要我们实现的代码
public class XXXInterceptor implements Interceptor {
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = judgeRequest(request);
if(response == null){
response = chain.proceed(request);
if(Response temp = judgeRequest(request) != null){
response =
return response
private Response judgeRequest(Request request) {
if(request 是否被取消){
return 异常的R
拦截器是可以实现我们的效果,但是因为retrofit是支持取消任务的。上面的代码要做一丢丢的改变
call.cancel();
public interface XXXDao {
@GET("http://120.155.93.85:8080/rs/{name}")
Call&List&Canteen&& getCanteen(@Path("name") String name, @Query("data") String date);
@GET("http://120.55.93.85:8080/rs/canteens")
Call&retrofit2.Response&List&Canteen&&& getCanteen1();
然后像上层抛出Call对象,当取消任务的时候,就调用cancel的方法,不过我这里没有对3,4做取消处理,因为我比较懒,闲麻烦。所以网络请求返回的还是Observable&??&对象。
业务处理部分:
小雪球拿到,我们就要在包装一下,像三明治一样,用2片面包包起来,其实就是前面做什么&后面做什么。
public interface AppDao {
RestExcuter&List&Canteen&& getCanteen(String path, String date);
RestExcuter&Result&List&Canteen&&& getCanteen1();
我用RestExcuter来包装了Observable&??&,代码如下:
private RestExcuter(@NonNull Observable&Bo& observable) {
if (observable == null) {
throw new NullPointerException("Observable null !");
this.observable =
然后提供了一些给上层调用的接口。
errorDoing是交给视图层错误时候该做什么。
postDoing是交给视图层成功了做什么。
handler是交给视图层做动画处理的,例如某一个App请求网络的时候,是一个Gif图播放。
public interface RestHandler&RestBo& {
* 前面做什么
void pre();
* 完成做什么
* @param bo
void post(RestBo bo);
* 异常做什么
* @param throwable
void error(Throwable throwable);
下面是如何在这一层取消请求呢?
//任务是否取消
AtomicBoolean isC
boolean isCancel(){
boolean result = isCancel.get();
if (result) {
ZLog.i("任务已经被取消!");
if (errorDoing != null) {
errorDoing.call(new RestCancelException());
private void removeExcuter(){
if(container != null){
container.removeRestExcuter(this);
container =
/** * 取消任务 */
public void cancel(){
isCancel.compareAndSet(false,true);
* 请求的容器,例如activity在它销毁时候 取消这个容器下的所有请求
public interface RestContainer {
void addRestExcuter(RestExcuter&?& excuter);
void cancelAllRestExcuter();
void removeRestExcuter(RestExcuter&?& excuter);
isCancel是控制这个任务是否被取消。
视图层相对简单点,就是调用业务层,干自己改做的事情。代码如下:
public void simpleClick(View view) {
appDao.getCanteen("canteens", "")
.setContainer(this)
.handler(new DialogHandler&List&Canteen&&(this,"网络请求","正在请求网络,请稍等..."))
.error(throwable -& binding.setInfo("请求出错啦"+throwable.getMessage()))
.post(body -& setCanteenInfo(body))
.excute();
好吧,就介绍到这。
Android : Kotlin语言
IOS : Swift 3.0
Java,Object-c,Kotlin,Swift
用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金Cover 有什么料? 从这篇文章中你能获得这些料: 知道setContentView()之后发生了什么? ... Android 获取 View 宽高的常用正确方式,避免为零 - 掘金相信有很多朋友...
用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你能获得这些料: 知道setContentView()之后发生了什么? ... Android 获取 View 宽高的常用正确方式,避免为零 - 掘金 相信有很多...
百战程序员_ Java1573题 QQ群:034603 掌握80%年薪20万掌握50%年薪10万 全程项目穿插, 从易到难,含17个项目视频和资料持续更新,请关注www.itbaizhan.com 国内最牛七星级团队马士兵、高淇等11位十年开发经验专...
AFHTTPRequestOperationManager 网络传输协议UDP、TCP、Http、Socket、XMPP 1、应用层http?iOS1发信息给服务器,iOS2如果想要知道iOS1发的信息是什么,只有iOS2客户端向服务器发送请求,服务器才会将服务器的数据返回...
Java 基础思维导图,让 Java 不再难懂 - 工具资源 - 掘金思维导图的好处 最近看了一些文章的思维导图,发现思维导图真是个强大的工具。了解了思维导图的作用之后,觉得把它运用到java上应该是个不错的想法,这样回顾知识点的时候一目了然,快速知道自己的短板。 思维导图...
今早起来时看到一句话:在大部分情境下,我们都有一个毛病,那就是“一厢情愿”。只要我觉得对方需要这个,那他就必须要接受,否则就是对我的辜负。 对于一厢情愿这个问题,我思考了好久,先来说个例子吧。 夹菜。在中国大部分家庭中,都有个习俗,那就是夹菜,先不说卫生问题,就那种被强迫的...
嘿,朋友,今年30了吧? 十年前那个短发女孩常常学人家喊着“买车买房,妻妾成群”,期望着有一天能在事业上叱诧风云,为了理想在拼搏。喊完又嫌弃自己俗,俗不可耐。亦或常常喊着未来一定要嫁给爱情。而今,近况如何?你茕茕孑立,形影相吊,为了工作过着朝五晚九的生活?或是遇到一个合拍的...
有自己的全面综合能力,有自己的特长。考虑以后的发展!
今天因为见某个人来到杭州,下着大雨,这个人是朋友介绍给我的朋友,只见他本人很瘦弱。一见面聊天聊到他父母都已经不在了,此时我才知道自己有多幸福,他有多坚强。不过我看他第一眼,确实喜欢不上他,可能我是外貌协会吧。毕竟163的个子,又瘦又弱。 此时我一个人住在一个名宿酒店里面,中...
老人常说春捂秋冻,动人不动水, 虽然感受到春天的气息,可身体的关节却不争气,总是莫名的刺痛,特别是有关节病的朋友更是如此!关节疼痛是非常遭罪和痛苦的疾病,很多人每年都要忍受,而在古中医中,关节问题实际上都与寒湿有关,到底关节问题在古中医上有何方法? 张仲景《伤寒论》方剂:甘...

我要回帖

更多关于 android 取消网络请求 的文章

 

随机推荐