安卓app、微信小程序访问webapi,将需要一时间,我们称之为耗时操作,其它诸如密集型计算、访问文件与设备等亦是如此。在这个期间我们应该跳出提示,告知用户正在等待,并且很多时候,在等待时不允许用户再对UI进行操作,直到耗时操作结束(无论是否成功,下同)
在安卓app中可以定义一个继承自Dialog的"等待对话框 WaItDialog",并且设置setCanceledOnTouchOutside(false),如此会设置一个"遮罩层",这样,当在耗时操作开始前开启(Show)WaItDialog直到结束关闭(dismiss),用户就不能点击UI了。
微信小程序可以直接用wx.showLoading与wx.hideLoading,并将mask设为true,实现类似功能。
当我们只有一个耗时操作时,问题很容易,但如果是多个呢?下面便讲解如何在进行多个耗时操作时,开关等待提示的问题。我们以访问api为例,假定以下场景,访问三个api,第一个get请求、第二个post请求、第三个故意访问一个不存在的api。当其中遇到错误时,应不能影响其它api的响应。所以我们需要先分别建立一个get请求的api与post请求的api。你可以任意语言来实现接口,我这里用传统的asp.net frameworks(visault studio 2019有asp.net core,可以跨平台,而frameworks只能在windows部署,请注意区分) 的一般处理程序(ashx)来实现。
第一个get请求的api
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WebApi
{
/// <summary>
/// HellloHandler 的摘要说明
/// </summary>
public class HellloHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
if ( context.Request.QueryString["name"]!=null)
{
context.Response.Write("你好"+ context.Request.QueryString["name"].ToString());
}
else
{
context.Response.Write("Hello World");
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
第二个post请求api
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WebApi
{
///
/// PostHandler 的摘要说明
///
public class PostHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
// context.Response.Write("Hello World");
if (context.Request.HttpMethod != "POST")
{
context.Response.Write("非法请求");
return;
}
string name = context.Request.Form["name"];
if (string.IsNullOrEmpty(name))
{
context.Response.Write(" hello Post");
}
else
{
context.Response.Write("post:"+name);
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
接下来,就可以实现微信小程序、安卓app的访问了。
微信小程序,是单线程的,实现较为简单,因为我们要访问三个api,所以总数设为3,然后定义m=0 当wx.request complete时m+1,当m>=3时表示,所以三个api访问结束,调用 wx.hideLoading。注意在调试阶段,请勾选不校验合法域名选项。
xml
wxml代码
<!--pages/index/index.wxml-->
<view class="container">
<view>{{view1}}</view>
<view>{{view2}}</view>
<view>{{view3}}</view>
</view>
js代码,注意将接口地址替换为你自己实际的地址
javascript
Page({
/**
* 页面的初始数据
*/
data: {
view1: '',
view2: '',
view3: '',
errors: [] // 用于存储错误信息
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.getMultipleApis();
},
completeCount: 3,
m:0,
gethelloapi() //get请求
{
var that=this;
wx.request({
url: 'http://192.168.2.102:59597/HellloHandler.ashx?name=Jim',
success:function(res)
{
var data=""
if (res.statusCode==200)
{
data=res.data
}
else
{
data="http错误:"+res.statusCode
}
console.log("get"+data)
that.setData(
{
view1:data,
},
)
}
,
fail:function(e)
{
that.setData
(
{
view1:e.message
}
)
},
complete:function()
{
that.m++;
if (that.m>=that.completeCount)
{
wx.hideLoading();
}
}
},
)
},
getpostoapi() //post请求
{
var that=this;
wx.request({
url: 'http://192.168.2.102:59597/PostHandler.ashx',
method:"POST",
header: {
'content-type': 'application/x-www-form-urlencoded' // 关键设置
},
data:{
name:'WangHua',
},
success:function(res)
{
var data=""
if (res.statusCode==200)
{
data=res.data
console.log("接收"+data)
}
else
{
data="http错误:"+res.statusCode
}
that.setData(
{
view2:data,
},
console.log("应答"+res.statusCode)
)
}
,
fail:function(e)
{
that.setData
(
{
view2:e.message
}
)
},
complete:function()
{
that.m++;
if (that.m>=that.completeCount)
{
wx.hideLoading();
}
}
},
)
},
getnoexistapi() //不存在的请求
{
var that=this;
wx.request({
url: 'http://192.168.2.102:59597/NoExistHandler.ashx?name=Lucy',
success:function(res)
{
var data=""
if (res.statusCode==200)
{
data=res.data
}
else
{
data="http错误:"+res.statusCode
}
that.setData(
{
view3:data,
},
console.log("应答"+res.statusCode)
)
}
,
fail:function(e)
{
that.setData
(
{
view3:e.message
}
)
},
complete:function()
{
that.m++;
if (that.m>=that.completeCount)
{
wx.hideLoading();
}
}
},
)
},
// 处理多个不同类型的API请求
getMultipleApis() {
// 显示加载提示
wx.showLoading({
title: '加载中...',
mask: true
});
this.gethelloapi();
this.getpostoapi();
this.getnoexistapi();
}
})
接下来安卓app代码,安卓不允许在主线程,要用分线程来调用,分线程又不能操控UI,另外分线程还有考虑共享资源的安全访问,所以情况要比微信小程序复杂多,在编写代码前我们要做一些准备设置。
第一步要在AndroidManifest添加相关网络权限。
并且因为是调试下进行,还要application节点下,设置android:usesCleartextTraffic="true"(默认网络访问必须https,ftps等有ssl的接口,设置此选项后可以解除限定)。
第二步,在Build.gradle引入okthhp(用来访问网络)、我封装好的WaitDialog.aar的包以及Glide (我在WaitDialog引用了Glide ,一个图片加载库)。
做完准备设置,就是代码了
布局文件
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="get api"
android:id="@+id/BtnApi"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/TV1"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/TV2"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/TV3"/>
</LinearLayout>
Java代码,这里有两个要注意的地方,前面也提及过。一是分线程不能操作UI,要在runOnUiThread中操作,二是,分线程安全访问共享资源的问题,我们不能直接像微信小程序一样m++来增加"计数器"。我们可以通过AtomicInteger类实现安全访问共享资源,实现类似m++的功能,以上两点代码都会体现。
package person.yin.mutiapi;
java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.json.JSONObject;
import java.io.IOException;
import java.io.StringWriter;
import java.util.concurrent.atomic.AtomicInteger;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.CookieJar;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import swaddle.yinzhenwei.waitdialog.WaitDialog;
public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
private WaitDialog waitDialog;
private Button btnapi;
private String TAG="APITest";
private TextView tv1;
private TextView tv2;
private TextView tv3;
private AtomicInteger requestCounter = new AtomicInteger(0);
private static final int apicount=3; //最大三个api访问
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv1=findViewById(R.id.TV1);
tv2=findViewById(R.id.TV2);
tv3=findViewById(R.id.TV3);
findViewById(R.id.BtnApi).setOnClickListener(this);
}
//***************
private void test()
{
waitDialog=new WaitDialog(this);
waitDialog.setText("查询中,稍候");
waitDialog.show();
waitDialog.show();
requestCounter.set(0);
gethelloapi("Bill",tv1);
getpostapi("Bob",tv2);
getnoexistapi("Liu",tv3);
}
private void gethelloapi(String name,TextView tv) //访问helloapi get请求
{
OkHttpClient client = new OkHttpClient.Builder()
.build();
RequestBody rbody = new FormBody.Builder()
.add("username", "admin")
.add("password", "12345")
.build();
Request request = new Request.Builder()
.url("http://192.168.2.102:59597/HellloHandler.ashx?name="+name)
.post(rbody)
// .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36")
// .header("Accept", "text/plain") // 匹配服务器响应类型
// .header("Connection", "close") // 避免长连接问题
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "请求失败: " + e);
// e.printStackTrace();
// 获取完整的堆栈信息
StringWriter sw = new StringWriter();
//e.printStackTrace(new PrintWriter(sw));
Log.e(TAG, "完整异常信息: " + sw.toString());
runOnUiThread(() ->
{
tv.setText("网络出现错误: " + e.getMessage());
requestCounter.incrementAndGet();
checkAllRequestsCompleted();
}
);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 打印完整的响应信息用于调试
Log.d(TAG, "收到响应: " + response.code());
if (!response.isSuccessful()) {
String errorBody = response.body() != null ? response.body().string() : "无响应体";
Log.e(TAG, "HTTP错误 " + response.code() + ": " + errorBody);
runOnUiThread(() ->
{
tv.setText("HTTP错误: " + response.code() );
requestCounter.incrementAndGet();
checkAllRequestsCompleted();
}
);
return;
}
String responseData = response.body().string();
Log.d(TAG, responseData);
runOnUiThread(() ->
{
//textViewResult.setText( responseData);
tv.setText(responseData);
requestCounter.incrementAndGet();
checkAllRequestsCompleted();
}
);
}
});
}
private void getnoexistapi(String name,TextView tv) //故意访问一个不存在api
{
OkHttpClient client = new OkHttpClient.Builder()
.build();
RequestBody rbody = new FormBody.Builder()
.add("username", "admin")
.add("password", "12345")
.build();
Request request = new Request.Builder()
.url("http://192.168.2.102:59597/NoExistHandler.ashx?name="+name)
.post(rbody)
// .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36")
// .header("Accept", "text/plain") // 匹配服务器响应类型
// .header("Connection", "close") // 避免长连接问题
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "请求失败: " + e);
// e.printStackTrace();
// 获取完整的堆栈信息
StringWriter sw = new StringWriter();
//e.printStackTrace(new PrintWriter(sw));
Log.e(TAG, "完整异常信息: " + sw.toString());
runOnUiThread(() ->
{
tv.setText("网络出现错误: " + e.getMessage());
requestCounter.incrementAndGet();
checkAllRequestsCompleted();
}
);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 打印完整的响应信息用于调试
Log.d(TAG, "收到响应: " + response.code());
Log.v(TAG,"ok");
if (!response.isSuccessful()) {
String errorBody = response.body() != null ? response.body().string() : "无响应体";
Log.e(TAG, "HTTP错误 " + response.code() + ": " + errorBody);
runOnUiThread(() ->
{
tv.setText("HTTP错误: " + response.code() );
requestCounter.incrementAndGet();
checkAllRequestsCompleted();
}
);
return;
}
String responseData = response.body().string();
Log.d(TAG, responseData);
runOnUiThread(() ->
{
//textViewResult.setText( responseData);
tv.setText(responseData);
requestCounter.incrementAndGet();
checkAllRequestsCompleted();
}
);
}
});
}
private void getpostapi(String name, TextView tv) //访问postapi post请求
{
// 创建带调试信息的OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.build();
RequestBody rbody = new FormBody.Builder()
.add("name", name)
.build();
Request request = new Request.Builder()
.url("http://192.168.2.102:59597/PostHandler.ashx")
.post(rbody)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "请求失败: " + e);
// e.printStackTrace();
// 获取完整的堆栈信息
StringWriter sw = new StringWriter();
//e.printStackTrace(new PrintWriter(sw));
Log.e(TAG, "完整异常信息: " + sw.toString());
runOnUiThread(() ->
{
tv.setText("网络出现错误: " + e.getMessage());
requestCounter.incrementAndGet();
checkAllRequestsCompleted();
}
);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 打印完整的响应信息用于调试
Log.d(TAG, "收到响应: " + response.code());
if (!response.isSuccessful()) {
String errorBody = response.body() != null ? response.body().string() : "无响应体";
Log.e(TAG, "HTTP错误 " + response.code() + ": " + errorBody);
runOnUiThread(() ->
{
tv.setText("HTTP错误: " + response.code() );
requestCounter.incrementAndGet();
checkAllRequestsCompleted();
}
);
return;
}
String responseData = response.body().string();
Log.d(TAG, responseData);
runOnUiThread(() ->
{
tv.setText( responseData);
requestCounter.incrementAndGet();
checkAllRequestsCompleted();
}
);
}
});
}
private void checkAllRequestsCompleted() {
if (requestCounter.get() >= apicount) {
//runOnUiThread(this::dismissLoading);
//runOnUiThread(()->waitDialog.dismiss());
waitDialog.dismiss();
}
}
@Override
public void onClick(View v)
{
switch (v.getId())
{
case R.id.BtnApi:
test();
break;
}
}
}
至此所有代码均已完成,我打包了微信小程序和安卓app的代码,因为api接口可以用多种语言实现,就不打包了,如果需要复制黏贴吧。代码地址 https://download.csdn.net/download/wstcl/91725186?spm=1001.2014.3001.5503