结对编程项目

软件工程 这就是链接
作业要求 这就是链接
作业目标 熟悉在未结对情况下如何结对开发项目

Github与合作者

合作者(学号):

  • 区德明:318005422
  • 虚左以待

Github链接:

https://github.com/DMingOu/CalculateExercise

由于本来的合作者临时有事不能和我一起结对编程,所以我也只好咬咬牙单刀赴会了。

因为本次的题目要求有图形化界面,而我也学过移动开发的,所以一不做二不休,直接提刀杀进AndroidStudio,开始秃头设计一款小学生也能用的四则计算题练习工具App,一来可以简化方便操作和展示,二来也算是在众多.exe中里面可以独树一帜。

一、PSP

PSP2.1 Personal Software Process Stages 预估耗时 (分钟) 实际耗时 (分钟)
Planning 计划 25 38
· Estimate · 估计这个任务需要多少时间 25 38
Development 开发 945 830
· Analysis · 需求分析 (包括学习新技术) 75 130
· Design Spec · 生成设计文档 100 80
· Design Review · 设计复审 60 35
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 60
· Design · 具体设计 120 110
· Coding · 具体编码 260 260
· Code Review · 代码复审 60 45
· Test · 测试 (自我测试,修改代码,提交修改) 95 110
Reporting 报告 120 105
· Test Repor · 测试报告 60 50
· Size Measurement · 计算工作量 30 30
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 25
· 合计 1090 973

二、四则运算练习工具APP需求分析

需求描述 是否实现
控制生成题目的个数
控制题目中数值范围
计算过程不能产生负数,除法的结果必须是真分数,题目不能重复,运算符不能超过3个
显示题目
显示答案
能支持一万道题目的生成
判定答案中的对错并进行数量统计
显示得分结果
具有图形化的操作界面

三、APP开发计划

功能 描述 开发者 进度
生成题目 随机生成操作数和运算符,组成有效的四则运算表达式 区德明 完成
计算结果 根据生成的表达式,计算生成正确的结果 区德明 完成
练习报告 输出成绩结果 区德明 完成
UI界面设计 设计软件的界面设计XML 区德明 完成
UI界面实现 使用Kotlin语言实现 Andoid APP 区德明 完成
功能测试与故障修复 测试程序的功能,修复出现的故障 区德明 完成
性能分析与优化 分析程序执行的性能,优化性能表现 区德明 完成

结构图

四、方案

4.1 生成题目的算法设计思路

线程池多线程并发,创建指定数量的题目,并利用Set集合进行去重操作。

核心代码如下:

执行生成题目的线程任务类

    /**
* 执行生成指定数量题目任务
* 线程类
*/
private class GenerateWorker(
private val exerciseNum: Int, //生成数量
private val numRange: Int, //范围上限
private val workerIndex: Int, //工作线程的编号
private val countDownLatch: CountDownLatch,
private val tempList : MutableList<Exercise>
) : Runnable {
override fun run() {
try {
val start = System.currentTimeMillis()
val exerciseList = generateExercises(
exerciseNum, numRange
)
//将 去重但是未编号的 列表 加入缓冲列表中
tempList.addAll(exerciseList)
countDownLatch.countDown()
} catch (e: Exception) {
e.printStackTrace()
}
}
}

执行完善收尾操作的线程任务:

    /**
* 用于补充题目序号, 完善,的任务
*/
private class SupplyWorker(
private val exerciseNum: Int,
private val countDownLatch: CountDownLatch,
private val tempList : MutableList<Exercise> ,
private var startIndex : Int ,
private val mHandler: QuestionListHandler //回调UI线程
) : Runnable {
override fun run() {
getExerciseOnCount(exerciseNum)
countDownLatch.countDown()
//根据已有的列表数据,进行编号
for(exercise in tempList) {
startIndex++
exercise.number = startIndex
}
updateGlobalExerciseList(tempList)
//通知 View层更新内容
val message = Message()
message.what = 666
mHandler.dispatchMessage(message)
}
}

提供给外界调用的入口:

fun produce(exerciseNumber: Int, numberRange: Int,startIndex : Int, handler: QuestionListHandler) {
if (exerciseNumber == 0 || numberRange == 0) {
return
}
//最小范围是2
if (numberRange < 2) {
throw RuntimeException("范围上限不可以少于2")
}
//手工计时器启动
start = System.currentTimeMillis() //设置本次加载的起始序号
this.startIndex = startIndex //每个线程的工作量
val workload = 25
//记录线程编号
var index = 1
//剩余工作量
var remainWorkload = exerciseNumber
//启动创建题目的线程组
val threadCount = if (exerciseNumber % workload == 0)
exerciseNumber / workload
else
exerciseNumber / workload + 1
//生成线程+一个输出线程
countDownLatch = CountDownLatch(threadCount + 1) //任务正式启动前。清空缓存列表,避免脏数据
tempExerciseList.clear() while (true) {
//执行生成题目的任务线程,编号对应
remainWorkload -= if (remainWorkload > workload) {
execute(
GenerateWorker(
workload,
numberRange,
index++,
countDownLatch,
tempExerciseList
)
)
workload
} else {
execute(
GenerateWorker(
remainWorkload,
numberRange,
index,
countDownLatch,
tempExerciseList
)
)
break
}
}
//启动完善每一道题的任务
execute(SupplyWorker(exerciseNumber, countDownLatch ,tempExerciseList ,startIndex , handler))
//数据创建完毕,重置状态
countDownLatch.await()
clear()
}

生成题目的操作,实际执行:

    @JvmStatic
fun generateExercises(exercisesNum: Int, numRange: Int , startIndex : Int = -1): List<Exercise> {
//控制编号
var index = startIndex
//如果没有传入编号则视为生成时不做编号处理
val isSetIndex = index!=-1
//控制生成循环的结束
var count = 0
val exerciseList: MutableList<Exercise> = ArrayList()
while (count < exercisesNum) {
val exercise = generateQuestion(numRange)
generateAnswer(exercise)
//有效题目加入List
if (validate(exercise)) {
count++
if(isSetIndex) {
index++
//设置序号
exercise.number = index
}
//生成可以输出的题目样式
EXERCISE_QUEUE.add(exercise)
exerciseList.add(exercise)
//放入去重Set,用作判断
exercisesSet.add(exercise.simplestFormatQuestion)
}
}
return exerciseList
}

4.2 客户端(用户入口)的设计思路

采用单Activity+多Fragment的架构。

  • util包提供算法和数据源的能力
  • View包承载页面UI的显示,
  • bean包存放实体类信息
  • widget包存放的是一些自定义控件

五、效能分析

通过大厂滴滴的开源性能工具平台,DoKit,接入App,进行性能的进一步分析,如下:

5.1 程序效能

使用APP的出题功能,分别生成1000道,2000道,10000道题目不同的消耗内存情况

  • 生成1000道题目时,只需要130MB
  • 生成2000道题目时,只需要155MB
  • 生成10000道题目的时候,就需要1005MB了

可以认为题目的数量影响着App的运行时所消耗的资源及内存,并随着题目的数量成正比例关系。

cpu消耗如下:

页面打开跳转的时长:

5.2 性能优化

首轮优化

可以看出线程池的线程配置对于大数据量非常重要,从生成10000条题目为例子,在4个线程在并发的创建对象的时候,因为设置的单个线程任务量为2000,无法完成剩下的2000任务,只能等4个线程任务都完成后再创建新线程执行任务。

解决方案:调整线程池的配置,提高线程的数量,提高每个生成题目的线程的任务数量
优化后的结果:

线程池复用线程,分页加载50条数据,逐步的加载出全部的数据,分散内存和CPU的密集消耗。

优化后的生成10000条数据,耗时从10秒提高到了7秒,提升了30%。如果继续对线程池的配置进行优化,效率还会继续提高。

进一步优化

但是对于移动应用来说,加载时间的耗时始终是大问题,让用户觉得等待时间过长,同时会有卡顿的问题,亟待解决,所以下面要通过性能分析工具,分析哪个步骤流程才是在生成题目列表的过程中造成卡顿呢??通过DoKit的卡顿堆栈分析图:

可以看出卡顿 与 打开页面,创建对应数据,数据创建后,列表UI开始加载数据, 频繁大量地创建View对象息息相关。

进一步解决方案:分页加载

数据量过大的时候,其实不需要一口气全部加载进去,可以采用分页加载的方式加载与创建对象,吞吐量减少的同时CPU和内存的占用也降低了。从另一个角度讲,页面也无必要一口气加载上千条乃至万条的数据 ( 屏幕就这么大 ,用户看不过来)

设定分页加载题目的方式,每页固定数量为 50 ,若所需数量不足50条,则继续加载所需的数量题目。

打开性能检测平台的监控 CPU,内存 ,帧率

未加载数据前:

打开页面,加载10000条数据:

可以看出,优化后,加载大数据,对页面的影响小了很多,CPU和内存的短时间消耗急剧降低,对App这种只拥有固定内存(少量)的进程,可以有很不错的效果。

六、APP真机测试报告

6.1 测试1:程序生成四则计算题和答案是否符合题目要求

结果说明:

以上为测试生成100道数值10以内的题目的截图

可以得出结论,看到题目是符合去重的要求且答案是正确的

6.2 测试2:程序是否能正确判断用户输入答案的正确性

以 6 道题目为例子,查看练习情况报告,可以看到可以给出正确答案提示,以及完成的情况,错对情况,得分率,很详细。

6.3 测试3:能否支持一万道题目的生成与显示

在出题模式下执行生成10000道数值范围在5以内的题目的功能

最终效果

首页 题目列表
练习做题 练习情况报告

七、总结

项目小结

获得的经验:

虽说只是做了一个小工程,从中也学习到了多线程的相关知识,数据结构的使用和APP界面的设计与布局。

不足的地方:

没有做查看历史做题的历史记录。

生成大批量的题目的时候,会随着页面加载越来越多的题目,随着RecyclerView持有的子itemView数量逐渐增多会导致分页加载,RecyclerView时会有卡顿感。

结对感受

期待ing,个人感觉是各司其职,对方请教时再给建议,要结合实际情况,不过分追求效果。

这就是小学生也会用的四则计算练习APP吗?- by软工结对编程项目作业的更多相关文章

  1. 3.结对编程成果报告(小学生四则运算的出题程序,Java实现)

    程序名称:小学生四则运算的出题程序 先附上代码: package com.makequestion; import java.text.DecimalFormat;import java.util.R ...

  2. 软工作业NO.2小学生线上杨永信——四则运算题目生成

    项目题目:实现一个自动生成小学四则运算题目的命令行程序 github地址:https://github.com/a249970271/Formula 驾驶员:梁沛诗 副驾驶:曾祎祺 项目说明 自然数: ...

  3. 【BUAA软工】结对编程作业

    项目 内容 课程:2020春季软件工程课程博客作业(罗杰,任健) 博客园班级链接 作业:BUAA软件工程结对编程项目作业 作业要求 课程目标 学习大规模软件开发的技巧与方法,锻炼开发能力 作业目标 完 ...

  4. 【Beta阶段】展示博客

    Beta阶段展示博客 blog software buaa 1.团队成员简介 Email:qianlxc@126.com Free time:8:00 7:00 a.m ~ 11:00 12:00p. ...

  5. [2017BUAA软工助教]第0次作业小结

    BUAA软工第0次作业小结 零.题目 作业链接: This is a hyperlink 一.评分规则 本次作业满分10分: 按时提交有分 一周内补交得0分 超过一周不交或抄袭倒扣全部分数 评分规则如 ...

  6. [2017BUAA软工助教]个人项目小结

    2017BUAA个人项目小结 一.作业链接 http://www.cnblogs.com/jiel/p/7545780.html 二.评分细则 0.注意事项 按时间完成并提交--正常评分 晚交一周以内 ...

  7. 【个人项目总结】C#四则运算表达式生成程序

    S1&2.个人项目时间估算 PSP表格如下: PSP2.1 Personal Software Process Stages Time(Before) Time(After) Planning ...

  8. [BUAA软工]第二次博客作业---结对编程

    [BUAA软工]结对作业 项目 内容 这个作业属于哪个课程 北航软工 这个作业的要求在哪里 2019年软件工程基础-结对项目作业 我在这个课程的目标是 学习如何以团队的形式开发软件,提升个人软件开发能 ...

  9. [BUAA软工]第一次博客作业---阅读《构建之法》

    [BUAA软工]第一次博客作业 项目 内容 这个作业属于哪个课程 北航软工 这个作业的要求在哪里 第1次个人作业 我在这个课程的目标是 学习如何以团队的形式开发软件,提升个人软件开发能力 这个作业在哪 ...

随机推荐

  1. 实操ES6之Promise

    箭头函数和this 写Promise的时候,自然而然会使用箭头函数的编写方式.箭头函数就是.Neter们熟知的lambda函数,已经被大部分主流语言支持,也受到了广大码农的交口称赞,但是Jser们却会 ...

  2. 想要使用GPU进行加速?那你必须事先了解CUDA和cuDNN

    这一期我们来介绍如何在Windows上安装CUDA,使得对图像数据处理的速度大大加快,在正式的下载与安装之前,首先一起学习一下预导知识,让大家知道为什么使用GPU可以加速对图像的处理和计算,以及自己的 ...

  3. shell小技巧(6)修改一批文件后缀

    当前目录下后缀为sh的文件,改为后缀shell. 这里列出两种方法,先看第一种.方法1:#!/bin/bash str=`find ./ -name \*.sh`  # 会产生一个列表 file=&q ...

  4. 原生JDK网络编程- NIO之Reactor模式

    “反应”器名字中”反应“的由来: “反应”即“倒置”,“控制逆转”,具体事件处理程序不调用反应器,而向反应器注册一个事件处理器,表示自己对某些事件感兴趣,有时间来了,具体事件处理程序通过事件处理器对某 ...

  5. 【API进阶之路】用API打造一条自动化内容生产流水线

    摘要:搞定了内容审核之后,我又把抓取工具.内容审核API.文本摘要生成API串联在一起,从抓到审再到编,建立了一条自动化的内容生产流水线,编辑团队只需要做优质内容的推荐就可以了. 上周,运营部将官网上 ...

  6. JS语法_集合

    数组方法 forEach // no-log Array.prototype.forEach_ = function (cb) { let len = this.length for (let i = ...

  7. python中使用token模拟登录

    背景:在接口测试中我们经常是需要一个登陆token,或者获取其他用到的参数来关联下一个接口用到的参数. Token的意义及用法 一.Token的来源: 当客户端多次向服务端请求数据时,服务端就需要多次 ...

  8. mysql之慢日志查询

    转自https://my.oschina.net/wuweixiang/blog/2987434 首先得配置my.cnf: #===================================== ...

  9. spring mvc(1) 为什么要使用mvc

    在使用spring mvc之前,我们首先要理解我们为什么要使用spring mvc.关于这个问题我们可以看一下java web的简单发展过程. 1. servlet 开发阶段 上世纪90年代,随着In ...

  10. HTML+CSS使用swiper快速生成最简单、最快捷、最易看懂的轮播图

    1.  在网页顶部输入swiper.com.con,进入swiper官网 2.   点击" API文档",获取轮播图代码的地方 3.   点击左侧"swiper初始化&q ...