技术标签: Promise 网络 鸿蒙开发 http 鸿蒙 加载列表 网络请求
为了把上一节(04鸿蒙APP开发之创建一个底部菜单栏及首页列表)中的测试数据,换成网络列表数据,实现动态加载。
官方的DEMO封装得太深,看不出最简化的结构。
基于这些原因,开始写下本篇博文,内容主要包含,网络请求封装、发起请求,及页面渲染。
项目最新的目录结构,如下图所示:
本段内容主要参考了这篇文章,【鸿蒙应用ArkTS开发系列】- http网络库使用讲解和封装,并完善了appendQueryParams、isValidUrl这两个函数,修改回调函数名称为success、fail。
官方的Http请求,需要在各个页面每次都要写很长一段代码,为了复用,且降低使用复杂度,所以对Http请求做了二次封装。
包含三个文件:RequestOption.ets、HttpManager.ets、HttpCore.ets,它们的作用如下:
RequestOption.ets
是一个枚举类,主要是构造一个请求所需的参数集合,包括 地址、请求方式、请求参数、返回数据类型、设置请求头信息。具体实现如下:
export interface RequestOptions {
url?: string;
method?: RequestMethod;
queryParams?: Record<string, string>;
extraData?: string | Object | ArrayBuffer;
header?: Object;
}
export enum RequestMethod {
OPTIONS = "OPTIONS",
GET = "GET",
POST = "POST",
PUT = "PUT",
HEAD = "HEAD",
DELETE = "DELETE",
TRACE = "TRACE",
CONNECT = "CONNECT"
}
HttpManager.ets
是一个类,使用单例的方式创建实例,作为页面与Http请求类的桥梁,通过调用HttpCore.request()函数,发起请求。具体实现如下:
//注意这一行非常关键,需要别名,不然无法调用request(options)函数
import {
httpCore as HttpCore } from './HttpCore';
import {
RequestOptions } from './RequestOption';
export class HttpManager {
private static mInstance: HttpManager;
//不允许实例
private constructor() {
}
static getInstance(): HttpManager {
if (!HttpManager.mInstance) {
HttpManager.mInstance = new HttpManager();
}
return HttpManager.mInstance;
}
request<T>(options: RequestOptions): Promise<T> {
return HttpCore.request(options);
}
}
HttpCore.ets
是封装原官方的Http请求,进行了回调函数扩展,方便在页面中捕捉回调,获取返回的网络数据,实现页面的状态更新。具体实现如下:
import http from '@ohos.net.http';
import {
RequestOptions } from '../net/RequestOption';
export class HttpCore {
request<T>(requestOption: RequestOptions): Promise<T> {
return new Promise<T>((success, fail) => {
this.sendRequest(requestOption)
.then((response) => {
if (typeof response.result !== 'string') {
fail(new Error("Invalid data type"));
} else {
let bean: T = JSON.parse(response.result);
if (bean) {
success(bean);
} else {
fail(new Error("JSON to T failed"));
}
}
})
.catch((error) => {
fail(error);
})
});
}
private sendRequest(requestOption: RequestOptions): Promise<http.HttpResponse> {
let httpRequest = http.createHttp();
let successFunction, failFunction;
const resultPromise = new Promise<http.HttpResponse>((success, fail) => {
successFunction = success;
failFunction = fail;
})
if (!this.isValidUrl(requestOption.url)) {
return Promise.reject(new Error("URL format error"));
}
let promise = httpRequest.request(this.appendQueryParams(requestOption.url, requestOption.queryParams), {
method: requestOption.method,
header: requestOption.header,
extraData: requestOption.extraData,
expectDataType: http.HttpDataType.STRING
});
promise.then((response) => {
console.info('Result:' + response.result);
console.info('code:' + response.responseCode);
console.info('header:' + JSON.stringify(response.header));
successFunction(response);
}).catch((err) => {
failFunction(err);
}).finally(() => {
httpRequest.destroy();
});
return resultPromise;
}
/**
* typescript中高级类型Record
* 组装参数
* @param url
* @param queryParams
* @returns
*/
private appendQueryParams(url: string, queryParams: Record<string, string>): string {
var params: string;
var i: number = 0;
for (const key in queryParams) {
if (i == 0) {
params += key + "=" + queryParams[key];
} else {
params += "&" + key + "=" + queryParams[key];
}
i++;
console.log(key + ":" + queryParams[key]);
}
if (params != null) {
return url + "?" + params
}
return url
}
/**
* 校验url是否合法,正则
* @param url
* @returns
*/
private isValidUrl(url: string): boolean {
var regular = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\*\+,;=.]+$/;
if (regular.test(url)) {
return true;
}
return false;
}
}
//注意这一行非常关键
export const httpCore = new HttpCore();
这三段代码中用到了非常多的陌生关键字,Promise、typeof、.then、.catch、.finally、Record<string, string>、.test,它们是做什么的
,我们来逐一说明。
const myRecord:Record<string, string>={
name:'张三' , city: 'Beijing', country: 'China' }
https://opentdb.com/api.php?amount=10&category=23&difficulty=easy&type=multiple
此API返回的JSON数据如下:
{
"response_code": 0,
"results": [{
"type": "multiple",
"difficulty": "easy",
"category": "History",
"question": "Who was among those killed in the 2010 Smolensk, Russia plane crash tragedy?",
"correct_answer": "The Polish President",
"incorrect_answers": ["Pope John Paul II", "Bang-Ding Ow", "Albert Putin"]
}, {
"type": "multiple",
"difficulty": "easy",
"category": "History",
"question": "What was the first sport to have been played on the moon?",
"correct_answer": "Golf",
"incorrect_answers": ["Football", "Tennis", "Soccer"]
}, {
"type": "multiple",
"difficulty": "easy",
"category": "History",
"question": "Which of these countries remained neutral during World War II?",
"correct_answer": "Switzerland",
"incorrect_answers": ["United Kingdom", "France", "Italy"]
}, {
"type": "multiple",
"difficulty": "easy",
"category": "History",
"question": "How many manned moon landings have there been?",
"correct_answer": "6",
"incorrect_answers": ["1", "3", "7"]
}, {
"type": "multiple",
"difficulty": "easy",
"category": "History",
"question": "In what year was the M1911 pistol designed?",
"correct_answer": "1911",
"incorrect_answers": ["1907", "1899", "1917"]
}, {
"type": "multiple",
"difficulty": "easy",
"category": "History",
"question": "During WWII, in 1945, the United States dropped atomic bombs on the two Japanese cities of Hiroshima and what other city?",
"correct_answer": "Nagasaki",
"incorrect_answers": ["Kawasaki", "Tokyo", "Kagoshima"]
}, {
"type": "multiple",
"difficulty": "easy",
"category": "History",
"question": "These two countries held a commonwealth from the 16th to 18th century.",
"correct_answer": "Poland and Lithuania",
"incorrect_answers": ["Hutu and Rwanda", "North Korea and South Korea", "Bangladesh and Bhutan"]
}, {
"type": "multiple",
"difficulty": "easy",
"category": "History",
"question": "The idea of Socialism was articulated and advanced by whom?",
"correct_answer": "Karl Marx",
"incorrect_answers": ["Vladimir Lenin", "Joseph Stalin", "Vladimir Putin"]
}, {
"type": "multiple",
"difficulty": "easy",
"category": "History",
"question": "Which German field marshal was known as the `Desert Fox`?",
"correct_answer": "Erwin Rommel",
"incorrect_answers": ["Ernst Busch", "Wolfram Freiherr von Richthofen", "Wilhelm List"]
}, {
"type": "multiple",
"difficulty": "easy",
"category": "History",
"question": "Which one of these was not a beach landing site in the Invasion of Normandy?",
"correct_answer": "Silver",
"incorrect_answers": ["Gold", "Juno", "Sword"]
}]
}
主要是包含两个类TestApiBean和ResTestAPiBean。
ResTestAPiBean.ets类实现如下:
import ArrayList from '@ohos.util.ArrayList';
import {
TestApiBean } from './TestApiBean';
export class ResTestAPiBean {
response_code?: string;
results?: Array<TestApiBean>;
}
TestApiBean.ets类实现如下:
export class TestApiBean {
id?:string;
question?: string;
correct_answer?: string;
type?: string;
constructor(
id:string,
question: string,
correct_answer: string,
type: string
) {
this.id=id;
this.question = question;
this.correct_answer = correct_answer;
this.type = type;
}
}
Index.ets类的实现如下:
import prompt from '@ohos.promptAction'
import util from '@ohos.util';
//引入自定义类
import {
HttpManager } from './net/HttpManager'
import {
RequestMethod } from './net/RequestOption';
import {
TestApiBean } from './entity/TestApiBean';
import {
ResTestAPiBean } from './entity/ResTestAPiBean';
@Entry
@Component
struct Index {
private tabsController = new TabsController();
@State index: number = 0; // 选项卡下标,默认为第一个
//组件Index自带的函数,类似于重写@Override,在build()之前就会执行
aboutToAppear() {
this.loadApiTestData();
}
//@State此时的状态就非常有用了,当你上下拉刷新UI的时候,只需要重新给此变量赋值就会自动刷新界面(UI)
@State testApiBeanList: Array<TestApiBean> = new Array<TestApiBean>();
/**
* 调用封装的网络请求类,访问网络API并获取列表数据
*/
loadApiTestData() {
HttpManager.getInstance().request<ResTestAPiBean>({
url: "https://opentdb.com/api.php?amount=10&category=23&difficulty=easy&type=multiple",
method: RequestMethod.GET
}).then((result) => {
for (let i = 0; i < result.results.length; i++) {
let item = result.results[i];
this.testApiBeanList.push({
id: util.generateRandomUUID(true),
question: item.question,
correct_answer: item.correct_answer,
type: item.type
});
}
}).catch((error) => {
console.log(error);
});
}
//开始构建UI
build() {
Column() {
Tabs({
controller: this.tabsController }) {
TabContent() {
Column() {
List({
space: 10, initialIndex: 0 }) {
ForEach(this.testApiBeanList, (item: TestApiBean, index: number) => {
// 循环渲染ListItem
ListItem() {
Row() {
Column() {
Text(item.correct_answer).fontSize(16).fontColor($r("app.color.color_list_title"))
Text(item.question).fontSize(14).fontColor($r("app.color.color_list_abstract"))
}.width('100%').alignItems(HorizontalAlign.Start)
}.width('100%').justifyContent(FlexAlign.Start).padding({
left: 16, right: 16, top: 8, bottom: 8 })
.onClick(() => {
//选项单击事件
prompt.showToast({
message: this.testApiBeanList[index].question, duration: 2000 })
})
}
}, item => item.id)
}.width("100%").listDirection(Axis.Vertical).scrollBar(BarState.Auto)
}.width('100%').height('100%').justifyContent(FlexAlign.Start).backgroundColor("#eeeeee")
}.tabBar(this.tabHome())
TabContent() {
Column() {
Text("订阅内容").fontSize(32)
}.width('100%').height('100%').justifyContent(FlexAlign.Center).backgroundColor("#eeeeee")
}.tabBar(this.tabSub)
TabContent() {
Column() {
Text("我的内容").fontSize(32)
}.width('100%').height('100%').justifyContent(FlexAlign.Center).backgroundColor("#eeeeee")
}.tabBar(this.tabMe)
}.barPosition(BarPosition.End)
}.width('100%').height('100%')
}
@Builder tabHome() {
Column() {
Image(this.index == 0 ? $r('app.media.ic_menu_home_focus') : $r('app.media.ic_menu_home_normal'))
.size({
width: 25, height: 25 }).margin({
top: 5, bottom: 5 })
Text("首页").fontSize(16).fontColor(this.index == 0 ? "#2a58d0" : "#6b6b6b")
}.width('100%').height('100%').onClick(() => {
this.index = 0;
this.tabsController.changeIndex(this.index)
})
}
@Builder tabSub() {
Column() {
Image(this.index == 1 ? $r('app.media.ic_menu_sub_focus') : $r('app.media.ic_menu_sub_normal'))
.size({
width: 25, height: 25 }).margin({
top: 5, bottom: 5 })
Text("订阅").fontSize(16).fontColor(this.index == 1 ? "#2a58d0" : "#6b6b6b")
}.width('100%').height('100%').onClick(() => {
this.index = 1;
this.tabsController.changeIndex(this.index)
})
}
@Builder tabMe() {
Column() {
Image(this.index == 2 ? $r('app.media.ic_menu_me_focus') : $r('app.media.ic_menu_me_normal'))
.size({
width: 25, height: 25 }).margin({
top: 5, bottom: 5 })
Text("我的").fontSize(16).fontColor(this.index == 2 ? "#2a58d0" : "#6b6b6b")
}.width('100%').height('100%').onClick(() => {
this.index = 2;
this.tabsController.changeIndex(this.index)
})
}
}
如下图所示:
网络请求封装:https://blog.csdn.net/q919233914/article/details/130643452
原创不易,求个关注。
微信公众号:一粒尘埃的漫旅
里面有很多想对大家说的话,就像和朋友聊聊天。
写代码,做设计,聊生活,聊工作,聊职场。
我见到的世界是什么样子的?
搜索关注我吧。
公众号与博客的内容不同。
文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib
文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang
文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些
文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器
文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距
文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器
文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn
文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios
文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql
文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...
文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120
文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数