> 技术文档 > OpenHarmony 5.0标准系统开发板PurplePi学习笔记_purple pi

OpenHarmony 5.0标准系统开发板PurplePi学习笔记_purple pi


最近随着科技的发展和开源社区的活跃,越来越多的开发者开始探索和利用开源技术来创造有趣的产品和体验。本次使用最便宜的OpenHarmony5.0的开发板Purple Pi 做个有意思的实验,打造个免费观影电视盒子。

OpenHarmony 5.0 开发板是一个基于分布式技术的操作系统开发平台,可以用于多种开发和测试场景。虽然它本身不是为电视盒子专门设计的,但你可以通过开发和定制,将它改造成一个具有类似功能的设备。以下是一些有意思的玩法:

  1. 应用程序开发:你可以使用 OpenHarmony 的开发工具来创建和测试各种应用程序,包括智能家居控制、游戏应用、音乐播放器等。

  2. 分布式能力探索:OpenHarmony 的一大特色是其分布式能力,你可以探索如何利用这一特性来实现设备间的无缝协同工作,比如通过一个设备控制另一个设备的功能。

  3. 媒体中心:你可以将 OpenHarmony 开发板改造成一个媒体中心,支持播放音乐、电影等多媒体内容。这需要根据开发板的硬件配置来选择合适的媒体播放框架进行开发。

  4. 学习与研究:对于开发者和研究者来说,OpenHarmony 开发板是一个很好的学习和研究平台,可以用来研究分布式操作系统的设计和实现原理。

  5. 智能家居控制:利用 OpenHarmony 的分布式能力,你可以开发一个智能家居控制中心,通过这个开发板来控制家中的其他智能设备,如灯泡、插座、摄像头等。

  6. 游戏开发:开发板也可以用来开发简单的游戏应用,尤其是适合分布式环境下的多人游戏。

随着智能制造技术的飞速发展,越来越多的人开始注重智能硬件的研究和开发。博主之前还吐槽开源鸿蒙真不给力,连个标准板的开发板都没有。要么不完整要么价格贵的离谱。如今OpenHarmony 上手学习正当时。Purple Pi OH开发板提供最新OpenHarmony5.0的开发板,丰富的开源资料,开放了OpenHarmony SDK,可以帮助开发者更好地学习和进阶。

以下是博主的Purple Pi OH鸿蒙开发板的学习和使用笔记,打造个免费观影电视盒子。直接拿来玩起来,边学边玩,提高兴趣。

开发板资料介绍地址:http://www.industio.cn/product-item-37.html

把OpenHarmony鸿蒙开发板玩起来,我的“爱影家”这款HarmonyOS NEXT应用推荐下,并且已经成功适配到OpenHarmony 5.0标准系统上,为用户提供了一个免费的观影平台。

OpenHarmony 5.0标准系统开发板PurplePi学习笔记_purple pi

其实无所谓适配,直接就安装上啦。我是使用debug的签名直接安装。也就是说只要你的HarmonyOS应用没有用到华为特有的HMS服务,签名后是可以直接安装运行的。
在这里插入图片描述

一、寻找理想的盒子

一直以来,手机上的观影体验总被各种广告打断,让人不胜其烦。对于我这样的普通观影爱好者来说,一个纯净、免费的观影平台是多么令人期待。然而,市面上的许多电视盒子,虽然功能强大,但是广告插播频繁,甚至有时还需要付费成为会员才能享受无广告观影。于是,我有了自己动手打造理想电视盒子的想法,希望通过自己的努力,DIY一个完全自主、无广告干扰的观影环境。

二、OpenHarmony 5.0:开源开发板的新选择

在众多开源系统中,OpenHarmony 5.0以其稳定性和高性能脱颖而出。特别是它提供的开源开发板,让我们有机会从零开始,构建一个完全按照自己的需求设计的电视盒子。这不仅是技术上的挑战,更是对个人创造力的考验。使用最便宜的OpenHarmony 5.0标准系统开发板,我开始了我的探索之旅。

在这里插入图片描述

在这里插入图片描述

APP开发步骤

虽然我的板子是OpenHarmony的,但是如果你没有使用到华为HMS的服务,那么开发上直接当做HarmonyOS NEXT就行啦。

  1. 搭建开发环境:首先,我确保安装了HarmonyOS NEXT的开发环境。根据官方文档进行安装和配置,一切准备就绪。

  2. 创建项目:使用DevEco Studio创建一个新的HarmonyOS NEXT项目,并选择ArkTS作为开发语言。一切从零开始,充满了未知和挑战。

  3. 配置网络请求:项目中引入了@nutpi/axios库,配置了网络请求的基础URL和拦截器。

    import axios from \'@nutpi/axios\';axios.defaults.baseURL = \'https://api.example.com\';axios.interceptors.request.use(config => { // 添加请求拦截器 return config;}, error => { return Promise.reject(error);});
  4. 实现影视首页功能:在影视首页中,我实现了轮播图、热映电影、即将上映电影和热门电视剧集的功能。通过API获取数据并在前端展示,整个过程充满了学习和实践的乐趣。以下是网络后台接口封装。在HarmonyOS NEXT开发环境中,可以使用@nutpi/axios库来简化网络请求的操作。本项目使用HarmonyOS NEXT框架和@nutpi/axios库实现一行代码写接口。大幅简化了网络接口的实现。
    为什么选择@nutpi/axios?
    nutpi/axios是坚果派对axios封装过的鸿蒙HTTP客户端库,用于简化axios库的使用和以最简单的形式写代码。使用nutpi/axios库可以大大简化代码,使网络接口变得简单直观。

    import {axiosClient, HttpPromise} from \'../../utils/axiosClient\';import { HotMovieReq, MovieRespData, SwiperData } from \'../bean/ApiTypes\';// 1.获取轮播图接口export const getSwiperData = (): HttpPromise<SwiperData> => axiosClient.get({url:\'/swiperdata\'});// 2.获取即将上映影视接口export const getSoonMovie = (start:number, count:number): HttpPromise<MovieRespData> => axiosClient.post({url:\'/soonmovie\', data: { start:start, count:count }});// 3.获取热门影视接口export const getHotMovie = (req:HotMovieReq): HttpPromise<MovieRespData> => axiosClient.post({url:\'/hotmovie\', data:req});// 4.获取最新上演影视接口export const getNewMovie = (start:number, count:number): HttpPromise<MovieRespData> => axiosClient.post({url:\'/newmovie\', data: { start:start, count:count }});// 5.获取最热门剧集接口export const getHotTv = (start:number, count:number): HttpPromise<MovieRespData> => axiosClient.post({url:\'/tvhot\', data: { start:start, count:count }});
首页电影海报轮播图懒加载
// 轮播图Swiper(this.swiperController) { LazyForEach(this.swiperData, (item: SwiperItem) => { Stack({ alignContent: Alignment.Center }) { Image(item.imageUrl) .width(\'100%\') .height(180) .zIndex(1) .onClick(() => { this.pageStack.pushDestinationByName(\"MovieDetailPage\", { id:item.id }).catch((e:Error)=>{ // 跳转失败,会返回错误码及错误信息 console.log(`catch exception: ${JSON.stringify(e)}`) }).then(()=>{ // 跳转成功 }); }) // 显示轮播图标题 Text(item.title) .padding(5) .margin({ top: 135 }) .width(\'100%\') .height(60) .textAlign(TextAlign.Center) .maxLines(2) .textOverflow({ overflow: TextOverflow.Clip }) .fontSize(22) .fontColor(Color.White) .opacity(100)// 设置标题的透明度 不透明度设为100%,表示完全不透明 .backgroundColor(\'#808080AA\')// 背景颜色设为透明 .zIndex(2) .onClick(() => { this.pageStack.pushDestinationByName(\"MovieDetailPage\", { id:item.id }).catch((e:Error)=>{ // 跳转失败,会返回错误码及错误信息 console.log(`catch exception: ${JSON.stringify(e)}`) }).then(()=>{ // 跳转成功 }); }) } }, (item: SwiperItem) => item.id)}.cachedCount(2).index(1).autoPlay(true).interval(4000).loop(true).indicatorInteractive(true).duration(1000).itemSpace(0).curve(Curve.Linear).onChange((index: number) => { console.info(index.toString())}).onGestureSwipe((index: number, extraInfo: SwiperAnimationEvent) => { console.info(\"index: \" + index) console.info(\"current offset: \" + extraInfo.currentOffset)}).height(180) // 设置高度
电影详情页的设计

在电影详情页中,将使用 Badge、SymbolSpan、Button、Rating 等组件来展示电影的详细信息。

import { getDetailMv, getMovieSrc } from \"../../common/api/movie\"import { Log } from \"../../utils/logutil\"import { BusinessError } from \"@kit.BasicServicesKit\"import { DetailMvResp, DetailMvRespCast } from \"../../common/bean/DetailMvResp\"import { LengthMetrics, promptAction } from \"@kit.ArkUI\"import { MvSourceResp } from \"../../common/bean/MvSourceResp\"@Builderexport function MovieDetailPageBuilder() { Detail()}@Componentstruct Detail { pageStack: NavPathStack = new NavPathStack() private uid = \'\' @State detailData: DetailMvResp | null = null; private srcData: MvSourceResp | null = null; private description: string = \'\' private isToggle = false @State toggleText: string = \'\' @State toggleBtn: string = \'展开\' build() { NavDestination() { Column({ space: 0 }) { Row() { Image(this.detailData?.images).objectFit(ImageFit.Auto).width(120).borderRadius(5) Column({ space: 8 }) { Text(this.detailData?.title).fontSize(18) Text(this.detailData?.year + \" \" + this.detailData?.genre).fontSize(14) Row() {  Badge({ count: this.detailData?.wish_count, maxCount: 10000, position: BadgePosition.RightTop, style: { badgeSize: 22, badgeColor: \'#fffab52a\' }  }) { Row() {  Text() {  SymbolSpan($r(\'sys.symbol.heart\')).fontWeight(FontWeight.Lighter).fontSize(32).fontColor([\'#fffab52a\'])  }  Text(\'想看\') }.backgroundColor(\'#f8f4f5\').borderRadius(5).padding(5)  }.padding(8)  Blank(10).width(40)  Badge({ count: this.detailData?.reviews_count, maxCount: 10000, position: BadgePosition.RightTop, style: { badgeSize: 22, badgeColor: \'#fffab52a\' }  }) { Row() {  Text() {  SymbolSpan($r(\'sys.symbol.star\')).fontWeight(FontWeight.Lighter).fontSize(32).fontColor([\'#fffab52a\'])  }  Text(\'看过\') }.backgroundColor(\'#f8f4f5\').borderRadius(5).padding(5)  }.padding(8) } Button(\'播放\', { buttonStyle: ButtonStyleMode.NORMAL, role: ButtonRole.NORMAL })  .borderRadius(8)  .borderColor(\'#fffab52a\')  .fontColor(\'#fffab52a\')  .width(100)  .height(35)  .onClick(() => { console.info(\'Button onClick\') if (this.srcData != null) {  this.pageStack.pushDestinationByName(\"VideoPlayerPage\", { item: { video: this.srcData.urls[0], tvurls: this.srcData.tvurls, title: this.srcData.title, desc: this.detailData?.summary } }).catch((e: Error) => {  // 跳转失败,会返回错误码及错误信息  console.log(`catch exception: ${JSON.stringify(e)}`)  }).then(() => {  // 跳转成功  }); } else {  promptAction.showToast({ message: \'暂无资源\' }) }  }) }.alignItems(HorizontalAlign.Start) // 水平方向靠左对齐 .justifyContent(FlexAlign.Start) // 垂直方向靠上对齐 .padding(10) }.height(160).width(\'100%\') Row() { Text(\'豆瓣评分\').fontSize(16).padding(5) Rating({ rating: (this.detailData?.rate ?? 0) / 2, indicator: true }) .stars(5) .stepSize(0.5).height(28) Text(this.detailData?.rate.toString()).fontColor(\'#fffab52a\').fontWeight(FontWeight.Bold).fontSize(36).padding(5) }.width(\'100%\').height(80).borderRadius(5).backgroundColor(\'#f8f4f5\').margin(20) Text(\'简介\').fontSize(18).padding({ bottom: 10 }).fontWeight(FontWeight.Bold).alignSelf(ItemAlign.Start) Text(this.toggleText).fontSize(14).lineHeight(20).alignSelf(ItemAlign.Start) Text(this.toggleBtn).fontSize(14).fontColor(Color.Gray).padding(10).alignSelf(ItemAlign.End).onClick(() => { this.isToggle = !this.isToggle if (this.isToggle) { this.toggleBtn = \'收起\' this.toggleText = this.description } else { this.toggleBtn = \'展开\' this.toggleText = this.description.substring(0, 100) + \'...\' } }) Text(\'影人\').fontSize(18).padding({ bottom: 10 }).fontWeight(FontWeight.Bold).alignSelf(ItemAlign.Start) Scroll() { Row({ space: 5 }) { ForEach(this.detailData?.cast, (item: DetailMvRespCast) => {  Column({ space: 0 }) { Image(item.cover).objectFit(ImageFit.Auto).height(120).borderRadius(5)  .onClick(() => {  }) Text(item.name)  .alignSelf(ItemAlign.Center)  .maxLines(1)  .textOverflow({ overflow: TextOverflow.Ellipsis })  .fontSize(14).padding(10)  }.justifyContent(FlexAlign.Center) }, (itm: DetailMvRespCast, idx) => itm.id) } }.scrollable(ScrollDirection.Horizontal) }.padding({ left: 10, right: 10 }) }.title(\"电影详情\") .width(\'100%\') .height(\'100%\') .onReady(ctx => { this.pageStack = ctx.pathStack //从上个页面拿参数 this.pageStack.getParamByName(\"MovieDetailPage\") interface params { id: string; } let par = ctx.pathInfo.param as params Log.debug(\"par:%s\", par.id) this.uid = par.id }) .onShown(() => { console.info(\'Detail onShown\'); getDetailMv(this.uid).then((res) => { Log.debug(res.data.message) Log.debug(\"request\", \"res.data.code:%{public}d\", res.data.code) this.detailData = res.data this.description = this.detailData.summary this.toggleText = this.description.substring(0, 100) + \'...\' }).catch((err: BusinessError) => { Log.debug(\"request\", \"err.data.code:%d\", err.code) Log.debug(\"request\", err.message) }); getMovieSrc(this.uid).then((res) => { Log.debug(res.data.message) Log.debug(\"request\", \"res.data.code:%{public}d\", res.data.code) if (res.data.code == 0) { this.srcData = res.data } }).catch((err: BusinessError) => { Log.debug(\"request\", \"err.data.code:%d\", err.code) Log.debug(\"request\", err.message) }); }) }}
三、配套应用“爱影家”:打造理想观影环境

在找到理想的硬件平台后,接下来就是选择安装你的软件,我的“爱影家”这款HarmonyOS NEXT 免费无广告观影应用,非常适合我的需求。它不仅能够满足我免费观影的愿望,还能安装各种我喜爱的应用,完全自主可控。更令人兴奋的是,“爱影家”实测完全可以直接跑在Openharmony上,且流畅运行。

上面是客户端的代码片段,完整项目已经开源。

“爱影家”app源码开源地址:
开源仓库地址1:https://gitee.com/yyz116/hmmovie
开源仓库地址2: https://atomgit.com/csdn-qq8864/hmmovie

这里需要特别提醒的是,虽然“爱影家”是HarmonyOS应用,但是它同样可以安装在Openharmony系统中。只不过,如果应用使用了华为特有的HMS服务,那么在Openharmony上可能无法实现完整功能。除此之外,并没有太多区别。因此,我们可以放心大胆地尝试。

通过HDMI接口接到电视上,板子上装个蓝牙鼠标或者再配个蓝牙键盘,通过蓝牙鼠标可以远程控制操作,实际比遥控器还好使。
在这里插入图片描述
在这里插入图片描述

四、安装与调试

在Openharmony上安装应用同样需要经过签名验证。对于开发者来说,使用debug测试签名就可以满足日常开发和测试需求。

以下是一个示例配置,展示了如何在Openharmony开发板上配置和签名“爱影家”应用:

{ \"app\": { \"signingConfigs\": [ { \"name\": \"default\", \"type\": \"HarmonyOS\", \"material\": { \"certpath\": \"C:\\\\Users\\\\Administrator.DESKTOP-0V6PRM4\\\\.ohos\\\\config\\\\default_hmmovie_ARLGlSXwySApvXWgNGcS8jRpt1Lt3i6Tji8YYDvdzgQ=.cer\", \"storePassword\": \"0000001B4A758CC2DB18DC3479859EE6177D2C8265ECC2FF7B89B7BE89322200B573E73EEA339451B63563\", \"keyAlias\": \"debugKey\", \"keyPassword\": \"0000001B3F6399DCDA592596F5DD8A02FA7DB27B5DBEB4D71A011BC4432FCD82A5058432D180A2F294113D\", \"profile\": \"C:\\\\Users\\\\Administrator.DESKTOP-0V6PRM4\\\\.ohos\\\\config\\\\default_hmmovie_ARLGlSXwySApvXWgNGcS8jRpt1Lt3i6Tji8YYDvdzgQ=.p7b\", \"signAlg\": \"SHA256withECDSA\", \"storeFile\": \"C:\\\\Users\\\\Administrator.DESKTOP-0V6PRM4\\\\.ohos\\\\config\\\\default_hmmovie_ARLGlSXwySApvXWgNGcS8jRpt1Lt3i6Tji8YYDvdzgQ=.p12\" } } ], \"products\": [ { \"name\": \"default\", \"signingConfig\": \"default\", \"compatibleSdkVersion\": \"5.0.0(12)\", \"runtimeOS\": \"HarmonyOS\", \"buildOption\": { \"strictMode\": { \"caseSensitiveCheck\": true, \"useNormalizedOHMUrl\": true } } } ], \"buildModeSet\": [ { \"name\": \"debug\", }, { \"name\": \"release\" } ] }, \"modules\": [ { \"name\": \"entry\", \"srcPath\": \"./entry\", \"targets\": [ { \"name\": \"default\", \"applyToProducts\": [ \"default\" ] } ] } ]}

通过上述配置,我们可以轻松地将“爱影家”应用部署到OpenHarmony开发板上,享受完全自主的观影体验。

五、结束语

综上所述,利用最便宜的OpenHarmony 5.0标准系统开发板和配套的“爱影家”应用,我们完全可以打造出一个理想的电视盒子。还让我们在使用过程中感受到了完全自主可控的乐趣。如果你也像我一样,想尝试自己动手创造一些有趣的东西,那么不妨尝试一下,你可能会收获意想不到的惊喜。