【React Native】一款跨平台篮球APP-为爱好而生

一 前言

我本人是一个篮球迷,平时很喜欢打篮球也有关注与篮球相关的资讯,相信NBA对每个篮球迷来讲都是一个再熟悉不过在平台了。篮球是我的爱好,而编程是我的职业,我是一名移动开发者。业余时间想着自己捣鼓个应用,思来想去觉得就做个与篮球相关的吧,一来是自己对这方面稍微了解一些,二来是完成自己的一个念想。

二 项目背景

应用需要兼容目前最主要的两大移动平台:Android和IOS,而为每端独立开发一套代码,就人力来讲耗时太长,工作量也相对较大,所以就考虑使用跨平台技术来开发,选用由FaceBook开源的React Native,尝试一下非原生模式的开发体验。

三 技术栈选型

React Native

React Native框架旨在使用JavaScript代码构建一个跨平台APP,图3.1是RN底层设计原理框架图。

图3.1 RN底层设计原理架构图

JSX可以说是XML的JavaScript语法拓展,在React框架中可以跟JavaScript相互转换。直接操作DOM节点的效率太低,Virtual DOM运用Diff算法更新视图,它实际上是DOM节点在内存中的一种轻型表达方式,不同平台使用各自的渲染引擎来生成UI。在iOS上使用了内置的JavaScriptCore提供JS运行环境,而Android上则是采用Webkit.org官方开源的jsc.so。

Redux

APP的状态维护会随着功能的增加而变得艰难,在React中禁止直接操作DOM节点,不然会出现状态变化和异步结果混淆在一起,但是更新State仍然是个棘手的问题。而Redex的集成可以让应用中的数据流更加可控。

Redux的工作流程如下:当你想要更新状态时,你需要发送一个活动(Action),Action里面写清楚你想要干什么,当你把事情分清楚之后,需要写一个把每个Action和返回的新状态联系起来的函数,这种函数统我们称它为Reducer,它负责给应用中监听状态处返回新的状态。

Typescript

熟悉面向对象开发的开发者可以选择TypeScript作为开发语言,因为它具备面向对象语言的特性,React Native提供了插件用于将TypeScript转换成JavaScript。它提供了编译时的类型检查,可以尽可能地避免JavaScript语言运行时可能出现的‘undefined’问题。

四 开发环境搭建

① 使用 Git 进行代码管理;

② 本地开发环境搭建,参照 React Natie开发文档

③ IDE:Visial Studio Code、Android Studio(本人在应用开发过程仅在Android环境下进行了调试,IOS还没跑过,想要跑IOS在同学需要自己搭建开发环境,可能会一些兼容问题得处理。)

五 需求分析

了解了用户的实际需求之后,结合需求优先级、现有开发时间、技术实现难度等影响因素,我提取了将投入实际开发的APP的几大功能需求点,分别是:赛况模块、赛事推送、球队模块、新闻模块、社区模块、个人中心模块。图5.1是APP的功能模块图:

图5.1 功能模块图

六 APP效果图

  1. 登录模块
    登录操作会有用户账号的正则表达式校验,账号和密码的判空操作,如有问题会有相应的错误提示,错误提示如图6.1,6.2所示。
6.1 登录账号正则表达式检查

6.2 登录账号正则表达式检查
  1. 推送模块
    推送模块需要前后端合作才能让推送服务准确无误,APP端用户在个人中心进入“我的主队”入口,选择一支自己支持的球队,提交保存,后台接到球队标签之后根据实际赛事情况给用户推送通知。
    推送模块的主要测试点在于:对用户在“我的主队”页面的选择进行检验,由于目前只支持关注一支球队,所以需要对用户的选择结果加以校验并在有误操作的情况下给予提示;推送服务的准确性,对后台推送的赛事信息中的球队进行校验,看是否符合用户所预先选择的标签。推送通知测试情况如图6.3,6.4所示。
6.3 ‘我的主队’页面

6.4 通知中心查看赛事推送
  1. 赛况模块
    赛况页面的测试点主要在于赛事信息的准确性、详情页加载的稳定性、赛事搜索页面交互的稳定性和数据的准确性。
    赛事主页的赛事加载采用首次进入全量加载,因为每天的比赛场次基本不会超过15场,所以没有分页加载的必要,赛事搜索页也同样采用全量加载模式。不同的是,搜索页中假设用户输入的日期没有比赛数据,那应该吐司提示“所搜索日期无比赛”,而主页的比赛数据默认加载当前和前一天的数据,假设这两天没有数据,则会一次溯源到离当前日期最近的两天的比赛数据。页面的实现和测试结果如图6.5,6.6,6.7,6.8所示。
6.5 赛况首页

6.6 赛事详情页

6.7 赛事搜索页

6.8 赛事搜索页'无赛事’
  1. 球队模块
    球队模块的测试点主要在于球队信息和球员信息的准确性,球队主页的球队信息加载同样采用首次进入全量加载,球队详情页中分Tab加载球队基本数据和球员数据,进入详情页前的网络加载会有Spinner提示,网路加载成功后消失。页面测试情况如图6.9,6.10所示。
6.9 球队首页

6.10 球队详情页
  1. 新闻模块
    新闻模块的测试点主要在于新闻列表的分页加载,新闻首页采用首次进入加载10条新闻,接下来用户可以点击页面下方的“加载更多”,随即加载第二页的十条数据,依此类推,由于接口限制,分页数据最高可达4页。另一个测试点则是新闻详情页WebView加载网页的稳定性。页面测试情况如图6.11,6.12所示。
6.11 新闻首页

6.12 新闻详情页
  1. 社区模块
    社区模块的主要功能有评论提交、评论插入,出于激励机制,每当用户提交一个评论,用户的个人积分会累积10分。测试点主要在于提交评论后评论传输的可靠性、评论列表插入评论的准确性、累积积分数据的准确性。页面测试情况如图6.13,6.14所示。
6.13 社区详情页提交评论

6.14 社区详细内容和热门评论
  1. 个人中心模块
    个人中心模块主要测试点在于修改用户个人资料、提交意见反馈、评论APP、退出登录等二级页面的稳定性和可靠性。同时还有一个重要入口:“我的主队”,在此页面中用户可以选择自己支持和关注的一支球队。页面测试情况如图6.15,6.16所示。
6.15 个人中心首页

6.16 意见反馈提交页面

七 总体设计原理

客户端-服务器交互模式

APP数据来源于NBA的官方接口和第三方数据平台,客户端需要向接口的服务器发送请求,服务器识别请求并返回数据。当客户端监听到用户触发某个事件时,会发送出对应的Action事件,Action中会有对应的网络请求,由客户端发出一个携带URL、参数等信息的请求,服务器接收到请求之后访问数据库,然后予以响应,请求成果则返回相应的JSON数据,失败则返回错误码。客户端对返回的数据进行解析,并在相应的UI上给予展示。

基于Redux的客户端数据流

APP架构采用客户端常用设计模式MVC,基于Redux数据流,其主要流程如图7.1所示,界面UI与数据模型通过中间桥梁Action进行关联,状态的改变导致界面的更新,导致状态改变的事件由活动(Action)发起,在Reducer函数进行处置并返回新的状态。

7.1 基于Redux在客户端数据流图

推送服务

推送服务集成了第三方推送平台极光推送JPush,它供给可视化的web端控制台,可以用于发送推送通知、统计数据。本毕业设计课题中推送服务的主要目的是提前通知用户感兴趣的赛事信息,包括比赛的时间和对阵双方。

在实现上有两种方案可供选择,一个是本地通知,还有一个是远程通知。

本地通知的原理是这样的:用户在APP内选择了自己的“主队”之后,客户端将主队的标签以redux缓存保留在本地,一天内用户第一次打开APP的时候就会网络请求后两天的比赛数据,将本地的“主队”标签与网络请求返回的数据集进行比对,提取出与“主队”相关的赛事信息集合,保存在本地。接着,需要JS层与Native层的交互,自定义一个NativeModule接口,接口里面实现一个从JS层获取赛事信息集合的方法,赛事信息集合以方法参数的形式传递过来。最后,使用JPush提供的创建本地通知的API构建通知,设置通知的标题、内容、触发时间。最后,客户端在监听到通知之后就会弹出系统通知框提醒用户相关的赛事信息。

远程通知的原理是这样的:使用JPush官网提供的服务端SDK在本地集成一个推送服务项目,用户在APP内选择了自己的“主队”之后,客户端将主队的标签以redux缓存保留在本地,与此同时,客户端调用react-native-jpush提供的接口JPushModule.setAlias(alias, successCallback)将标签发送给服务器SDK,后台接收到标签之后会保存起来。每天凌晨零点,推送后台会发送一个访问NBA赛事接口的请求,获取当天的比赛数据集合,然后与“主队”标签做比对,提取出用户关注的球队的赛事信息存放到集合里面。接着,服务器调用JPush服务端SDK提供的接口JPushClient.createSingleSchedule(name,time,PushPayload)设置定时推送通知任务,推送的用户根据就是客户端提交上来的别名,在比赛开始前半个小时会给用户设备发送推送通知,客户端在监听到通知之后就会弹出系统通知框提醒用户相关的赛事信息。

综合比较两种方案,第二种方案更具可行性。第一种方案受网络因素限制较大,请求比赛数据集合和推送通知都放在客户端进行处理,受用户使用APP情况的影响较大,比如用户今天没有打开APP的话,就无法请求数据,通知推送就无法进行。相比之下,将获取用户关注的球队的赛事信息和推送通知的实现放在服务端来做的话,就可以不受用户操作APP情况的影响,客户端只需要监听通知,这个方案更加准确可靠。

推送服务方案二的原理图如图7.2所示:

Android系统上,推送进程会作为服务在后台运行,创建并操持与服务器的长连接,服务端则是调用JPush REST 提供的API来实现自定制的推送服务。

7.2 推送服务方案二原理图

九 所遇问题汇总

QA集合列举如下:

  1. 运行Android工程失败

    Q:
    命令行下运行 react-native run-android 
    ERROR  EPERM: operation not permitted, 
    lstat 'F:\VSCodeWorkSpace\RN\basketballWorkflow\android\app\
    
    A:在AS(已在项目的Android目录下)运行  gradlew clean ,
    重启服务react-native start,
    然后重新run。
    
  2. React Navigaion:导航对象没有根页面

    Q:Please specify at last one route when you configuring the navigator
    
    A:新建 TabNavigator / StackNavigator 对象的时候,不可没有根页面。
    
  3. 更新Typescript

    Q:每次更改文件之后都需要手动执行 tsc,
    待typescript转成javaScript之后,
    才可以执行 react-native run-android
    
    A:开一个终端,运行 tsc -w,
    即可自动检测更改并且转换,
    无需手动执行 tsc
    
  4. 开启Hot Reloading动态更新布局

    Q:调试页面布局的时候,需要手动执行 react-native run-android 或者 摇动手机 Reloading,
    麻烦且耗时
    
    A:摇一摇手机,开启手机的 Enable Hot Reloading
    
  5. React Navigation:如何给React Navigator的headerRight添加监听触摸事件

    Q:如何给React Navigator的headerRight添加监听触摸事件
    
    A:代码例子如下:
    static navigationOptions = ({navigation, screenProps}) => ({  
            headerTitle: '登录',  
            headerLeft:(  
                <Text  onPress={()=>navigation.state.params.navigatePress()} style={{marginLeft:5, width:30, textAlign:"center"}} >  
                    <Icon  name='ios-arrow-back'size={24} color='white' />  
                </Text>  
            )  
        });  
      
        _onBackAndroid=()=>{  
            alert('点击headerLeft');  
        }  
          
        componentDidMount(){  
            //在static中使用this方法  
            this.props.navigation.setParams({ navigatePress:this._onBackAndroid })  
        }  
    
  6. 打印日志

    Q:如何在控制台上打印日志(Windows上)
    
    A:打开终端,输入  adb logcat *:S ReactNative:V ReactNativeJS:V
    
  7. 关闭黄色的WarningBox

    Q:
    Warning: isMounted(...) is deprecated in plain JavaScript React classes.
    Instead, make sure to clean up subscriptions and 
    pending requests in componentWillUnmount to prevent memory leaks.
    
    A:
    在index.js中添加忽略这个warning的代码:
    YellowBox.ignoreWarnings(['Warning: GiftedListView:  isMounted is deprecated.'])
    

十 React Native开发心得

  • React Native “Write once,run everywhere”的设计理念能让开发者以比较小的学习成本来开发出一个跨平台的应用软件,很适合数据流式的App,使用Redux能够很好地管理数据状态;
  • 对于有比较重的需求的软件,比如需要大量使用地图,不建议使用RN开发,一个是性能问题没有原生的好,还有就是第三方地图库对RN的支持不那么完善,很多时候需要自己填坑;
  • 使用RN写UI时,需要关注页面重复渲染的问题,组件的更新在底层使用了diff算法,在Props或者State发生变化时,就会引起组件的重新渲染,当然这也可以通过shouldComponentUpdate生命周期来加以控制,或者让组件继承PureComponent。可能在渲染数据量较小的时候对性能的影响不那么明显,但是还是得养成在写代码过程中关注性能的好习惯;
  • 对于RN能否缩短开发周期,提高开发效率的问题,只能说凡事有利有弊,使用RN开发UI的效率高了,但是在解决一些特定平台问题上面花的时间可能会比较多,因为有些问题只能放在Native去解决,也就意味着需要Android和iOS各自开发一套,然后还要加上Naitve与JS交互的代码。

后言
关于APP的介绍这么多了,有什么不对的地方还望多多指教,也欢迎大家关注我(简书/GitHub
谢谢观看此文。
欢迎访问 源代码地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,227评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,755评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,899评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,257评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,617评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,757评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,982评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,715评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,454评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,666评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,148评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,512评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,156评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,112评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,896评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,809评论 2 279
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,691评论 2 272

推荐阅读更多精彩内容