《移动互联网技术》第十一章 Android应用工程案例: 掌握Android系统的需求分析和设计以及 Android项目的程序测试和版本管理方法

简介: 《移动互联网技术》第十一章 Android应用工程案例: 掌握Android系统的需求分析和设计以及 Android项目的程序测试和版本管理方法

🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁

🦄 个人主页——libin9iOak的博客🎐
🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺
🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐
🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批评指正!🍁🐥


《移动互联网技术》课程简介

《移动互联网技术》课程是软件工程、电子信息等专业的专业课,主要介绍移动互联网系统及应用开发技术。课程内容主要包括移动互联网概述、无线网络技术、无线定位技术、Android应用开发和移动应用项目实践等五个部分。移动互联网概述主要介绍移动互联网的概况和发展,以及移动计算的特点。无线网络技术部分主要介绍移动通信网络(包括2G/3G/4G/5G技术)、无线传感器网络、Ad hoc网络、各种移动通信协议,以及移动IP技术。无线定位技术部分主要介绍无线定位的基本原理、定位方法、定位业务、数据采集等相关技术。Android应用开发部分主要介绍移动应用的开发环境、应用开发框架和各种功能组件以及常用的开发工具。移动应用项目实践部分主要介绍移动应用开发过程、移动应用客户端开发、以及应用开发实例。

课程的教学培养目标如下:

1.培养学生综合运用多门课程知识以解决工程领域问题的能力,能够理解各种移动通信方法,完成移动定位算法的设计。

2.培养学生移动应用编程能力,能够编写Andorid应用的主要功能模块,并掌握移动应用的开发流程。

3. 培养工程实践能力和创新能力。

 通过本课程的学习应达到以下目的:

1.掌握移动互联网的基本概念和原理;

2.掌握移动应用系统的设计原则;

3.掌握Android应用软件的基本编程方法;

4.能正确使用常用的移动应用开发工具和测试工具。

第十一章 工程案例

本章小结:

1**、本单元学习目的**

通过学习Android应用的界面设计、数据库设计和菜单设计,掌握设计一个复杂应用系统的方法。重点掌握程序测试、版本控制以及应用发布等技术。

2**、本单元学习要求**

(1) 掌握Android系统的需求分析和设计;

(2) 掌握Android项目的程序测试和版本管理方法。

3**、本单元学习方法**

结合教材以及Android Studio开发平台,构建一个APP 系统,通过编程练习,运行调试,深入理解Android软件应用开发流程和开发方法。

4**、本单元重点难点分析**

重点

(1)系统设计

用户界面设计是移动设备的重要组成部分,它涉及认知心理学、设计学、语言学等内容,是一个复杂且融合多个学科知识的工程。手机界面是用户与设备沟通的唯一途径,因此界面要能够为用户提供方便、有效的服务。

首先,实现日记卡片列表,活动的布局采用了协调布局(CoordinatorLayout),协调其子view,并以触摸影响布局的形式,从而产生动画效果,其典型的子View包括:悬浮按钮,SnackBar。在其中,又加入了一个刷新布局(SwipeRefreshLayout布局),它用来下拉时,刷新列表控件,显示其他日记卡片。在SwipeRefreshLayout布局中,采用RecyclerView来列出各个日记卡片。

<android.support.design.widget.CoordinatorLayout
   android:layout_width="match_parent"
   android:layout_height="match_parent">
   <android.support.v4.widget.SwipeRefreshLayout
     android:id="@+id/swipe_refresh_diary"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     app:layout_behavior="@string/appbar_scrolling_view_behavior">
     <android.support.v7.widget.RecyclerView
       android:id="@+id/recycler_view_diary"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />
   </android.support.v4.widget.SwipeRefreshLayout>
<android.support.design.widget.CoordinatorLayout
   android:layout_width=“match_parent”
   android:layout_height=“match_parent”>
   … …
   <android.support.design.widget.FloatingActionButton
     android:id="@+id/btn_float_diary"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_gravity="bottom|end"
     android:layout_margin="16dp"
     android:src="@drawable/ic_done" />
 </android.support.design.widget.CoordinatorLayout>

创建CardActivity,将日记的测试数据加入diaries数组。每一个Dairy对象包括:日期、标题和图片ID号。为了测试卡片列表,增加了一个日记列表对象diaryList,随机生成更多的日记卡片。

public class CardActivity extends AppCompatActivity {

private Diary[] diaries = {
   new Diary("日记 2017.7.10", "嗨,大家好\uD83D\uDE0A",

R.drawable.flower),

new Diary(“日记 2017.9.12”, “今天很开心\uD83D\uDE00”,

R.drawable.lake),

… …

private List diaryList = new ArrayList<>();

private DiaryAdapter adapter;

private SwipeRefreshLayout swipeRefresh;

@Override

protected void onCreate(Bundle savedInstanceState) {

… …

Toolbar toolbar = findViewById(R.id.toolbar); *

* setSupportActionBar(toolbar);

FloatingActionButton fab = findViewById(R.id.btn_float_diary);

fab.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

Snackbar.make(view, “删除数据”, Snackbar.LENGTH_SHORT)

.setAction(“撤销”, new View.OnClickListener() {

@Override

public void onClick(View v) {

Toast.makeText(CardActivity.this,

“数据恢复”, Toast.LENGTH_SHORT).show();

}

}).show();}

});

在onCreate函数中,Toolbar 通过 setSupportActionBar(toolbar) 被修饰成actionbar;接着,编写悬浮按钮事件监听器。在用户按下时,用snackbar显示信息,调用make函数时,它的第一个参数必须是协调布局,否则,弹出的Snackbar将覆盖悬浮按钮控件。

(2)数据库设计

日记应用中的所有数据(比如,测试题目和日记),通过构建的自定义ORM(对象关系数据映射)工具,保存在数据库中,并且实现数据库的各种操作;为了测试,创建一个数据库活动,在界面上加入了一个“添加数据”按钮。通过orm对象,可以直接将日记对象插入数据库。采用ORM框架之后,程序不再直接访问底层数据库,而是以面向对象的方式来操作持久化对象(例如:创建,修改,删除等),而ORM框架将这些面向对象的操作转化成底层的SQL操作。

构造一个数据库工厂类DatabaseFactory,采用单例模式来获取DatabaseFactory对象。接下来,在构造函数中,先设置数据库保存路径和数据库对象,然后创建数据库(或打开已有的数据库)。

public class DatabaseFactory {

private static final DatabaseFactory instance =

new DatabaseFactory();

public static DatabaseFactory getInstance() {

return instance;

}

private SQLiteDatabase database;

private String databasePath;

private DatabaseFactory() {

databasePath =“data/data/pers.cnzdy.tutorial/Exam.db”;

database = SQLiteDatabase.openOrCreateDatabase(

databasePath, null);

}

public DatabaseORM getBaseDao(Class entityClass) {

DatabaseORM orm = null;

try {
       orm = DatabaseORM.class.newInstance();
       orm.init(database, entityClass);
      } catch (InstantiationException e) {
       e.printStackTrace();
     } catch (IllegalAccessException e) {
       e.printStackTrace();
     }
  return orm;
}

数据库工厂类中只有一个函数:getBaseDao函数,用来创建DatabaseORM对象,应用中所有对象与数据库的操作都由DatabaseORM对象来完成,下面介绍DatabaseORM类。

(3)菜单设计

在界面上,菜单选项不显示在主屏幕上,而是通过滑动的方式将隐藏的菜单显示出来。滑动菜单只在需要的时候显示,节省了屏幕空间。实现滑动菜单需要用到DrawerLayout布局。DrawerLayout分为侧边菜单和主内容区两部分,侧边菜单提供滑动的展开与隐藏功能;主内容区用来设置菜单项,比如用ListView显示菜单项,它由开发者实现。

在DrawerLayout中放置两个控件。第一个控件是Toolbar,它放在FrameLayout布局中,作为主屏幕中显示的内容(主内容区)。第二个控件放置一个NavigationView控件,作为滑动菜单(侧边菜单)显示的内容,当然你也可以放置其他控件。注意:主内容区的布局代码要放在侧滑菜单布局代码的前面,以便DrawerLayout能够判断哪个控件是侧滑菜单,哪个控件是主内容区。

在侧边菜单中使用系统提供的侧边栏控件NavigationView来显示更丰富的菜单信息。NavigationView是Design Support库中提供的一个控件。在设置侧边菜单时,要注意设置控件的layout_gravity属性,也就是必须告诉DrawerLayout滑动菜单是在屏幕的左边还是右边,指定left表示在左边,指定right表示在右边,如果指定了start,表示根据系统语言自动判断。英语、汉语等从左到右显示的语言,滑动菜单在左边;阿拉伯语等从右到左的语言,滑动菜单就在右边。headerLayout用来设置头部信息的布局,它可以根据需要进行定制。slide_menu显示滑动菜单上的列表(menu)。headerLayout用来设置头部信息的布局,它可以根据需要进行定制。在headerLayout中放置头像、用户昵称和邮箱地址三项内容。将头像图片存放在drawable目录下。接下来,添加CircleImageView和TextView控件,用来显示头像、用户昵称和邮箱地址。在这里引用了一个开源项目CircleImageView,用它来显示圆形图片,注意将CircleImageView库引入到项目中。打开app/build.gradle文件,在dependencies闭包中该库。

创建menu资源文件来显示头部下面的菜单列表。选择多张图片作为菜单选项的图标,并将它们放在drawable目录下。在res\menu文件夹上点击鼠标右键,选择New→Menu resource file,创建菜单列表文件slide_menu.xml,并增加多个菜单选项。

标签嵌套了组标签,group表示一个组。将group的checkableBehavior 属性指定为single,它表示组中的所有菜单项只能单选。菜单列表中一共有6个菜单项,分别指定它们的android:id 属性、android:icon属性(菜单项的图标)和android:title属性(即菜单项显示的文字)。

public class SlideMenuActivity extends AppCompatActivity {

private DrawerLayout slideMenuDrawerLayout;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_material_design);

Toolbar toolbar = findViewById(R.id.*toolbar*);
     setSupportActionBar(toolbar);

… …

}

通常情况下滑动菜单隐藏起来,为了让用户知道应用软件有滑动菜单功能,需要提供一个提示让用户知道。调用actionBar的setHomeAsUpIndicator函数,在界面上,通过ActionBar的导航按钮来提示用户。ActionBar本身由Toolbar实现,最左侧的图标就是用来提示用户的导航按钮(即:应用有滑动菜单)。这个按钮称为“HomeAsUp”,用户点击这个图标,就会显示出滑动菜单界面。

public class SlideMenuActivity extends AppCompatActivity {

… …

ActionBar actionBar = getSupportActionBar();

if (actionBar != null) {

actionBar.setDisplayHomeAsUpEnabled(true);

actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_bird);

}

menu、headerLayout和NavigationView设置好以后,在SlideMenuActivity中添加NavigationView控件代码。首先获取NavigationView的实例,然后调用它的setCheckedItem函数将”照片”菜单项设置为默认选中。接着调用setNavigationItemSelectedListener函数来设置菜单项选中事件监听器。当用户点击了菜单项时,就会回调onNavigationItemSelected函数。在这个函数中实现相应的逻辑处理。目前代码中只调用了DrawerLayout的closeDrawers函数来关闭菜单。

slideMenuDrawerLayout = findViewById(

R.id.slide_menu_drawer_layout);

NavigationView navView = findViewById(R.id.nav_view);

navView.setCheckedItem(R.id.slide_photo);

navView.setNavigationItemSelectedListener(

new NavigationView.OnNavigationItemSelectedListener() {

@Override

public boolean onNavigationItemSelected(MenuItem item) {

slideMenuDrawerLayout.closeDrawers();

return true;

}

});

在onCreateOptionsMenu函数中,给toolbar加入菜单;而onOptionsItemSelected函数,触发toolbar上的菜单,当点击左边的图标home时,调用抽屉布局的openDrawer函数,展开侧边菜单。其他菜单项,没有实现具体的功能仅用Toast来做演示。

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.toolbar, menu);

return true;

}

@Override

public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case android.R.id.home:

slideMenuDrawerLayout.openDrawer(GravityCompat.START);

break;

case R.id.search:

Toast.makeText(this, “搜索”, Toast.LENGTH_SHORT).show();

break;

… …

作为Material Design的一种设计,滑动菜单为移动应用的开发者提供了很好的设计理念。只要遵循Material Design的各种规范和建议来构造应用系统,最终将创建统一、美观的应用界面。工具栏和滑动菜单上的菜单项,仅实现了简单的演示功能,结合前面的知识,完善这些菜单项的功能。

(4)单元测试

单元测试周期性对项目进行函数级别的测试,在良好的覆盖率下,能够持续维护代码逻辑,从而支持项目从容应对快速的版本更新。单元测试是参与项目开发的工程师在项目代码之外建立的白盒测试工程,用于执行项目中的目标函数并验证其状态或者结果,其中,单元指的是测试的最小模块,通常指函数。单元测试代码能够检测目标代码的正确性,打包时单元测试的代码不会被编译进入APK中。

在传统的软件开发中,测试是在Build以后执行,来验证代码的正确性,而以测试驱动开发的方式,则将测试提前,在软件设计以后,不是直接编写代码,而是先编写测试代码,然后基于测试代码来实现功能代码。

根据要实现的功能,需要根据要测试的函数设计测试用例,比如:实现min函数,它的输入参数是一个列表数据结构。根据基本的测试原则,要考虑几点:位置无关、元素个数、特殊的元素,比如0和负数、以及相等的元素等等。

在Android Studio中,androidTest是Android单元测试,执行测试的时候需要Android连接设备,速度比较慢,适合需要调用Android api的单元测试;test是JUnit单元测试,用来执行不需要Android依赖的单元测试类,速度快,适合只是对java代码功能进行单元测试。

首先,设计一个计算类,它包括一个加法函数sum,接着编写Computation类的测试代码,用JUnit单元测试来验证sum函数。

public class Computation {

public Computation() {

} 
public int sum(int num1, int num2) {
   return num1 + num2;
}

}

public class ComputationTest {

Computation comp;

@Before

public void SetUp() {

comp = new Computation();

}

@Test

public void addition_isCorrect() {

assertEquals(4, 2 + 2);

}

创建ComputationTest类后,在Setup函数中创建comp对象,注解@Before表示在测试方法之前执行;addition_isCorrect是一个测试方法,@Test注解表示这是一个测试方法,assertEquals是测试断言,用来判断测试是否成功,它的第一个参数预期值,第二个参数是真实值,如果两者相等,则测试成功,否则失败。

Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。

Mock 最大的功能是帮你把单元测试的耦合分解开,如果代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。

Mock 对象使用范畴包括:1)真实对象具有不可确定的行为,产生不可预测的效果(如:股票行情,天气预报);2)真实对象很难被创建;3)真实对象的某些行为很难被触发;4)真实对象实际上还不存在的等等。

使用 Mock 对象测试的关键步骤包括:

  1. 使用一个接口来描述这个对象;
  2. 在产品代码中实现这个接口;
  3. 在测试代码中实现这个接口;
  4. 在被测试代码中只是通过接口来引用对象,所以它不知道这个引用的对象是真实对象,还是 Mock 对象。

public interface IMathUtils {

public int abs(int num); // 求绝对值

}

import static org.mockito.Mockito.mock;

import static org.mockito.Mockito.when;

public class MockTest {

public static void main(String[] args) {

IMathUtils mathUtils = mock(IMathUtils.class); // 生成mock对象

when(mathUtils.abs(-1)).thenReturn(1); // 当调用abs(-1)时,返回1

int abs = mathUtils.abs(-1); // 输出结果 1

Assert.assertEquals(abs, 1);// 测试通过

}

}

IMathUtils是一个接口,没有实现,用Mockito框架mock之后,IMathUtils.abs(-1)就有返回值1了。Mockito代理了IMathUtils.abs(num)的行为,只要调用时符合指定参数(代码中指定参数-1),就可以得到映射的返回值。

public class Calculater {

public double divide(int a, int b) {

// 检测被除数是否为0

if (MathUtils.checkZero(b)) {

throw new RuntimeException(“dividend is zero”);

}

return (double) a / b;

}

}

public class MathUtils {

public static boolean checkZero(int num) {

return num == 0;

}

}

divide(a,b)计算a除以b,但被除数b不应该为0,所以用MathUtils.checkZero(b)验证b==0。如果MathUtils.checkZero的判断逻辑有误,例如:

public static boolean checkZero(int num) {

return num != 0; // bug

}

因为Calculater引用的任何依赖,都可能出错。如果用junit做单元测试,依赖里面可能是Android库或者jni native方法,依赖方法执行就会报错。以上的各种原因,都会影响单元测试的结果。所以,改进代码如下:

public class Calculater {

IMathUtils mathUtils;

public double divide(int a, int b) {

if (mathUtils.checkZero(b)) {

throw …

}

return (double) a / b;

}

}

public interface IMathUtils {

public boolean checkZero(int num);

}

在Calculater构造方法传入IMathUtils派生类,又或者用setter。在项目执行代码中,传MathUtils,而单元测试时,可以写一个MathUtilsTest继承IMathUtils,传给Calculater。只要保证MathUtilsTest.checkZero()正确。经过重构,Calculater就不依赖原来的MathUtils,单元测试时可以替换专门的实现,达到了依赖隔离的目的。

为了更好地解决上述问题,引入Mock。在单元测试mock可以模拟返回数据,也可以模拟接口/方法的行为。例如mathUtils.checkZero(b):“当mathUtils调用checkZero(num)”时,判断 num==0;或者“当调用checkZero(0)时返回true,num为其他值时返回false”。checkZero()是一个行为,返回true、false。单元测试时,经常要让方法/接口模拟某些行为,并得到模拟数据。例如,需要测试a=2,b=1和a=2,b=0调用divide(a,b)两者结果分别是2,抛出错误,使用mockito框架让mathUtils.checkZero()模拟行为,代码如下:

public static void main(String[] args) {

// 生成IMathUtils模拟对象

IMathUtils mathUtils = mock(IMathUtils.class);

// 当num==1时,checkZero(num)返回false

when(mathUtils.checkZero(1)).thenReturn(false);

// 当num==0时,checkZero(num)返回true

when(mathUtils.checkZero(0)).thenReturn(true);

Calculater calculater = new Calculater(mathUtils);

assertEquals(calculater.divide(2,1), 2); // 验证 divide(2,1) 结果是2

try {

calculater.divide(2, 0); // 预期抛出错误

// 如果divide没抛错,则此处抛错

throw new RuntimeException(“no expectant exception”);

} catch (Exception e) {

Assert.assertEquals(e.getMessage(), “dividend is zero”); // 验证错误信息

}

}

mock(IMathUtils.class)生成IMathUtils类的模拟对象。这个mock对象调用任何方法都不会被实际执行;when(mathUtils.checkZero(1)).thenReturn(false),当调用checkZero(num)并且num==1,返回false,这里mockito模拟了checkZero()行为,并模拟了返回数据;所以,calculater.divide(2,1)返回结果2,calculater.divide(2, 0)抛出RuntimeException。

使用mockito模拟类方法和返回数据,通过mock隔离了Calculater对IMathUtils实现类的依赖,并通过单元测试,验证了divide()的逻辑正确性。

难点

版本管理是指对项目的整体版本的演变过程进行管理,例如,从 1.0 到 1.1,再到 2.0 等。版本控制是借助第三方的版本控制工具,追踪管理项目文档(包括代码)的每一个变更。在项目开发过程中,团队内部会随着项目的进展发布最新的快照版本。但是开发到一定的阶段,需要将快照版本定位成一个发布版本对团队外部进行发布,同时,在这个定位版本的基础上进行二次开发,开发过程中又形成新的快照,到一定阶段后,再发布一个定位的发布版本,以此重复进行,直到最后项目完成。

这个过程中快照版本和发布版本的切换管理就是版本管理。版本管理关系的一个核心问题就是要科学地解决快照版本和发布版本之间的切换问题。理想的发布版本,应该是在项目进展到一个比较稳定的状态。这种稳定状态包括源代码的状态和构件的状态,所以一般构建一个稳定版本的条件有以下几个方面。

1)所有的测试案例应该全部通过。自己的测试案例都不能通过,说明有漏洞。发布这种带有明显漏洞的版本是没有意义的。

2)项目中没有配置任何快照版本的依赖。发布版本与快照版本最显著的区别就是稳定。

3)项目中没有配置任何快照版本的插件。

4)项目中所有的文档(包含代码)都要提交到版本控制系统。一个稳定版本的发布,不仅表示项目进入到了一个相对稳定的阶段,而且还必须保证相关的文档能齐备,以便以后能准确返回到这个阶段。如果要发布的文档没有全部集中提交到版本控制系统中,意味着一不小心文档就会残缺,这样的版本没法回滚。

VSS全称为 Visual Source Safe,它主要任务是负责项目文件的管理,几乎可以适用任何软件项目。管理软件开发中各个不同版本的源代码和文档,占用空间小并且方便各个版本代码和文档的获取,对开发小组中对源代码的访问进行有效的协调。VSS能帮助解决一部分版本控制方面的问题,也在一定程度上帮助解决代码共享方面的难题。但是依旧存在一些不足,比如:1.文件大多会以独占的形势进行锁定。如果一个人在修改的时候其他人没有办法进行修改。2.VSS只支持Windows版本,且只兼容微软的开发工具。3.文件存储,服务器必须共享文件夹,对文件的安全性没有足够保障。

SVN是Subversion的简称,是一个开放源代码的版本控制系统,相较于RCS、CVS,它采用了分支管理系统,它的设计目标就是取代CVS。互联网上很多版本控制服务已从CVS迁移到Subversion。SVN用于多个人共同开发同一个项目,共用资源的目的。和VSS相比,除开最基本的代码和文件管理功能外,主要的区别是提供了分支的功能,从而解决了VSS文件独占的问题。大幅提升了开发人员的工作效率,开发人员写完代码,随时可以提交到自己的分支上,最后对所有分支进行合并,解决冲突即可。SVN作为集中式的版本管理系统,优点:1.管理方便,逻辑明确,操作简单,上手快。2.易于管理,集中式服务器更能保证安全性。3.代码一致性非常高。4.有良好的目录级权限控制系统。缺点:1.对服务器性能要求高,数据库容量经常暴增,体量大。2.必须联网。如果不能连接到服务器上,基本上不可以工作,如果服务器不能连接上,就不能提交,还原,对比等等。3.不适合开源开发。4.分支的管控方式不灵活

Git是一款免费、开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。作为一个开源的分布式版本控制系统,可以有效、高速的处理从很小到非常大的项目版本管理。分布式相比于集中式的最大区别在于开发者可以提交到本地,每个开发者通过克隆(git clone),在本地机器上拷贝一个完整的Git仓库。Git适合分布式开发,每一个个体都可以作为服务器。每一次Clone就是从服务器上pull到了所有的内容,包括版本信息。公共服务器压力和数据量都不会太大,速度快、灵活,分支之间可以任意切换。任意两个开发者之间可以很容易的解决冲突,并且单机上就可以进行分支合并。离线工作不影响本地代码编写,等有网络连接以后可以再上传代码,并且在本地可以根据不同的需要,本地新建自己的分支。

git使用方法

(1)cd 当前项目目录

(2)git init // 初始化一个本地的仓库

就是在本地文件夹中添加了一个.git的文件夹用于记录所有的项目变更信息。

(3)git status //查看本地仓储的变更状态

用于查看本地仓储的状态,第一次查看,显示的是一堆没有被跟踪的文件。

git status -s // -s 是输出简要的变更日志

(4)git add --all / git add . //添加本地暂存(托管)文件

所有添加跟踪,类似于node_modules这种性质的文件是不应该被跟踪。

(5)添加本地GIT忽略清单文件

在代码库文件夹的根目录添加一个.gitignore文件,此文件用于说明忽略的文件有哪些。

(6)提交被托管的文件变化到本地仓储

git commit -m ‘需要说明的信息如:第一次提交’

将本地的变化提交的本地的仓库文件夹归档,一般在一个小单元的整体变化后再提交。

(7)对比差异

git diff

可以用于对比当前状态和版本库中状态的变化。

(8)提交日志

git log //可以查看提交日志

(9)回归到指定版本

git reset --hard 哈希值的前六位

(10)为仓储添加远端(服务器端)地址

// 添加一个远端地址并起了一个别名叫origin

$ git remote add origin https://github.com/lele/Git.git

$ git remote -v // 查看现有的远端列表

(11)将本地仓储的提交记录推送到远端的master分支

$ git push -u origin master

(12)拉取远端master分支的更新记录到本地

$ git pull origin master

GitHub是开源的代码库以及版本控制库,是目前使用网络上使用最为广泛的服务,GitHub可以托管各种Git库。首先需要注册一个GitHub账号,打开https://github.com/,点击“Sign up”进行注册。点击“create an account”后,可进入后续界面。Github对开源的公共项目是免费的,如果想创建私有的版本库,可根据私有库的数量进行付费。默认情况下选择的是Free,即当前创建的账户在github上只能创建开源库。登陆后点击“+New repository”,进入版本库创建页面,填写具体的信息,如项目名称、项目描述并勾选说明文件 README后点击“Create Repository”即可完成版本库的创建。

版本控制对于发布新版本和开发中的版本维护有着关键性的作用,用户在安装APP的时候需要知道详细的版本信息,并且在对已有版本进行升级的时候,能否正确安装新版本都和版本控制有关。比如:当手机中安装APP的versionCode=1,升级更新的版本号是2(versionCode=2),此时APP可以正常安装更新。反之,如果手机中当前APP版本号是2,升级的或者从其他渠道获取的同一APP的版本号为1,此时替换安装就会失败。这种情况就是说,同一个APP低版本是不能直接覆盖安装手机中已存在的高版本应用(通过版本号versionCode来判断)。这样在应用升级维护过程中,提高了软件的可维护性和安全性。对于其他应用想要搜索或者调用指定版本号和版本名称的应用来说,有了可靠的保证。对于第三方APP市场来说,这个版本更为关键的是,决定对用户下载下来之后的兼容性判断起到一定管控作用。

Google为APK定义了两个关于版本属性:VersionCode和VersionName,它们有不同的用途。下面是一个Manifest.xml文件示例:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.example.app"
  android:versionCode="1"
  android:versionName="1.0" >
  <application
    android:icon="@drawable/ic_launcher"
    android:theme="@style/AppTheme" >
    ...      
  </application>
</manifest>

版本号(versioncode)是相对比较重要的一个属性。对消费者不可见,仅用于应用市场、程序内部识别版本,判断新旧等用途。versionCode是一个Integer类型的值。所以在设置的时候,不要将versionCode设置的太大,最好不要超过Integer的取值范围(当然一般也是不会超过的)。然后,每次发布更新版本时可以递增versionCode的值。

VersionName是展示给用户的版本信息,它是一个值为String类型的属性,一般和VersionCode成对出现。VersionCode是方便程序开发者运行和维护Application而设置的一个有效的值。VersionName是放在各个第3方平台上提供给用户的一个版本名,是对VersionCode的解释和描述。

本章习题:

1、本单元考核点

Android系统的界面设计、菜单设计和数据库设计。

Android系统的单元测试、版本控制和应用发布。

2、本单元课后习题

1、简述Android的版本号格式;说明各种版本号的含义。

答案:Android版本号分为三段式和四段式数字形式。

四段式是一种常见软件版本号的形式是major.minor.maintenance.build,用“.”进行区隔:

major是主版本号,一般在软件有重大升级时增长;

minor是次版本号、子版本号,一般在软件有新功能时增长;

maintenance是维护版本、阶段版本号,一般在软件有主要的问题修复后增长;

build是构建版本或者日期版本号加希腊字母版本号,一般只要软件被重新编译过就会增长。希腊字母版本号共有5种,分别为:base、alpha、beta、RC、release。例如:1.1.1.051021_beta。

⑴ Base:

此版本表示该软件仅仅是一个假页面链接,通常包括所有的功能和页面布局,但是页面中的功能都没有做完整的实现,只是做为整体的一个基础架构。

⑵ α(Alpha)版:内测版。

软件的初级版本,表示该软件在此阶段以实现软件功能为主,通常只在软件开发者内部交流,或者专业测试人员测试用,一般而言,该版本软件的Bug较多,需要继续修改,是测试版本。测试人员提交Bug经开发人员修改确认之后,发布到测试网址让测试人员测试,此时可将软件版本标注为alpha版。

⑶ β(Beta)版:公测版。

该版本相对于α版已有了很大的改进,消除了严重的错误,但还是存在着一些缺陷,需要经过多次测试来进一步消除,此版本主要的修改对像是软件的UI,供专业爱好者大规模测试用。

⑷ RC 版:

RC是 Release Candidate 的缩写,意思是发布倒计时,候选版本,该版本已经相当成熟了,完成全部功能并清除大部分的BUG,基本上不存在导致错误的BUG,与即将发行的正式版相差无几。

⑷ Release 版:

该版本意味“最终版本”,在前面版本的一系列测试版之后,终归会有一个正式版本,是最终交付用户使用的一个版本。该版本有时也称为标准版。一般情况下,Release不会以单词形式出现在软件封面上,取而代之的是符号®。

2、简述3种层次化的系统设计模式,并说明各层的主要功能。

答案:常用的3种系统设计模式:

1、三层分层设计包括:表示层(UI)、业务逻辑层(BLL)、数据访问层(DAL);其中业务逻辑层,作为表示层和数据访问层的桥梁;

2.四层分层设计包括:表示层、业务规则层(ECL)、业务逻辑层、数据访问层;业务规则层的作用是对UI层传下来的参数进行检验,比如登录名的合法性,密码的合法性等;

3.引入Service层设计,包括:表示层、服务层、业务逻辑层、数据访问层;服务层主要用于给表示层业务逻辑入口,定义接口服务。

表示层最重要的是保持代码和结构整洁:1)尽量采用抽象和封装的思维,面向对象设计;2)按组件划分,比如包名按照view/activity/fragment/adapter/util/service/app/network等来划分;3)基类的定义和封装,以及管理所有的API接口等。业务层的作用是向上跟表示层交互;向下与数据层交互;数据层主要是如何获取数据,如何组织整理数据,如何展示数据。可以将数据层分为网络层,本地数据层,交付层。网络层主要处理一些网络相关的,比如节省流量、不同网络状态的处理、API参数合法性、不同的错误码和响应码对应情况;本地数据层主要处理数据,数据是否需要缓存,缓存策略和缓存的时间周期等;交付层不用关心数据来源,只关心结果。

参考资源:

1、林学森著.深入理解Android内核设计思想.北京:人民邮电出版社,2014.

2、任玉刚著.Android开发艺术探索.北京:电子工业出版社,2015.

原创声明

=======

作者: [ libin9iOak ]


本文为原创文章,版权归作者所有。未经许可,禁止转载、复制或引用。

作者保证信息真实可靠,但不对准确性和完整性承担责任。

未经许可,禁止商业用途。

如有疑问或建议,请联系作者。

感谢您的支持与尊重。

点击下方名片,加入IT技术核心学习团队。一起探索科技的未来,共同成长。


目录
相关文章
|
24天前
|
测试技术 API 项目管理
API测试方法
【10月更文挑战第18天】API测试方法
41 1
|
1月前
|
安全 测试技术
北大李戈团队提出大模型单测生成新方法,显著提升代码测试覆盖率
【10月更文挑战第1天】北京大学李戈教授团队提出了一种名为“统一生成测试”的创新方法,有效提升了大模型如GPT-2和GPT-3在单一测试中的代码生成覆盖率,分别从56%提升至72%和从61%提升至78%。这种方法结合了模糊测试、变异测试和生成对抗网络等多种技术,克服了传统测试方法的局限性,在大模型测试领域实现了重要突破,有助于提高系统的可靠性和安全性。然而,该方法的实现复杂度较高且实际应用效果仍需进一步验证。论文可从此链接下载:【https://drive.weixin.qq.com/s?k=ACAAewd0AA48Z2kXrJ】
61 1
|
23天前
|
测试技术 UED
软件测试中的“灰盒”方法:一种平衡透明度与效率的策略
在软件开发的复杂世界中,确保产品质量和用户体验至关重要。本文将探讨一种被称为“灰盒测试”的方法,它结合了白盒和黑盒测试的优点,旨在提高测试效率同时保持一定程度的透明度。我们将通过具体案例分析,展示灰盒测试如何在实际工作中发挥作用,并讨论其对现代软件开发流程的影响。
|
2月前
|
人工智能 测试技术 开发者
北大李戈团队提出大模型单测生成新方法,显著提升代码测试覆盖率
【9月更文挑战第27天】北京大学李戈团队在人工智能领域取得重要突破,提出HITS新方法,通过将待测方法分解为多个切片并利用大型语言模型逐个生成测试用例,显著提升代码测试覆盖率,尤其在处理复杂方法时效果显著,为软件开发和测试领域带来新希望。尽管存在一定局限性,HITS仍展示了巨大潜力,未来有望克服限制,推动软件测试领域的创新发展。论文详情见【https://www.arxiv.org/pdf/2408.11324】。
88 6
|
14天前
|
Java 测试技术 Maven
Java一分钟之-PowerMock:静态方法与私有方法测试
通过本文的详细介绍,您可以使用PowerMock轻松地测试Java代码中的静态方法和私有方法。PowerMock通过扩展Mockito,提供了强大的功能,帮助开发者在复杂的测试场景中保持高效和准确的单元测试。希望本文对您的Java单元测试有所帮助。
29 2
|
1月前
|
测试技术 Python
自动化测试项目学习笔记(三):Unittest加载测试用例的四种方法
本文介绍了使用Python的unittest框架来加载测试用例的四种方法,包括通过测试用例类、模块、路径和逐条加载测试用例。
60 0
自动化测试项目学习笔记(三):Unittest加载测试用例的四种方法
|
1月前
|
测试技术 Python
自动化测试项目学习笔记(二):学习各种setup、tearDown、断言方法
本文主要介绍了自动化测试中setup、teardown、断言方法的使用,以及unittest框架中setUp、tearDown、setUpClass和tearDownClass的区别和应用。
60 0
自动化测试项目学习笔记(二):学习各种setup、tearDown、断言方法
|
1月前
|
测试技术 UED
软件测试中的探索性测试:一种高效且灵活的测试方法
本文将深入探讨探索性测试的核心概念、优势及其在实际项目中的应用。我们将从探索性测试的基本定义入手,逐步解析其在不同场景下的具体实施方法和最佳实践。通过详细的案例分析和方法对比,帮助读者全面了解这种既高效又灵活的软件测试技术。
|
1月前
|
安全 测试技术 API
一图看懂API测试9种方法
一图看懂API测试九种方法:冒烟测试验证基本功能,功能测试确保符合规格,集成测试检查组件协同工作,回归测试防止新变更引入问题,负载测试评估性能稳定性,压力测试挑战极限负载,安全测试发现并修复漏洞,用户界面测试确保UI与API协调,模糊测试提升异常数据处理鲁棒性。
|
1月前
|
弹性计算 安全 Linux
阿里云国际版使用ping命令测试ECS云服务器不通的排查方法
阿里云国际版使用ping命令测试ECS云服务器不通的排查方法