当前位置: 首页 > news >正文

HarmonyOS黑马云音乐项目增加网络听歌功能(一、轮播图的实现)

黑马云音乐项目是个不错的练手项目。但网上的素材都是本地的,听歌资源有限。在本文中,猫哥将带大家为HarmonyOS黑马云音乐项目增加网络听歌功能,丰富听歌的体验。

使用nutpi/axios三方库来调用后台音乐接口. 后台接口由猫哥提供,仅用于学习研究。同时本文猫哥将介绍如何使用后台接口获取数据来实现轮播图功能。欢迎大家继续完善打造良好的免费听歌新体验。

猫哥提供的免费鸿蒙后台接口地址:

https://blog.csdn.net/yyz_1987/article/details/154404554

猫哥提交的黑马云音乐项目地址:

https://gitee.com/yyz116/heimayun

在这里插入图片描述

nutpi/axios库介绍

nutpi/axios库是一个坚果派贡献的三方库,基于axios库的封装。比封装之前的方式更好用了。在HarmonyOS项目中,它非常适合用于发起网络请求。通过封装一个axiosClient,我们可以统一管理和配置请求,使代码更加简洁和易于维护。

三方库安装

要在HarmonyOS项目中安装nutpi/axios库,需要在项目的oh-package.json5文件中添加依赖。以下是添加依赖的方法:

"dependencies": {"@nutpi/axios": "1.0.4"
}
实现音乐API

我们将定义两个API来从后台获取热歌榜和推荐歌单。

在这里插入图片描述

API接口定义

//api/music.ets
import { HttpPromise } from '@nutpi/axios';
import { axiosClient } from '../../ulits/axiosClient';
import { HotMusicResp } from '../model/HotMusicResp';
import { MusicMenuResp } from '../model/MusicMenuResp';// 1. 获取新歌热歌榜接口
export const getHotMusics = (start: number, count: number): HttpPromise<HotMusicResp> => axiosClient.post({ url: '/musicmenus', data: { start: start, count: count, kind: 'topWyMusic' } });// 2. 获取推荐歌单接口
export const getMusicMenu = (start: number, count: number): HttpPromise<MusicMenuResp> => axiosClient.get({ url: '/getsongmenu', params: { start: start, count: count } });

以上实现是不是很简单?两行代码即实现了上述两个接口。

以上的HotMusicResp 和MusicMenuResp 根本不用写,都是根据接口响应的json自动转换生成的。如何生成的?使用jsonFormat插件。

如何使用jsonFormat插件?

拷贝接口响应的json内容,然后使用jsonFormat插件自动生成MusicMenuResp.ets的代码。网络接口部分是最简单的,是不是一分钟不到就写完了一个接口?

在这里插入图片描述
在这里插入图片描述

最终生成的MusicMenuResp.ets文件内容如下:

export interface MusicMenuResp {code: number;message: string;data: MusicMenuRespData[];count: number;start: number;total: number;title: string;
}
export interface MusicMenuRespDataSongs {sid: string;song: string;sing: string;url: string;cover: string;
}
export interface MusicMenuRespData {title: string;cover: string;user: string;note: string;songs: MusicMenuRespDataSongs[];
}
在recommend.ets页面中使用API

recommend.ets文件是我们将使用定义的API来获取和显示数据的地方。

接口定义

首先,我们定义一个 interface SwiperData 来结构化轮播图的数据:

interface SwiperData {id: string;cover: string;url: string;song: string;sing: string;
}

在组件中使用接口

接下来,我们在recommend 组件中使用接口:

@Preview
@Component
struct recommend {// 轮播图数据@State swiperList: SwiperData[] = []@State dailyRecommend: recommendDailyType[] = []@State recommendList: recommendListType[] = []/*** 获取新歌榜*/async getHotMusics() {try {console.info('获取新歌榜:');const res = await getHotMusics(0, 10);if (res.data.code === 0 && res.data.data) {this.swiperList = []for (const it of res.data.data) {this.swiperList.push({ id: it.sid, cover: it.cover, url: it.url, song: it.song, sing: it.sing } as SwiperData)}} else {promptAction.showToast({message: '获取新歌榜失败',duration: 2000});}} catch (error) {console.error('获取新歌榜失败:', JSON.stringify(error as Error));promptAction.showToast({message: '获取新歌榜失败',duration: 2000});}}/*** 获取推荐歌单*/getMusicMenus() {getMusicMenu(0, 10).then((res) => {Log.debug(res.data.message)Log.debug("request", "res.data.code:%{public}d", res.data.code)}).catch((err: BusinessError) => {Log.debug("request", "err.data.code:%d", err.code)Log.debug("request", err.message)});}aboutToAppear(): void {this.dailyRecommend = dailyRecommendthis.recommendList = recommendListthis.getHotMusics()this.getMusicMenus()}
}

解释

  • getHotMusics 方法: 这是一个异步函数,用于从后台获取热歌榜数据。它调用 getHotMusics API,处理响应数据,并更新 swiperList 状态。如果请求失败或返回错误码,则会显示一个提示框(toast)消息,指示获取失败。

  • getMusicMenus 方法: 这个方法用于从后台获取推荐歌单数据,调用 getMusicMenu API。它会在控制台输出响应消息和状态码。如果发生错误,会输出错误码和错误消息。

  • aboutToAppear 方法: 这个生命周期方法在组件即将显示在屏幕上时被调用。我们在这里初始化状态变量,并调用 getHotMusicsgetMusicMenus 方法来获取数据。

axiosClient .ets客户端封装

axiosClient .ets是使用nutpi/axios库封装的一个客户端类。
为什么使用了nutpi/axios网络库后还要再这么封一下?因为拦截器和自定义全局配置,每个人的不一样,还需要灵活的改。当然如果你不用拦截器的话就不需要封装它了,直接new AxiosHttpRequest就可以用了。

该封装完成后,放到utils文件夹下,作为一个通用的封装直接拿来使用即可。

//axiosClient.ets
import {AxiosError,AxiosHeaders,AxiosHttpRequest,AxiosRequestHeaders,AxiosResponse,FileUploadConfig,HttpPromise,UploadFile
} from '@nutpi/axios';
import { Log } from './logutil';
import { promptAction, router } from '@kit.ArkUI';function showToast(msg: string) {Log.debug(msg)promptAction.showToast({ message: msg })
}function showLoadingDialog(msg: string) {Log.debug(msg)// promptAction.showToast({ message: msg })
}function hideLoadingDialog() {}// 移除硬编码的token,改为动态获取
// AppStorage.SetOrCreate('Authorization', "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Miwibmlja25hbWUiOm51bGwsImF2YXRhciI6bnVsbCwiaWF0IjoxNzU0NjY2ODk3LCJleHAiOjE3NTQ3NTMyOTd9.32Kp1pCt-HIqKglOpLrRhOMdSjyonutEDkifQRaoZCo");// 图片地址的前缀
//export const IMAGE_BASE_URL = 'https://source.felin.maotuai.com' // 正式环境
// export const IMAGE_BASE_URL = 'https://source.felintest.maotuai.com'   // 测试环境// 通用响应数据结构(兼容 code 与 success 协议)
interface CommonResponse {code?: number;success?: boolean;message?: string;
}/*** axios请求客户端创建*/
const axiosClient = new AxiosHttpRequest({baseURL: "http://120.27.146.247:8000/api/v1", //正式环境// baseURL: "http://xqytest.maotuai.com/api",  //测试环境timeout: 10 * 1000,checkResultCode: false,showLoading: true,headers: new AxiosHeaders({'Content-Type': 'application/json'}) as AxiosRequestHeaders,interceptorHooks: {requestInterceptor: async (config) => {// 在发送请求之前做一些处理,例如打印请求信息// console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')console.log('🌏网络请求Request 请求方法:', `${config.method}`);console.log('🌏网络请求Request 请求链接:', `${config.baseURL}${config.url}`);console.log('🌏网络请求Request Params:', `${JSON.stringify(config.params)}`);console.log('🌏网络请求Request Data:', `${JSON.stringify(config.data)}`);// console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<')// 动态获取token//const token = AppAuthManager.getToken();// if (token && config.headers) {//   config.headers['Authorization'] = 'Bearer ' + token;//   // console.log('token','Bearer ' + token)// }// 添加设备ID到请求头,用于多点登录和安全机制辅助校验if (config.headers) {try {//const deviceId = await DeviceManager.getDeviceId();//config.headers['uid'] = deviceId;// console.log('🔧 [登录系统] 网络请求添加设备ID (uid):', deviceId);//Log.debug('网络请求添加设备ID:', deviceId);} catch (error) {console.error('❌ [登录系统] 获取设备ID失败:', JSON.stringify(error));Log.error('获取设备ID失败:', JSON.stringify(error));}}axiosClient.config.showLoading = config.showLoadingif (config.showLoading) {showLoadingDialog("加载中...")}if (config.checkLoginState) {// 检查登录状态// if (!AppAuthManager.getToken())// {//   if (config.needJumpToLogin) {//     // 可以在这里跳转到登录页面//     // router.replaceUrl({ url: 'pages/LoginPage' });//   }//   throw new Error("请登录")// }}return config;},requestInterceptorCatch: (err:Error) => {console.error("网络请求RequestError", err.toString())if (axiosClient.config.showLoading) {hideLoadingDialog()}return err;},responseInterceptor: (response:AxiosResponse<CommonResponse>) => {// 优先执行自己的请求响应拦截器,再执行通用请求的if (axiosClient.config.showLoading) {hideLoadingDialog()}console.log('🌏网络请求response:', `${JSON.stringify(response)}`);console.log('🌏网络请求response Data:', `${JSON.stringify(response.data)}`);if (response.status === 200) {const data: CommonResponse = response.data as CommonResponse;const ok = (data.code === 0 || data.success === true);if (!ok) {errorHandle(response);return Promise.reject(response);}return Promise.resolve(response);}return Promise.reject(response);},responseInterceptorCatch: (error:Error) => {if (axiosClient.config.showLoading) {hideLoadingDialog()}console.error("网络请求响应异常", error.toString());errorHandler(error);return Promise.reject(error);},}
});// 处理业务错误
async function errorHandle(response:AxiosResponse<CommonResponse>) {const data: CommonResponse = response.data as CommonResponse;switch (data.code) {case 401:await handle401Error();break;// 403 token过期// 登录过期对用户进行提示// 清除本地token和清空vuex中token对象// 跳转登录页面case 403:showToast("登录过期,请重新登录")handleLogout();break;// 404请求不存在case 404:showToast("网络请求不存在")break;default:const message: string = data.message ?? '';if (message && !message.includes('用户未加入任何家庭')) {showToast(message);}break}
}// 处理401错误 - 尝试续票
async function handle401Error() {try {Log.info('检测到401错误,开始续票处理');// if (!renewalSuccess) {//   Log.warn('401错误续票失败,跳转到登录页面');//   await handleLogout();// } else {//   Log.info('401错误续票成功,用户可继续使用');// }} catch (error) {Log.error('处理401错误失败:', error);await handleLogout();}
}// 统一的登出处理
async function handleLogout() {try {// 清除token// 跳转到登录页面router.replaceUrl({ url: 'pages/LoginPage' });} catch (error) {console.error('登出处理失败:', JSON.stringify(error));// 即使出错也要跳转到登录页面router.replaceUrl({ url: 'pages/LoginPage' });}
}interface DataItem {message: string;
}
interface ErrorItem {status: number;data:DataItem;
}
interface CustomError extends Error {response?: ErrorItem;
}
async function errorHandler(error: Error) {if (error instanceof AxiosError) {//showToast(error.message)} else if (error != undefined ) {const err:CustomError = error as CustomErrorif(err.response != undefined && err.response.status){switch (err.response.status) {// 401: 未登录// 未登录则跳转登录页面,并携带当前页面的路径// 在登录成功后返回当前页面,这一步需要在登录页操作。case 401:await handle401Error();break;// 403 token过期// 登录过期对用户进行提示// 清除本地token和清空vuex中token对象// 跳转登录页面case 403:showToast("登录过期,请重新登录")handleLogout();break;// 404请求不存在case 404:showToast("网络请求不存在")break;// 其他错误,直接抛出错误提示default:if (err.response.data.message) {// 过滤掉不需要显示的提示信息const message:string = err.response.data.message;if (message && !message.includes('用户未加入任何家庭')) {showToast(message);}}}}}
}// 导出供其他模块使用
export { axiosClient, HttpPromise, UploadFile, FileUploadConfig };
结论

在本文中,我们成功地为HarmonyOS黑马云音乐项目增加了网络听歌功能,并使用nutpi/axios库实现了推荐歌单和热歌榜的获取。我们还通过后台接口获取的数据实现了轮播图功能。这种方法不仅提升了用户体验,还使代码库更加易于管理和扩展。

http://www.dtcms.com/a/577086.html

相关文章:

  • 二、Netty-NIO核心原理详解(NIO核心组件:Buffer、Channel、Selector)
  • 网站短信接口怎么做网站开发环境有什么
  • 网站建设客户开发方案网站建设公司介绍
  • 矩阵乘法优化
  • sward零基础学习,如何在sward文档中集成Kanass事项
  • React使用笔记(持续更新中)
  • ArkTS运行时
  • C语言递归宏详解
  • 指令微调(Instruction Tuning)
  • Linux 中 NIC(网络接口卡)和协议栈的区别以及DPDK
  • MATLAB实现贝叶斯回归预测
  • ZYNQ介绍
  • 【Python】-- 趣味代码 - Piano游戏
  • 解决使用EasyExcel导出带公式的excel,公式不自动计算问题
  • 展示型网站多少钱建设大型网站的公司
  • 前端FAQ: 描述⼀下你最近使⽤过的前端框架,并解释为何选择它们?
  • [特殊字符] 微前端部署实战:Nginx 配置 HTTPS 与 CORS 跨域解决方案(示例版)
  • 短视频矩阵系统搭建指南:源码部署与全流程解析
  • 李沐动手学深度学习笔记(1)
  • 做网站云服务器选择多大带宽北京网站建设有哪些公司好
  • 第8章 模块系统
  • GraphRAG在Windows环境下离线部署
  • Spring Boot 实战:企业级接口限流与熔断机制设计
  • 二十一、二进制文件部署高可用集群
  • 窗口dp|组合数学
  • 【linux国庆练习】
  • 织梦cms怎么做双语网站wordpress网页小特效
  • 我的世界做壁纸的网站移动互联网开发心得体会
  • CST对电路板与地面平面耦合的电磁模拟
  • Apple授权登录开发流程