赞
踩
在导入小米便签源代码的时候,由于一开始使用2023的最新版本,但是由于gradle问题,在导入的时候无法自动识别为“Android”模式,后面采用了2020版本,成功识别出“Android”模式。当然在环境搭建过程中也出现了多种多样的问题,参考了往届学长的博客:在Win10以及SDK为33的环境下——小米便签项目的搭建_3 files found with path 'meta-inf/dependencies'.-CSDN博客
里面涵盖了大部分搭建过程会出现的问题,十分详细。
在环境搭建完成之后,在运行测试时,发现功能缺失的现象,在查找资料后,解决此问题:
①定位:app->res->values->styles
②在styles.xml中下滑到最后,可以看见:
③修改代码为:
<style name="NoteActionBarStyle" parent="@android:style/Widget.Holo.Light.ActionBar.Solid"> <!-- <item name="android:displayOptions" />--> <item name="android:visibility">visible</item> </style>
由于个人习惯问题,我们在正式阅读代码之前,根据代码中的命名和参考网上的资料,将包、子包、类以及它们的作用绘制成表格,以此形成了一个大体的框架认识,其中添加标记的是我们认为是核心类的部分:
包 | 子包 | 类 | 主要作用 |
data | contact | 联系人数据库 | |
Notes | 便签数据库,用于记录便签相关属性和数据 | ||
NotesProvider | 便签信息提供类 | ||
NotesDatabaseHelper | 数据库帮助类,用于辅助创建、处理数据库的条目 | ||
gtask | data | MetaData | 关于同步任务的元数据 |
Node | 同步任务的管理结点,用于设置、保存同步动作的信息 | ||
SqlData | 数据库中基本数据,方法包括读取数据、获取数据库中数据、提交数据到数据库 | ||
SqlNode | 数据库中便签数据,方法包括读取便签内容、从数据库中获取便签数据、设置便签内容、提交便签数据到数据库 | ||
Task | 同步任务,将创建、更新和同步动作包装成JSON对象,用本地和远程的JSON对结点内容进行设置,获取同步信息,进行本地和远程的同步 | ||
TaskList | 同步任务列表,将Task组织成同步任务列表进行管理 | ||
exception | ActionFailureException | 动作失败异常 | |
NetworkFailureException | 网络异常失败 | ||
remote | GTaskAsyncTask | GTask异步任务,方法包括任务同步和取消,显示同步任务的进程、通知和结果 | |
GTaskClient | GTask客户端,提供登录Google账户,创建任务和任务列表,添加和删除结点,提交、重置更新、获取任务列表等功能 | ||
GTaskManager | GTask管理者,提供同步本地和远端的任务,初始化任务列表,同步内容、文件夹,添加、更新本地和远端结点,刷新本地同步任务ID等功能 | ||
GTaskSyncService | GTask同步服务,用于提供同步服务 (开始、取消同步),发送广播 | ||
model | Note | 单个便签项 | |
WorkingNote | 当前活动便签项 | ||
tool | BackupUtils | 备份工具类,用于数据备份读取、显示 | |
DataupUtils | 便签数据处理工具类,封装如查找、移动、删除数据等操作 | ||
GTaskStringUtils | 同步中使用的字符串工具类,为了jsonObject提供string对象 | ||
ResourceParser | 界面元素的解析工具类,利用R.java这个类获取资源供程序调用 | ||
ui | AlarmAlertActivity | 闹铃提醒界面 | |
AlarmInitReceiver | 闹铃提醒启动消息接收器 | ||
AlarmReceiver | 闹铃提醒接收器 | ||
DateTimePicker | 设置提醒时间的部件 | ||
DateTimePickerDialog | 设置提醒时间的对话框界面 | ||
DropdownMenu | 下拉菜单界面 | ||
FoldersListAdapter | 文件夹列表链接器(链接数据库) | ||
NoteEditActivity | 便签编辑活动 | ||
NoteEditText | 便签的文本编辑界面 | ||
NoteItemData | 便签项数据 | ||
NotesListActivity | 主界面,用于实现处理文件夹列表的活动 | ||
NotesListAdapter | 便签列表链接器(链接数据库) | ||
NotesPreferenceActivity | 便签同步的设置界面 | ||
widget | NoteWidgetProvider | 桌面挂件 | |
NoteWidgetProvider_2x | 2倍大小的桌面挂件 | ||
NoteWidgetProvider_4x | 4倍大小的桌面挂件 |
首先我们需要明晰例图是什么,里面有哪些元素,有哪些关系,它们又是用什么样的图像表示的?
带着这个问题,我们参考:UML图之例图-CSDN博客,完成了本次例图绘制:
1、明晰包图的绘制方法:UML--包图详解_uml包图-CSDN博客
2、在绘制过程中,我们不可避免地参考了网上已有的体系结构图:
但在根据网上的体系结构图绘制过程了,也有了一些自己的体会:
(1)在绘制的包图的时候,特别是向小米便签这样,对现阶段的我相对大型的代码文件时,首先就是要初步地划分层次。
上图中将小米便签分成了:界面层、业务层、模型层、数据层。其中很容易考虑到界面层和数据层,界面是最直接面对软件用户,而数据是最基础的部分。但是对于业务层(部分资料称之为:工具的集成与实现)和模型层就有些不能理解。
在咨询Cursor之后,发现了MVC架构:
于是在网上查找相关的资料:超详细的!!!MVC架构模式说明 - 知乎 (zhihu.com)
然后明晰为什么采取界面层、业务层、模型层、数据层这样的划分层次。
这样就可以得到一个初步的框架:
(2)在划分完层次之后,事情就变得简单了,那就是根据每一个类的功能将其填入对应的层级中。
界面层:根据表格(在前置动作部分)中列出的主要功能,我们可以很轻松地将ui和widget填入其中,而res包含了所有的xml文件和相对应图片,属于MVC架构里面View部分,所以也应该填入界面部分。
业务层:根据MVC架构的逻辑,个人判断它应该担任是C(Control控制器)的角色:处理用户输入的信息。负责从视图读取数据,控制用户输入,并向模型发送数据,是应用程序中处理用户交互的部分。负责管理与用户交互交互控制。
因此根据表格中的主要功能,首先排除ui和widget,接着关于gtask.date,我们可以发现它大部分功能是与数据库交互,不符合业务层的要求,因此也排除,那么model嘛,看名字就知道是模型层的,最后只剩下tool、gtask.exception和gtask.remote。
tool:
gtask.exception和gtask.remote:
从正面来看,他们的确符合业务层的逻辑;从反面论证,首先他们必定不属于界面层和数据层,而模型层在于与数据层的交互,而它们并没有。
最后对于gtask.data为什么不属于数据层,而是模型层,以我现阶段基于小米便签源代码的理解就是:数据层只是单纯地对数据库进行操作(创建数据库,数据库内的增删改查等)而不涉及其他部分(如调用数据库),这一点可以通过对比表格内容发现。当然,后续核心类的分析,也可以很清楚发现data类只对数据库内部进行操作。
1、首先我们必须明确类间关系一种有几种?各自的含义是什么?如何通过图像表示出来?
类间关系一种有6种,分别为继承(extends)、实现(implements)、关联关系、聚合关系、组合关系、依赖关系。
各自的含义是什么?如何通过图像表示出来?
由于继承和实现已经较为深入的学习并实践过,关系的判断可以通过单词(extends和implements)查询来完成,因此不赘述。
图例:
关联关系:某类作为另一类的成员变量,分为单向关联和双向关联。
eg:
单向联系:班级中增加了学生,但是学生中没有增加班级属性(他爱她,她不爱他?)
双向关联:班级中增加了学生,学生中也增加了班级属性(他爱她,她也爱他?)
图例:
单向联系:(单相思?所以单箭头)
双向联系:(双向奔赴?但不是双箭头)
聚合关系:某类作为另一类的成员变量(整体和部分可分离,整体的生命周期和部分的生命周期不同)(你+你女朋友=情侣,可以分手?情侣关系结束,但你不会die)
eg:计算机与CPU、公司与员工的关系、班级和学生(班级里所有的学生共同组成班级,而关联关系描述的是单个学生与班级的关系)的关系
图例:
组合关系:某类作为另一类的成员变量(整体和部分不可分离,整体的生命周期和部分的生命周期相同)(你和你的恋爱脑?)
eg:人和四肢的关系
图例:(实心的,这段关系铁滴狠)
依赖关系:若类A使用类B(类B作为类A的方法的参数(或者局部变量)存在),则由类A指向类B,但是类B的变化会影响到类A
eg:人和他掌握技能(你和你的代码能力?)
图例:
以下是根据网上的例子画出的调用关系图:
data:
gtask.data:
gtask.remote:
model:
widget:
ui:
除开明确的继承和实现关系,对于调用关系图仍存在一些不理解的地方,如:
gtask.data中为什么是SqlNode->SqlData,而不是SqlData->SqlNode,或者SqlNode是SqlData的聚合,代码中明显有:
(1)首先定义了小米便签应用中的一些常量和标识符:
其中:
AUTHORITY:用于标识便签应用的权限;
TAG:用于标识便签应用的标签
TYPE_NOTE、TYPE_FOLDER、TYPE_SYSTEM:定义了便签的类型,分别表示便签、文件夹和系统类型
ID_ROOT_FOLDER、ID_TEMPARAY_FOLDER、ID_CALL_RECORD_FOLDER、ID_TRASH_FOLER:定义了系统文件夹的标识符
INTENT_EXTRA_ALERT_DATE、INTENT_EXTRA_BACKGROUND_ID、INTENT_EXTRA_WIDGET_ID、INTENT_EXTRA_WIDGET_TYPE、INTENT_EXTRA_FOLDER_ID、INTENT_EXTRA_CALL_DATE:定义了意图中的额外数据键
TYPE_WIDGET_INVALIDE、TYPE_WIDGET_2X、TYPE_WIDGET_4X:定义了小部件的类型,包括无效类型、2倍大小和4倍大小类型
(2)接着定义了两个静态变量:
其中:
CONTENT_NOTE_URI:表示便签(Note)的内容URI,用于访问便签相关数据。
CONTENT_DATA_URI:表示数据(Data)的内容URI,用于访问数据相关信息。
这些URI通过解析包含权限(AUTHORITY)和路径("note"或"data")的字符串来创建,用于在应用中访问和操作便签和数据的内容。
(3)定义了两个接口:
通过阅读具体的代码,可以发现:这两个接口主要用于定义便签和数据表的列名和数据类型,以便在应用中进行数据库操作和数据存储
NoteColumns接口:
定义了便签(Note)数据表的列名和数据类型,包括便签的唯一ID、父级ID、创建日期、修改日期、提醒日期、内容摘要、小部件ID、小部件类型、背景颜色ID、附件标识、便签数量、类型、同步ID、本地修改标识、原始父级ID、GTask ID和版本号等。
DataColumns接口:
定义了数据(Data)表的列名和数据类型,包括数据的唯一ID、MIME类型、关联的便签ID、创建日期、修改日期、内容、以及5个通用数据列(data1至data5)用于不同类型的数据存储。
(4)定义了两个方法,都引用了DataColumns接口:
TextNote类:
其中,对于DataColumns接口的继承体现在DATA1(MIME类型)为整型数据类型,即:
CallNote类:
其中,对于DataColumns接口的继承体现在DATA3(MIME类型)为TEXT数据类型,即:
(5)定义了静态内部类DataConstants:
(1)对Notes类中DataColumns、DataConstants、NoteColumns进行引用:
(2)定义了数据库名称、版本号、表名称以及类的标签和实例变量:
其中:
DB_NAME:定义了数据库的名称为"note.db"。
DB_VERSION:定义了数据库的版本号为4。
TABLE接口:定义了两个表的名称,分别为"note"和"data"。
TAG:定义了一个标签为"NotesDatabaseHelper"。
mInstance:定义了一个静态变量mInstance,用于表示NotesDatabaseHelper类的实例。
(3)定义了SQL语句分别用于创建便签表、数据表、包含ID、便签和数据的表格:
(4)定义了对数据库进行增删改查等工作的SQL语句:
将笔记移动到文件夹时增加文件夹的笔记数:
从文件夹中移动笔记时减少文件夹的笔记计数:
在文件夹中插入新笔记时增加文件夹的笔记数:
从文件夹中删除笔记时减少文件夹的笔记计数:
插入类型为{DataConstants.Note}的数据时更新注释的内容:
当{DataConstants.Note}类型的数据发生更改时,更新注释的内容:
删除{DataConstants.Note}类型的数据后,更新注释的内容:
删除数据属于已删除的注释:
删除笔记属于已删除的文件夹:
移动笔记属于已移动到垃圾文件夹的文件夹:
(5)定义创建数据库的方法:
createNoteTable:
createDataTable:
createNoteTable(SQLiteDatabase db):创建便签表,包括执行创建表的SQL语句、重新创建表的触发器、创建系统文件夹等操作。
reCreateNoteTableTriggers(SQLiteDatabase db):重新创建便签表的触发器。
createSystemFolder(SQLiteDatabase db):创建系统文件夹,包括通话记录文件夹、默认文件夹、临时文件夹和回收站文件夹。
createDataTable(SQLiteDatabase db):创建数据表,包括执行创建表的SQL语句、重新创建表的触发器和创建数据表的索引。
reCreateDataTableTriggers(SQLiteDatabase db):重新创建数据表的触发器。
getInstance(Context context):获取NotesDatabaseHelper类的实例。
onCreate(SQLiteDatabase db):在数据库创建时调用,执行创建便签表和数据表的操作。
(6)定义数据库升级更新的方法:
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion):在数据库升级时调用,根据旧版本和新版本执行相应的升级操作。
upgradeToV2(SQLiteDatabase db):升级数据库到版本2的操作。
upgradeToV3(SQLiteDatabase db):升级数据库到版本3的操作,包括删除未使用的触发器、添加GTask ID列和添加回收站系统文件夹。
upgradeToV4(SQLiteDatabase db):升级数据库到版本4的操作,添加版本号列到便签表中。
(1)引用了Notes.DataColumns、Notes.NoteColumns、NotesDatabaseHelper.TABLE接口:
(2)NoteProvider类继承了ContentProvider:
由于本次实验主要为代码泛读,因此关于ContentProvider类的内容就不过多展开,此处引入一个较为详细的链接以便于后续学习:Carson带你学Android:关于ContentProvider的知识都在这里了!_自定义contentprovider 找不到class-CSDN博客
(3)设置URI匹配规则、定义搜索结果的投影和查询搜索便签摘要的SQL语句:
(4)处理便签应用中的数据访问和操作,包括查询、插入、删除和更新等操作:
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):处理查询操作,根据URI匹配不同的查询类型,包括便签、数据和搜索结果的查询。
insert(Uri uri, ContentValues values):处理插入操作,根据URI匹配不同的插入类型,包括便签和数据的插入,并通知数据变化。
delete(Uri uri, String selection, String[] selectionArgs):处理删除操作,根据URI匹配不同的删除类型,包括便签和数据的删除,并通知数据变化。
update(Uri uri, ContentValues values, String selection, String[] selectionArgs):处理更新操作,根据URI匹配不同的更新类型,包括便签和数据的更新,并通知数据变化。
parseSelection(String selection):辅助方法,用于解析查询条件。
increaseNoteVersion(long id, String selection, String[] selectionArgs):辅助方法,用于增加便签版本号。
getType(Uri uri):获取URI的MIME类型,暂未实现。
(1)在阅读代码时,发现ContentValues并不是像NoteData一样属于自定义的,因此上网查找了其相关资料:android开发系列之由ContentValues看到的 - 蔡鸿军 - 博客园 (cnblogs.com)
(2)NoteData的内部类:管理便签中的文本数据和通话数据的操作,包括设置数据内容、ID,判断是否有本地修改以及将数据推送到ContentResolver中进行同步。
(3)创建一个新的笔记id,用于将新笔记添加到数据库:
主要用于管理工作便签的各种属性和操作,包括创建、加载、保存便签数据,设置提醒日期、背景颜色、小部件信息等,以及定义便签设置变化时的回调方法,被NoteEditActivity和NoteListActivity调用。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。