安卓app、微信小程序等访问多个api时等待提示调用与关闭问题

安卓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

相关推荐
没有了遇见32 分钟前
Kotlin高级用法之<扩展函数/属性>
android·kotlin
安卓开发者35 分钟前
Android中使用RxJava实现网络请求与缓存策略
android·网络·rxjava
2501_915921431 小时前
iOS 应用上架多环境实战,Windows、Linux 与 Mac 的不同路径
android·ios·小程序·https·uni-app·iphone·webview
ggtc4 小时前
为workflow-core扩展外抛事件
c#·webapi·workflow-core
louisgeek4 小时前
Android Studio 打印中文乱码
android
眼镜会飞5 小时前
Flutter 3.x新版android端的build.gradle.kts文件配置arm64-v8a和armeabi-v7a等
android·前端·flutter
vocal5 小时前
【我的安卓第一课】Activity 的伙伴 Fragment
android
咸虾米5 小时前
微信小程序通过uni.chooseLocation打开地图选择位置,相关设置及可能出现的问题
vue.js·微信小程序
AAA修煤气灶刘哥6 小时前
微信小程序+Spring Boot:三步教你搞定微信小程序登录+Token加密+全局拦截器
spring boot·后端·微信小程序