@ -1,4 +1,4 @@
/ *
/ * *
* Copyright ( c ) 2010 - 2011 , The MiCode Open Source Community ( www . micode . net )
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
@ -35,71 +35,80 @@ import java.io.FileOutputStream;
import java.io.IOException ;
import java.io.PrintStream ;
public class BackupUtils {
private static final String TAG = "BackupUtils" ;
// Singleton stuff
// Singleton stuff :即单例模式,确保一个类中只有一个实例且此实例可以被全局访问
private static BackupUtils sInstance ;
// 获取BackupUtils的单例实例
public static synchronized BackupUtils getInstance ( Context context ) {
if ( sInstance = = null ) {
sInstance = new BackupUtils ( context ) ;
}
// 如果sInstance为null, 则创建一个新的BackupUtils对象并将其赋值给sInstance
return sInstance ;
}
/ * *
/ *
* Following states are signs to represents backup or restore
* status
* /
// Currently, the sdcard is not mounted
// Currently, the sdcard is not mounted: 当前SD卡未挂载
// 定义备份和还原状态常量
public static final int STATE_SD_CARD_UNMOUONTED = 0 ;
// The backup file not exist
// The backup file not exist :备份数据不存在
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1 ;
// The data is not well formated, may be changed by other programs
// The data is not well formated, may be changed by other programs :数据被破坏或者被修改
public static final int STATE_DATA_DESTROIED = 2 ;
// Some run-time exception which causes restore or backup fails
// Some run-time exception which causes restore or backup fails :程序运行时异常导致备份或者还原操作失败
public static final int STATE_SYSTEM_ERROR = 3 ;
// Backup or restore success
// Backup or restore success :备份或者还原操作成功
public static final int STATE_SUCCESS = 4 ;
private TextExport mTextExport ;
// 声明一个TextExport对象mTextExport, 实现对象信息的导出
private BackupUtils ( Context context ) {
mTextExport = new TextExport ( context ) ;
// 创建一个TextExport对象, 并将其赋值给mTextExport
}
private static boolean externalStorageAvailable ( ) {
return Environment . MEDIA_MOUNTED . equals ( Environment . getExternalStorageState ( ) ) ;
// 检查外存是否可用, 如果可用则返回true, 否则返回false
}
public int exportToText ( ) {
return mTextExport . exportToText ( ) ;
// 调用TextExport对象的exportToText方法, 并返回结果
}
public String getExportedTextFileName ( ) {
return mTextExport . mFileName ;
// 获取导出对象的名字
}
public String getExportedTextFileDir ( ) {
return mTextExport . mFileDirectory ;
// 获取导出对象的目录
}
private static class TextExport {
// 定义一个常量数组, 用于从ContentProvider中获取特定的便签信息
private static final String [ ] NOTE_PROJECTION = {
NoteColumns . ID ,
NoteColumns . MODIFIED_DATE ,
NoteColumns . SNIPPET ,
NoteColumns . TYPE
NoteColumns . ID , //便签标识符
NoteColumns . MODIFIED_DATE , //便签的修改日期
NoteColumns . SNIPPET , //便签摘要
NoteColumns . TYPE //便签类型
} ;
private static final int NOTE_COLUMN_ID = 0 ;
// 定义一个常量, 表示便签ID的列索引
private static final int NOTE_COLUMN_MODIFIED_DATE = 1 ;
// 定义一个常量,表示便签修改日期的列索引
private static final int NOTE_COLUMN_SNIPPET = 2 ;
// 定义一个常量,表示便签摘要的列索引
private static final String [ ] DATA_PROJECTION = {
// 定义了一个包含数据列的投影数组
DataColumns . CONTENT ,
DataColumns . MIME_TYPE ,
DataColumns . DATA1 ,
@ -108,55 +117,70 @@ public class BackupUtils {
DataColumns . DATA4 ,
} ;
private static final int DATA_COLUMN_CONTENT = 0 ;
private static final int DATA_COLUMN_CONTENT = 0 ; // 数据内容列的索引
private static final int DATA_COLUMN_MIME_TYPE = 1 ;
private static final int DATA_COLUMN_MIME_TYPE = 1 ; //数据的媒体类型
private static final int DATA_COLUMN_CALL_DATE = 2 ;
private static final int DATA_COLUMN_CALL_DATE = 2 ; // 通话日期列的索引
private static final int DATA_COLUMN_PHONE_NUMBER = 4 ;
private static final int DATA_COLUMN_PHONE_NUMBER = 4 ; // 电话号码列的索引
private final String [ ] TEXT_FORMAT ;
private static final int FORMAT_FOLDER_NAME = 0 ;
private static final int FORMAT_NOTE_DATE = 1 ;
private static final int FORMAT_NOTE_CONTENT = 2 ;
private final String [ ] TEXT_FORMAT ; // 文本格式数组
private static final int FORMAT_FOLDER_NAME = 0 ; // 文件夹名称的格式索引
private static final int FORMAT_NOTE_DATE = 1 ; // 备注日期的格式索引
private static final int FORMAT_NOTE_CONTENT = 2 ; // 备注内容的格式索引
private Context mContext ;
private String mFileName ;
private String mFileDirectory ;
private Context mContext ; //文本
private String mFileName ; //文件名
private String mFileDirectory ; //文件目录
public TextExport ( Context context ) {
TEXT_FORMAT = context . getResources ( ) . getStringArray ( R . array . format_for_exported_note ) ;
// 从资源中获取字符串数组, 并将其赋值给TEXT_FORMAT成员变量
mContext = context ;
// 将传入的Context对象赋值给mContext成员变量
mFileName = "" ;
// 将mFileName成员变量初始化为空字符串
mFileDirectory = "" ;
// 将mFileDirectory成员变量初始化为空字符串
}
private String getFormat ( int id ) {
return TEXT_FORMAT [ id ] ;
}
} // 根据传入的id参数, 从TEXT_FORMAT数组中获取对应索引位置的字符串格式化模板
/////////////////////////////////////////
/ * *
* Export the folder identified by folder id to text
* /
private void exportFolderToText ( String folderId , PrintStream ps ) {
// Query notes belong to this folder
// Query notes belong to this folder : 通过查询parent id是文件夹id的note来选出制定ID文件夹下的Note
Cursor notesCursor = mContext . getContentResolver ( ) . query ( Notes . CONTENT_NOTE_URI ,
NOTE_PROJECTION , NoteColumns . PARENT_ID + "=?" , new String [ ] {
folderId
} , null ) ;
if ( notesCursor ! = null ) {
/ *
这 行 太 复 杂 了 , 给 出 详 细 解 释
创 建 一 个 名 为 notesCursor 的 游 标 对 象 , 用 于 存 储 查 询 结 果 。
使 用 mContext . getContentResolver ( ) 获 取 ContentResolver 对 象 , 它 用 于 与 系 统 中 的 内 容 提 供 者 进 行 交 互 。
调 用 ContentResolver 的 query ( ) 方 法 执 行 查 询 操 作 , 传 入 以 下 参 数 :
Notes . CONTENT_NOTE_URI : 表 示 要 查 询 的 数 据 的 URI , 即 "Notes" 表 格 的 URI 。
NOTE_PROJECTION : 表 示 查 询 结 果 中 要 返 回 的 列 。
NoteColumns . PARENT_ID + "=?" : 表 示 查 询 条 件 , 即 根 据 "NoteColumns.PARENT_ID" 字 段 的 值 进 行 查 询 。
new String [ ] { folderId } : 表 示 查 询 条 件 的 参 数 值 , 即 具 体 的 "folderId" 值 。
null : 表 示 查 询 结 果 排 序 方 式 , 此 处 为 默 认 排 序 方 式 。
查 询 结 果 将 会 存 储 在 notesCursor 游 标 对 象 中 , 可 以 通 过 该 对 象 获 取 查 询 结 果 。
* /
if ( notesCursor ! = null ) { // 如果查询结果不为空
if ( notesCursor . moveToFirst ( ) ) {
do {
// Print note's last modified date
// Print note's last modified date : // 打印笔记的最后修改日期
ps . println ( String . format ( getFormat ( FORMAT_NOTE_DATE ) , DateFormat . format (
mContext . getString ( R . string . format_datetime_mdhm ) ,
notesCursor . getLong ( NOTE_COLUMN_MODIFIED_DATE ) ) ) ) ;
// Query data belong to this note
// Query data belong to this note : // 查询属于该笔记的数据
String noteId = notesCursor . getString ( NOTE_COLUMN_ID ) ;
exportNoteToText ( noteId , ps ) ;
} while ( notesCursor . moveToNext ( ) ) ;
} while ( notesCursor . moveToNext ( ) ) ; // 遍历结果集中的每一行数据
}
notesCursor . close ( ) ;
}
@ -164,13 +188,25 @@ public class BackupUtils {
/ * *
* Export note identified by id to a print stream
* 将 以 ID 标 识 的 笔 记 导 出 到 输 出 流 中
* /
private void exportNoteToText ( String noteId , PrintStream ps ) {
Cursor dataCursor = mContext . getContentResolver ( ) . query ( Notes . CONTENT_DATA_URI ,
DATA_PROJECTION , DataColumns . NOTE_ID + "=?" , new String [ ] {
noteId
} , null ) ;
/ *
创 建 一 个 名 为 exportNoteToText 的 方 法 , 该 方 法 接 收 noteId 和 PrintStream 对 象 ps 作 为 参 数 , 用 于 导 出 笔 记 内 容 到 文 本 文 件 。
创 建 一 个 名 为 dataCursor 的 游 标 对 象 , 用 于 存 储 查 询 结 果 。
使 用 mContext . getContentResolver ( ) 获 取 ContentResolver 对 象 , 它 用 于 与 系 统 中 的 内 容 提 供 者 进 行 交 互 。
调 用 ContentResolver 的 query ( ) 方 法 执 行 查 询 操 作 , 传 入 以 下 参 数 :
Notes . CONTENT_DATA_URI : 表 示 要 查 询 的 数 据 的 URI , 即 "Notes" 表 格 的 DATA_URI 。
DATA_PROJECTION : 表 示 查 询 结 果 中 要 返 回 的 列 。
DataColumns . NOTE_ID + "=?" : 表 示 查 询 条 件 , 即 根 据 "DataColumns.NOTE_ID" 字 段 的 值 进 行 查 询 。
new String [ ] { noteId } : 表 示 查 询 条 件 的 参 数 值 , 即 具 体 的 "noteId" 值 。
null : 表 示 查 询 结 果 排 序 方 式 , 此 处 为 默 认 排 序 方 式 。
查 询 结 果 将 会 存 储 在 dataCursor 游 标 对 象 中 , 可 以 通 过 该 对 象 遍 历 获 取 查 询 结 果 的 每 条 记 录 。
* /
if ( dataCursor ! = null ) {
if ( dataCursor . moveToFirst ( ) ) {
do {
@ -185,16 +221,17 @@ public class BackupUtils {
ps . println ( String . format ( getFormat ( FORMAT_NOTE_CONTENT ) ,
phoneNumber ) ) ;
}
// Print call date
// Print call date :打印电话号码
ps . println ( String . format ( getFormat ( FORMAT_NOTE_CONTENT ) , DateFormat
. format ( mContext . getString ( R . string . format_datetime_mdhm ) ,
callDate ) ) ) ;
// Print call attachment location
// Print call attachment location :打印附件位置
if ( ! TextUtils . isEmpty ( location ) ) {
ps . println ( String . format ( getFormat ( FORMAT_NOTE_CONTENT ) ,
location ) ) ;
}
} else if ( DataConstants . NOTE . equals ( mimeType ) ) {
//获取笔记内容
String content = dataCursor . getString ( DATA_COLUMN_CONTENT ) ;
if ( ! TextUtils . isEmpty ( content ) ) {
ps . println ( String . format ( getFormat ( FORMAT_NOTE_CONTENT ) ,
@ -211,25 +248,27 @@ public class BackupUtils {
Character . LINE_SEPARATOR , Character . LETTER_NUMBER
} ) ;
} catch ( IOException e ) {
Log . e ( TAG , e . toString ( ) ) ;
Log . e ( TAG , e . toString ( ) ) ; //日志输出打印异常信息
}
}
/ * *
* Note will be exported as text which is user readable
* 该 方 法 的 功 能 是 将 应 用 程 序 中 的 笔 记 导 出 为 文 本 格 式 , 包 括 文 件 夹 和 笔 记 的 信 息 。 导 出 的 文 本 可 以 被 用 户 阅 读 和 查 看 。
* /
public int exportToText ( ) {
// 检查外部存储是否可用
if ( ! externalStorageAvailable ( ) ) {
Log . d ( TAG , "Media was not mounted" ) ;
return STATE_SD_CARD_UNMOUONTED ;
}
// 获取导出到文本的打印流
PrintStream ps = getExportToTextPrintStream ( ) ;
if ( ps = = null ) {
Log . e ( TAG , "get print stream error" ) ;
return STATE_SYSTEM_ERROR ;
}
// First export folder and its notes
// First export folder and its notes :首先导出文件夹及其笔记
Cursor folderCursor = mContext . getContentResolver ( ) . query (
Notes . CONTENT_NOTE_URI ,
NOTE_PROJECTION ,
@ -240,7 +279,7 @@ public class BackupUtils {
if ( folderCursor ! = null ) {
if ( folderCursor . moveToFirst ( ) ) {
do {
// Print folder's name
// Print folder's name :打印文件夹名称
String folderName = "" ;
if ( folderCursor . getLong ( NOTE_COLUMN_ID ) = = Notes . ID_CALL_RECORD_FOLDER ) {
folderName = mContext . getString ( R . string . call_record_folder_name ) ;
@ -251,18 +290,33 @@ public class BackupUtils {
ps . println ( String . format ( getFormat ( FORMAT_FOLDER_NAME ) , folderName ) ) ;
}
String folderId = folderCursor . getString ( NOTE_COLUMN_ID ) ;
//导出文件夹中的笔记
exportFolderToText ( folderId , ps ) ;
} while ( folderCursor . moveToNext ( ) ) ;
}
folderCursor . close ( ) ;
}
// Export notes in root's folder
// Export notes in root's folder :导出根文件夹中的笔记
Cursor noteCursor = mContext . getContentResolver ( ) . query (
Notes . CONTENT_NOTE_URI ,
NOTE_PROJECTION ,
NoteColumns . TYPE + "=" + + Notes . TYPE_NOTE + " AND " + NoteColumns . PARENT_ID
+ "=0" , null , null ) ;
/ *
创 建 一 个 名 为 noteCursor 的 游 标 对 象 , 用 于 存 储 查 询 结 果 。
使 用 mContext . getContentResolver ( ) 获 取 ContentResolver 对 象 , 它 用 于 与 系 统 中 的 内 容 提 供 者 进 行 交 互 。
调 用 ContentResolver 的 query ( ) 方 法 执 行 查 询 操 作 , 传 入 以 下 参 数 :
Notes . CONTENT_NOTE_URI : 表 示 要 查 询 的 数 据 的 URI , 即 "Notes" 表 格 的 NOTE_URI 。
NOTE_PROJECTION : 表 示 查 询 结 果 中 要 返 回 的 列 。
NoteColumns . TYPE + "=" + Notes . TYPE_NOTE + " AND " + NoteColumns . PARENT_ID + "=0" : 表 示 查 询 条 件 , 即 根 据 "NoteColumns.TYPE" 字 段 的 值 等 于 "Notes.TYPE_NOTE" 且 "NoteColumns.PARENT_ID" 字 段 的 值 等 于 0 。
null : 表 示 查 询 条 件 的 参 数 值 , 此 处 不 需 要 参 数 。
null : 表 示 查 询 结 果 排 序 方 式 , 此 处 为 默 认 排 序 方 式 。
查 询 条 件 是 将 两 个 条 件 用 "AND" 运 算 符 连 接 起 来 , 其 中 :
NoteColumns . TYPE + "=" + Notes . TYPE_NOTE 表 示 筛 选 出 类 型 为 "Notes.TYPE_NOTE" ( 可 能 是 常 量 ) 的 笔 记 。
NoteColumns . PARENT_ID + "=0" 表 示 筛 选 出 父 文 件 夹 ID 为 0 的 笔 记 ( 可 能 是 代 表 根 目 录 ) 。
查 询 结 果 将 会 存 储 在 noteCursor 游 标 对 象 中 , 可 以 通 过 该 对 象 遍 历 获 取 查 询 结 果 的 每 条 记 录 。
* /
if ( noteCursor ! = null ) {
if ( noteCursor . moveToFirst ( ) ) {
@ -284,8 +338,10 @@ public class BackupUtils {
/ * *
* Get a print stream pointed to the file { @generateExportedTextFile }
* 生 成 导 出 的 文 本 文 件 , 并 返 回 一 个 指 向 该 文 件 的 打 印 流 。
* /
private PrintStream getExportToTextPrintStream ( ) {
// 生成将要导出的文件路径和名称
File file = generateFileMountedOnSDcard ( mContext , R . string . file_path ,
R . string . file_name_txt_format ) ;
if ( file = = null ) {
@ -296,6 +352,7 @@ public class BackupUtils {
mFileDirectory = mContext . getString ( R . string . file_path ) ;
PrintStream ps = null ;
try {
// 创建输出流并返回打印流
FileOutputStream fos = new FileOutputStream ( file ) ;
ps = new PrintStream ( fos ) ;
} catch ( FileNotFoundException e ) {
@ -311,22 +368,31 @@ public class BackupUtils {
/ * *
* Generate the text file to store imported data
* 该 方 法 的 功 能 是 根 据 提 供 的 资 源 ID 和 当 前 时 间 戳 生 成 一 个 存 储 导 入 数 据 的 文 本 文 件 , 并 返 回 该 文 件 对 象 。
* /
private static File generateFileMountedOnSDcard ( Context context , int filePathResId , int fileNameFormatResId ) {
// 创建一个StringBuilder来构建文件路径
StringBuilder sb = new StringBuilder ( ) ;
// 将外部存储路径添加到StringBuilder中
sb . append ( Environment . getExternalStorageDirectory ( ) ) ;
// 将文件路径的资源ID转换为实际路径添加到StringBuilder中
sb . append ( context . getString ( filePathResId ) ) ;
// 创建表示文件夹的File对象
File filedir = new File ( sb . toString ( ) ) ;
// 添加文件名和格式到StringBuilder中
sb . append ( context . getString (
fileNameFormatResId ,
DateFormat . format ( context . getString ( R . string . format_date_ymd ) ,
System . currentTimeMillis ( ) ) ) ) ;
// 创建表示文件的File对象
File file = new File ( sb . toString ( ) ) ;
try {
// 检查文件夹是否存在,不存在则创建
if ( ! filedir . exists ( ) ) {
filedir . mkdir ( ) ;
}
// 检查文件是否存在,不存在则创建
if ( ! file . exists ( ) ) {
file . createNewFile ( ) ;
}