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

相关推荐
selt7919 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
Yao_YongChao9 小时前
Android MVI处理副作用(Side Effect)
android·mvi·mvi副作用
非凡ghost10 小时前
JRiver Media Center(媒体管理软件)
android·学习·智能手机·媒体·软件需求
席卷全城10 小时前
Android 推箱子实现(引流文章)
android
齊家治國平天下11 小时前
Android 14 系统中 Tombstone 深度分析与解决指南
android·crash·系统服务·tombstone·android 14
maycho12313 小时前
MATLAB环境下基于双向长短时记忆网络的时间序列预测探索
android
思成不止于此13 小时前
【MySQL 零基础入门】MySQL 函数精讲(二):日期函数与流程控制函数篇
android·数据库·笔记·sql·学习·mysql
brave_zhao13 小时前
达梦数据库(DM8)支持全文索引功能,但并不直接兼容 MySQL 的 FULLTEXT 索引语法
android·adb
sheji341613 小时前
【开题答辩全过程】以 基于Android的网上订餐系统为例,包含答辩的问题和答案
android
easyboot14 小时前
C#使用SqlSugar操作mysql数据库
android·sqlsugar