From 6b2b4db1ec8ba33b1c6eb6d6ecba1797ac94067f Mon Sep 17 00:00:00 2001 From: XF1001 <1214358029@qq.com> Date: Fri, 4 Dec 2020 11:48:14 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E4=BB=A3=E7=A0=81=E6=A0=87?= =?UTF-8?q?=E6=B3=A8=E6=88=90=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.idea/caches/build_file_checksums.ser | Bin 538 -> 0 bytes .../.idea/markdown-navigator-enh.xml | 10 + src/Notes-master/.idea/markdown-navigator.xml | 46 +++ .../.idea/modules/Notes-master.iml | 4 + .../.idea/modules/app/Notes-master.app.iml | 4 + src/Notes-master/.idea/sonarIssues.xml | 39 +++ .../3aac5305cb73bfbdeb8078cd264d04323fa80e92 | 0 .../934a4e2abf19d28a53f6aeb1dcd99248c44b6892 | 5 + .../c182d0c9c237ea8a46a92ccaae9bb5c751923a88 | 3 + .../d187f1271655c3d91661a39fe6de395b6a9f290a | 3 + .../e094aec5c3e1b6f44539adff3114f5a1ad603ddc | 8 + .../f1226eeacd46c914d51f3d1a6d6f27377490d2a4 | 13 + .../f4a01d6a4fcb971362ec00a83903fd3902f52164 | 0 .../.idea/sonarlint/issuestore/index.pb | 15 + .../net/micode/notes/gtask/data/MetaData.java | 23 +- .../net/micode/notes/gtask/data/Node.java | 5 +- .../net/micode/notes/gtask/data/SqlData.java | 31 +- .../net/micode/notes/gtask/data/SqlNote.java | 49 +++- .../net/micode/notes/gtask/data/Task.java | 40 ++- .../net/micode/notes/gtask/data/TaskList.java | 47 +++- .../exception/ActionFailureException.java | 11 + .../exception/NetworkFailureException.java | 8 + .../notes/gtask/remote/GTaskASyncTask.java | 75 ++++- .../notes/gtask/remote/GTaskClient.java | 102 ++++++- .../notes/gtask/remote/GTaskManager.java | 266 +++++++++++++++--- .../notes/gtask/remote/GTaskSyncService.java | 64 ++++- src/Notes-master/local.properties | 4 +- 27 files changed, 778 insertions(+), 97 deletions(-) delete mode 100644 src/Notes-master/.idea/caches/build_file_checksums.ser create mode 100644 src/Notes-master/.idea/markdown-navigator-enh.xml create mode 100644 src/Notes-master/.idea/markdown-navigator.xml create mode 100644 src/Notes-master/.idea/sonarIssues.xml create mode 100644 src/Notes-master/.idea/sonarlint/issuestore/3/a/3aac5305cb73bfbdeb8078cd264d04323fa80e92 create mode 100644 src/Notes-master/.idea/sonarlint/issuestore/9/3/934a4e2abf19d28a53f6aeb1dcd99248c44b6892 create mode 100644 src/Notes-master/.idea/sonarlint/issuestore/c/1/c182d0c9c237ea8a46a92ccaae9bb5c751923a88 create mode 100644 src/Notes-master/.idea/sonarlint/issuestore/d/1/d187f1271655c3d91661a39fe6de395b6a9f290a create mode 100644 src/Notes-master/.idea/sonarlint/issuestore/e/0/e094aec5c3e1b6f44539adff3114f5a1ad603ddc create mode 100644 src/Notes-master/.idea/sonarlint/issuestore/f/1/f1226eeacd46c914d51f3d1a6d6f27377490d2a4 create mode 100644 src/Notes-master/.idea/sonarlint/issuestore/f/4/f4a01d6a4fcb971362ec00a83903fd3902f52164 create mode 100644 src/Notes-master/.idea/sonarlint/issuestore/index.pb diff --git a/src/Notes-master/.idea/caches/build_file_checksums.ser b/src/Notes-master/.idea/caches/build_file_checksums.ser deleted file mode 100644 index db95d1629fbd895ae872737f22a8ee98f9974cf9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 538 zcmZ4UmVvdnh`~NNKUXg?FQq6yGexf?KR>5fFEb@IQ7^qHF(oHeub?PDD>b=9F91S2 zm1gFoxMk*~I%lLNXBU^|7Q2L-Ts|(GuF1r}l-a(W(##Th_( zR`y#54~r#SWM*J;W8likPfT%3OfJbU@?_vF$tX%K&dAS6sVJ~_U;qK0at2z_zyOYkOxc*3?Qo+1gu{DdQxPhc)j(<%!CPNp8f?o zu{gD)Br`9)7^bn1p^kwKq;~l~slyX9w?5T~iVIHDSCuYd&~&zn2`x@7DvpV8Gc+(T zjDhNjK?G$%YEemMYH=ytlq<45H~SejvAk^Hobl#|Q(XxI4~jeC3S$$!3hTbkXV`Xq u$?-$WD>eZ=m6M;Gm;;I+3{yRXx2 + + + + + + + + + \ No newline at end of file diff --git a/src/Notes-master/.idea/markdown-navigator.xml b/src/Notes-master/.idea/markdown-navigator.xml new file mode 100644 index 0000000..57b84e5 --- /dev/null +++ b/src/Notes-master/.idea/markdown-navigator.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Notes-master/.idea/modules/Notes-master.iml b/src/Notes-master/.idea/modules/Notes-master.iml index 21ca423..7ec378f 100644 --- a/src/Notes-master/.idea/modules/Notes-master.iml +++ b/src/Notes-master/.idea/modules/Notes-master.iml @@ -17,4 +17,8 @@ + + \ No newline at end of file diff --git a/src/Notes-master/.idea/modules/app/Notes-master.app.iml b/src/Notes-master/.idea/modules/app/Notes-master.app.iml index e9f35bf..87830b7 100644 --- a/src/Notes-master/.idea/modules/app/Notes-master.app.iml +++ b/src/Notes-master/.idea/modules/app/Notes-master.app.iml @@ -50,4 +50,8 @@ + + \ No newline at end of file diff --git a/src/Notes-master/.idea/sonarIssues.xml b/src/Notes-master/.idea/sonarIssues.xml new file mode 100644 index 0000000..465ef5e --- /dev/null +++ b/src/Notes-master/.idea/sonarIssues.xml @@ -0,0 +1,39 @@ + + + + + + \ No newline at end of file diff --git a/src/Notes-master/.idea/sonarlint/issuestore/3/a/3aac5305cb73bfbdeb8078cd264d04323fa80e92 b/src/Notes-master/.idea/sonarlint/issuestore/3/a/3aac5305cb73bfbdeb8078cd264d04323fa80e92 new file mode 100644 index 0000000..e69de29 diff --git a/src/Notes-master/.idea/sonarlint/issuestore/9/3/934a4e2abf19d28a53f6aeb1dcd99248c44b6892 b/src/Notes-master/.idea/sonarlint/issuestore/9/3/934a4e2abf19d28a53f6aeb1dcd99248c44b6892 new file mode 100644 index 0000000..a0df7ff --- /dev/null +++ b/src/Notes-master/.idea/sonarlint/issuestore/9/3/934a4e2abf19d28a53f6aeb1dcd99248c44b6892 @@ -0,0 +1,5 @@ + +2 +java:S2386,"Make this member "protected".(X +n +java:S3776"RRefactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.( \ No newline at end of file diff --git a/src/Notes-master/.idea/sonarlint/issuestore/c/1/c182d0c9c237ea8a46a92ccaae9bb5c751923a88 b/src/Notes-master/.idea/sonarlint/issuestore/c/1/c182d0c9c237ea8a46a92ccaae9bb5c751923a88 new file mode 100644 index 0000000..fd4c7f2 --- /dev/null +++ b/src/Notes-master/.idea/sonarlint/issuestore/c/1/c182d0c9c237ea8a46a92ccaae9bb5c751923a88 @@ -0,0 +1,3 @@ + +[ +java:S1124"EReorder the modifiers to comply with the Java Language Specification.( \ No newline at end of file diff --git a/src/Notes-master/.idea/sonarlint/issuestore/d/1/d187f1271655c3d91661a39fe6de395b6a9f290a b/src/Notes-master/.idea/sonarlint/issuestore/d/1/d187f1271655c3d91661a39fe6de395b6a9f290a new file mode 100644 index 0000000..f59a333 --- /dev/null +++ b/src/Notes-master/.idea/sonarlint/issuestore/d/1/d187f1271655c3d91661a39fe6de395b6a9f290a @@ -0,0 +1,3 @@ + +i +java:S3776"RRefactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.(ǎ \ No newline at end of file diff --git a/src/Notes-master/.idea/sonarlint/issuestore/e/0/e094aec5c3e1b6f44539adff3114f5a1ad603ddc b/src/Notes-master/.idea/sonarlint/issuestore/e/0/e094aec5c3e1b6f44539adff3114f5a1ad603ddc new file mode 100644 index 0000000..75d7970 --- /dev/null +++ b/src/Notes-master/.idea/sonarlint/issuestore/e/0/e094aec5c3e1b6f44539adff3114f5a1ad603ddc @@ -0,0 +1,8 @@ + +r +java:S3923"[Remove this conditional structure or edit its code blocks so that they're not all the same.(ԡ + +java:S1319"pThe return type of this method should be an interface such as "List" rather than the implementation "ArrayList".(ﷺ +t +java:S2293*"YReplace the type specification in this constructor call with the diamond operator ("<>").(ʩ +Y java:S125"").( +u +java:S2293"YReplace the type specification in this constructor call with the diamond operator ("<>").( +n +java:S3776"RRefactor this method to reduce its Cognitive Complexity from 91 to the 15 allowed.( +2 +java:S2386."Make this member "protected".(u +u +java:S2293"YReplace the type specification in this constructor call with the diamond operator ("<>").( \ No newline at end of file diff --git a/src/Notes-master/.idea/sonarlint/issuestore/f/4/f4a01d6a4fcb971362ec00a83903fd3902f52164 b/src/Notes-master/.idea/sonarlint/issuestore/f/4/f4a01d6a4fcb971362ec00a83903fd3902f52164 new file mode 100644 index 0000000..e69de29 diff --git a/src/Notes-master/.idea/sonarlint/issuestore/index.pb b/src/Notes-master/.idea/sonarlint/issuestore/index.pb new file mode 100644 index 0000000..b513b71 --- /dev/null +++ b/src/Notes-master/.idea/sonarlint/issuestore/index.pb @@ -0,0 +1,15 @@ + +@ +app/build.gradle,f\4\f4a01d6a4fcb971362ec00a83903fd3902f52164 +k +;app/src/main/java/net/micode/notes/gtask/data/MetaData.java,c\1\c182d0c9c237ea8a46a92ccaae9bb5c751923a88 +g +7app/src/main/java/net/micode/notes/gtask/data/Node.java,3\a\3aac5305cb73bfbdeb8078cd264d04323fa80e92 +j +:app/src/main/java/net/micode/notes/gtask/data/SqlData.java,9\3\934a4e2abf19d28a53f6aeb1dcd99248c44b6892 +j +:app/src/main/java/net/micode/notes/gtask/data/SqlNote.java,f\1\f1226eeacd46c914d51f3d1a6d6f27377490d2a4 +g +7app/src/main/java/net/micode/notes/gtask/data/Task.java,d\1\d187f1271655c3d91661a39fe6de395b6a9f290a +k +;app/src/main/java/net/micode/notes/gtask/data/TaskList.java,e\0\e094aec5c3e1b6f44539adff3114f5a1ad603ddc \ No newline at end of file diff --git a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/MetaData.java b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/MetaData.java index 3a2050b..9250255 100644 --- a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/MetaData.java +++ b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/MetaData.java @@ -24,39 +24,51 @@ import net.micode.notes.tool.GTaskStringUtils; import org.json.JSONException; import org.json.JSONObject; - +// 功能描述:主要是描述数据属性(property)的信息,用来支持例如指示存储位置、历史数据、资源查找、文件记录等功能。 +//实现:继承task类,用于记录数据的变化 public class MetaData extends Task { + // 调用getSimpleName ()函数得到类的简写名称存入字符串TAG中 private final static String TAG = MetaData.class.getSimpleName(); - + // 相关GID文件 (GID文件用来保存帮助文件的信息) private String mRelatedGid = null; - + // 调用JSONObject库函数put (),Task类中的setNotes ()和setName ()函数,实现设置数据,即生成元数据库 public void setMeta(String gid, JSONObject metaInfo) { try { + // 将这对键值放入metaInfo这个jsonobject对象中 metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); + // 捕捉异常放入TAG } catch (JSONException e) { Log.e(TAG, "failed to put related gid"); } + // 设置便签,将json类的metainfo转换为String setNotes(metaInfo.toString()); + // 设置gtask名字 setName(GTaskStringUtils.META_NOTE_NAME); } - + // 获取相关联的Gid public String getRelatedGid() { return mRelatedGid; } @Override + //判断是否值得存放,即当前数据是否有效,若数据非空则返回真值 public boolean isWorthSaving() { return getNotes() != null; } @Override + //功能:使用远程json数据对象设置元数据内容 + //实现:调用父类Task中的setContentByRemoteJSON ()函数,如果获取的notes非空,则去掉获取notes的空格后存入metaInfo中,并从metaInfo获取键值来获取相关gid;如果为空,则抛出异常 + //参数:js属于JSONObject类,属于远程的数据 public void setContentByRemoteJSON(JSONObject js) { + //普通的直接引用。与this类似,super相当于是指向当前对象的父类,这样就可以用super.xxx来引用父类的成员。 super.setContentByRemoteJSON(js); if (getNotes() != null) { try { JSONObject metaInfo = new JSONObject(getNotes().trim()); mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); } catch (JSONException e) { + // 输出异常 Log.w(TAG, "failed to get related gid"); mRelatedGid = null; } @@ -64,17 +76,20 @@ public class MetaData extends Task { } @Override + //功能:使用本地json数据对象设置元数据内容,一般不会用到,若用到,则抛出异常 public void setContentByLocalJSON(JSONObject js) { // this function should not be called throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); } @Override + // 功能:从元数据内容中获取本地json对象,一般不会用到,若用到,则抛出异常 public JSONObject getLocalJSONFromContent() { throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); } @Override + // 功能:获取同步动作状态,一般不会用到,若用到,则抛出异常 public int getSyncAction(Cursor c) { throw new IllegalAccessError("MetaData:getSyncAction should not be called"); } diff --git a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/Node.java b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/Node.java index 63950e0..88eaa20 100644 --- a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/Node.java +++ b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/Node.java @@ -20,6 +20,7 @@ import android.database.Cursor; import org.json.JSONObject; +// 建立node类来提供模板,设置各种参数及定义各种函数,会在别的地方用到,定义了各种同步活动的标识码 public abstract class Node { public static final int SYNC_ACTION_NONE = 0; @@ -46,14 +47,14 @@ public abstract class Node { private long mLastModified; private boolean mDeleted; - + //实现:设置mgid为空,名字为空,最后一次修改时间为0(没有修改),mDeleted为false表示未删除。 public Node() { mGid = null; mName = ""; mLastModified = 0; mDeleted = false; } - + // 创建JSONObject对象,创建操作和更新操作,参数为int actionid public abstract JSONObject getCreateAction(int actionId); public abstract JSONObject getUpdateAction(int actionId); diff --git a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/SqlData.java b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/SqlData.java index d3ec3be..5c4f186 100644 --- a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/SqlData.java +++ b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/SqlData.java @@ -36,6 +36,7 @@ import org.json.JSONObject; public class SqlData { + //调用getSimpleName ()函数将类的简写名称存入string类型变量TAG中 private static final String TAG = SqlData.class.getSimpleName(); private static final int INVALID_ID = -99999; @@ -70,7 +71,8 @@ public class SqlData { private String mDataContentData3; private ContentValues mDiffDataValues; - + //功能:第一种构造函数,初始化 + //参数:类型为context,从上下文获取,初始化其中变量 public SqlData(Context context) { mContentResolver = context.getContentResolver(); mIsCreate = true; @@ -81,14 +83,17 @@ public class SqlData { mDataContentData3 = ""; mDiffDataValues = new ContentValues(); } - + //功能:第二中构造函数,初始化 + //实现:通过游标cursor c来获取数据,进行替换 + //参数:游标c是将传进来的参数替换掉初始的数值 public SqlData(Context context, Cursor c) { mContentResolver = context.getContentResolver(); mIsCreate = false; loadFromCursor(c); mDiffDataValues = new ContentValues(); } - + //功能:从光标处加载数据,DATA_ID_COLUMN等参数为0,1,2,3,4,应为获取五列参数 + //实现:用各种get函数获取相关值 private void loadFromCursor(Cursor c) { mDataId = c.getLong(DATA_ID_COLUMN); mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); @@ -96,16 +101,23 @@ public class SqlData { mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); } - + //功能:设置共享数据并提供异常抛出与处理机制 + //实现:首先js判断是否有DataColumns.ID,然后判断是否为第一种构造方式,将DataColumns.ID列和对应dataId列值加入到SQLData中, + //然后更新mDataId,最后判断并设置mDataMimeType、mDataContent等需共享的数据 public void setContent(JSONObject js) throws JSONException { + // 如果传入JSONObject有DataColumns.ID,则dataID = DataColumns.ID,否则设置为INVALID_ID=-99999 long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; + //如果mIsCreate为True(根据第一种构造方式)或者mDataId(当前数据的ID)与dataId(元数据的ID)不符。 + //第一种构造方式只是简单的初始化,没具体内容 if (mIsCreate || mDataId != dataId) { + // 将DataColumns.ID列和对应dataId列值加入到SQLData中 mDiffDataValues.put(DataColumns.ID, dataId); } mDataId = dataId; - + // 判断并更新 String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) : DataConstants.NOTE; + // 这个对象的MimeType不和(dataMimeType)共享数据一样,则将共享数据.MIME_TYPE加入数据库中 if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); } @@ -129,7 +141,8 @@ public class SqlData { } mDataContentData3 = dataContentData3; } - + //功能:获取共享的数据内容,并提供异常抛出与处理机制 + //实现:根据mIsCreate判断初始化类型,如果为第一种无内容的初始化,则返回null,否则,将要共享的内容存入JSONObject类的js中 public JSONObject getContent() throws JSONException { if (mIsCreate) { Log.e(TAG, "it seems that we haven't created this in database yet"); @@ -143,7 +156,9 @@ public class SqlData { js.put(DataColumns.DATA3, mDataContentData3); return js; } - + //功能:commit 函数用于把当前所做的修改保存到数据库 + //实现:首先判断构造方式,如果为第一种构造方式,判断是否为有效然后更新共享数据;如果为第二种构造方式,首先判断要共享的数据mDiffDataValues是否存在,然后确认版本,如果版本还没有确认,则记录id和data,如果已经确认,则更新对应版本ID,最后将要共享的数据清空,回到初始化,表示已经共享 + //参数:long类型的noteId,boolean类型的validateVersion用于版本确认,long类型的version为版本号 public void commit(long noteId, boolean validateVersion, long version) { if (mIsCreate) { @@ -182,7 +197,7 @@ public class SqlData { mDiffDataValues.clear(); mIsCreate = false; } - + // 获取当前ID public long getId() { return mDataId; } diff --git a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/SqlNote.java b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/SqlNote.java index f4d0011..5050a1d 100644 --- a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/SqlNote.java +++ b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/SqlNote.java @@ -77,13 +77,13 @@ public class SqlNote { public static final int WIDGET_TYPE_COLUMN = 11; public static final int SYNC_ID_COLUMN = 12; - + // 本地修改的符号 public static final int LOCAL_MODIFIED_COLUMN = 13; - + // 在进入临时文件夹之前,原始的父id(未使用) public static final int ORIGIN_PARENT_ID_COLUMN = 14; - + // 用户id public static final int GTASK_ID_COLUMN = 15; - + //版本 public static final int VERSION_COLUMN = 16; private Context mContext; @@ -121,7 +121,8 @@ public class SqlNote { private ContentValues mDiffNoteValues; private ArrayList mDataList; - + //第一种构造函数 + //构造函数,参数只有context,初始化新建的对象中的所有变量 public SqlNote(Context context) { mContext = context; mContentResolver = context.getContentResolver(); @@ -142,7 +143,8 @@ public class SqlNote { mDiffNoteValues = new ContentValues(); mDataList = new ArrayList(); } - + //第二种构造函数 + //参数有context和cursor,对cursor指向的对象进行初始化 public SqlNote(Context context, Cursor c) { mContext = context; mContentResolver = context.getContentResolver(); @@ -154,7 +156,8 @@ public class SqlNote { mDiffNoteValues = new ContentValues(); } - + //第三种构造方式 + //构造函数,参数有 context 和 id,对 id 指向的对象进行初始化 public SqlNote(Context context, long id) { mContext = context; mContentResolver = context.getContentResolver(); @@ -165,9 +168,11 @@ public class SqlNote { loadDataContent(); mDiffNoteValues = new ContentValues(); } - + //功能:通过id从光标处加载数据 + //实现:通过id获取ContentResolver中的相应内容,并赋给cursor,如果有内容就将移入文档,并再次等待光标的内容;否则日志显示警告,最后关闭光标,方法完毕 private void loadFromCursor(long id) { Cursor c = null; + //避免异常 try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)", new String[] { @@ -184,7 +189,8 @@ public class SqlNote { c.close(); } } - + //功能:通过cursor从光标加载数据 + //实现:各种get类型的函数,通过光标c获取参数 private void loadFromCursor(Cursor c) { mId = c.getLong(ID_COLUMN); mAlertDate = c.getLong(ALERTED_DATE_COLUMN); @@ -199,7 +205,10 @@ public class SqlNote { mWidgetType = c.getInt(WIDGET_TYPE_COLUMN); mVersion = c.getLong(VERSION_COLUMN); } - + //功能:通过content机制获取共享数据并加载到数据库当前光标处 + //实现:获取ID对应content内容,如果查询到该note的id确实有对应项,即cursor有对应,获取ID对应content内容 + //如果光标处有内容,提示note无数据warning,当记录数量不为0,则循环直到记录不存在,不断地取出记录放到DataList中 + //如果cursor为空,警告,最终判断光标为空,则关闭 private void loadDataContent() { Cursor c = null; mDataList.clear(); @@ -225,16 +234,23 @@ public class SqlNote { c.close(); } } - + //功能:设置通过content机制用于共享的数据信息 + //实现:先判断是不是系统文件夹,再判断是否为文件夹类型,如果共享数据存在摘要,则将其赋给声明的 snippet变量,否则变量为空 + //如果SQLNote采用的是第一种构造方式,或者snippet为空,则将snippet这一项键值存入contentvalue中,尽管是“” + //如果是note,获取提示日期,设置用于共享数据的信息,最后遍历 dataArray,查找 id 为 dataId 的数据,更新sqlData public boolean setContent(JSONObject js) { try { JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + // 判断是不是系统文件夹 if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { Log.w(TAG, "cannot set system folder"); + // 判断是否为文件夹类型 } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { + // snnipet——片段、片断 // for folder we can only update the snnipet and type String snippet = note.has(NoteColumns.SNIPPET) ? note .getString(NoteColumns.SNIPPET) : ""; + // 如果SQLNote采用的是第一种构造方式,或者snippet为空,则将snippet这一项键值存入contentvalue中,尽管是“” if (mIsCreate || !mSnippet.equals(snippet)) { mDiffNoteValues.put(NoteColumns.SNIPPET, snippet); } @@ -246,7 +262,9 @@ public class SqlNote { mDiffNoteValues.put(NoteColumns.TYPE, type); } mType = type; + //如果是NOTE } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) { + // 获取提示日期 JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID; if (mIsCreate || mId != id) { @@ -358,7 +376,10 @@ public class SqlNote { } return true; } - + //功能:获取content机制提供的数据并加载到note中 + //实现:判断采用的是第一种构造方式,自然实施初始化而已,显示错误:没创建数据库 + //再判断如果对象的类型是note类型,这个对象的13项按键值对方式(ID, mId)、(ALERTED_DATE, mAlertDate)···加入note中 + //类型为系统文件或目录文件时,将id,类型,以及摘要,存入jsonobject,然后对应META_HEAD_NOTE键,存入共享 public JSONObject getContent() { try { JSONObject js = new JSONObject(); @@ -392,7 +413,9 @@ public class SqlNote { } } js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); + // 类型为系统文件或目录文件时 } else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) { + // 将id,类型,以及摘要,存入jsonobject,然后对应META_HEAD_NOTE键,存入共享 note.put(NoteColumns.ID, mId); note.put(NoteColumns.TYPE, mType); note.put(NoteColumns.SNIPPET, mSnippet); @@ -406,7 +429,7 @@ public class SqlNote { } return null; } - + // 设置当前 id 的父 id public void setParentId(long id) { mParentId = id; mDiffNoteValues.put(NoteColumns.PARENT_ID, id); diff --git a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/Task.java b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/Task.java index 1c42e99..97d24c9 100644 --- a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/Task.java +++ b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/Task.java @@ -31,7 +31,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; - +// 创建Task类,继承自父类Node public class Task extends Node { private static final String TAG = Task.class.getSimpleName(); @@ -44,7 +44,8 @@ public class Task extends Node { private Task mPriorSibling; private TaskList mParent; - + //功能:Task类的构造函数,对对象进行初始化 + //实现:调用父类构造方法,对变量初始化 public Task() { super(); mCompleted = false; @@ -53,12 +54,16 @@ public class Task extends Node { mParent = null; mMetaInfo = null; } - + //功能:获取创建的action。获取当前创建的action的属性值,比如说它的id号、类型、index值。 + //实现:首先新建一个 JSONObject 的对象js用来存放同步过程中所用到的 task 信息,然后在共享数据js里存入action_type、action_id、index + //再新建一个 JSONObject 对象entity打包存放 name,creator id,type task,判断是否有notes,有则将其也放入entity中 + //最后将entity、parent_id、dest_parent_type、list_id存入js中,判断如果存在优先兄弟 task,则将其 id 放入 js 中 public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); try { // action_type + // 共享数据存入动作类型action_ type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); @@ -69,11 +74,13 @@ public class Task extends Node { js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this)); // entity_delta + // 新建一个 JSONObject 对象打包存放 name,creator id,type task JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, GTaskStringUtils.GTASK_JSON_TYPE_TASK); + // 如果存在 notes ,则将其也放入 entity 中 if (getNotes() != null) { entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); } @@ -102,7 +109,8 @@ public class Task extends Node { return js; } - + //功能:获取更新的action + //实现:和上面getCreateAction差不多 public JSONObject getUpdateAction(int actionId) { JSONObject js = new JSONObject(); @@ -134,7 +142,8 @@ public class Task extends Node { return js; } - + //功能:通过远端的jsonobject获取任务内容 + //实现:首先判断js是否非空,接着判断如果有id这个变量,就进行gid的设置,同样方法设置last_modified、name等变量 public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try { @@ -174,7 +183,10 @@ public class Task extends Node { } } } - + //功能:通过本地的jsonobject获取内容 + //实现:首先判断js是否为空或者没有META_HEAD_NOTE,为空则显示日志,创建JSONObject类note存入js的META_HEAD_NOTE + //创建JSONArray类dataArray存入META_HEAD_DATA,判断note的TYPE不能匹配Notes的TYPE则无效 + //最后遍历 dataArray 查找与数据库中DataConstants.NOTE 记录信息一致的 data public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) || !js.has(GTaskStringUtils.META_HEAD_DATA)) { @@ -203,7 +215,8 @@ public class Task extends Node { } } } - + //功能:通过Content机制获取本地JSON数据。 + //实现:通过判断元数据是否存在,如果不存在,则创建一个新的对象,并将信息完善。最后返回js对象。 public JSONObject getLocalJSONFromContent() { String name = getName(); try { @@ -246,7 +259,8 @@ public class Task extends Node { return null; } } - + //功能:设置元数据信息 + //参数:MetaData类 metaData,元数据 public void setMetaInfo(MetaData metaData) { if (metaData != null && metaData.getNotes() != null) { try { @@ -257,7 +271,8 @@ public class Task extends Node { } } } - + //功能:获取同步操作 + //实现:判断mMetaInfo是否被删除或者远程数据被删除,然后判断光标传入的note ID是否匹配并判断本地的便签数据是否已更新 public int getSyncAction(Cursor c) { try { JSONObject noteInfo = null; @@ -310,16 +325,17 @@ public class Task extends Node { return SYNC_ACTION_ERROR; } - + //功能:判断是否值得存放,即当前数据是否有效,若数据非空 或 名字合法存在且去除空格后的名字长度大于零则返回真值。 + // 其它:trim() 的作用是去除字符串前后的空格:public String trim()返回字符串的副本,忽略前导空白和尾部空白 public boolean isWorthSaving() { return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) || (getNotes() != null && getNotes().trim().length() > 0); } - + // 将task设置为修改完毕 public void setCompleted(boolean completed) { this.mCompleted = completed; } - + // 设定mNotes public void setNotes(String notes) { this.mNotes = notes; } diff --git a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/TaskList.java b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/TaskList.java index 6444a4e..d1a70c9 100644 --- a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/TaskList.java +++ b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/data/TaskList.java @@ -42,7 +42,8 @@ public class TaskList extends Node { mChildren = new ArrayList(); mIndex = 1; } - + //功能:获取生成动作 + //实现:创建JSONObject js存入action_type、action_id、index、entity_delta,生成并返回一个包含了一定数据的JSONObject实体 public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); @@ -73,7 +74,8 @@ public class TaskList extends Node { return js; } - + //功能:接受更新action,返回jsonobject + //实现:同上面getCreateAction类似 public JSONObject getUpdateAction(int actionId) { JSONObject js = new JSONObject(); @@ -102,7 +104,8 @@ public class TaskList extends Node { return js; } - + //功能:通过云端 JSON 数据设置实例化对象 js 的内容 + //实现:通过传入的js判断非空,然后设置id、last_modified、name public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try { @@ -128,7 +131,8 @@ public class TaskList extends Node { } } } - + //功能:通过本地 JSON 数据设置对象 js 内容 + //实现:实现方法也与上面setContentByRemoteJSON类似,不过设置的是根据本地js传入 public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); @@ -159,7 +163,8 @@ public class TaskList extends Node { e.printStackTrace(); } } - + //功能:从content获取本地json + //实现:getname()函数获取文件夹名称 public JSONObject getLocalJSONFromContent() { try { JSONObject js = new JSONObject(); @@ -185,7 +190,12 @@ public class TaskList extends Node { return null; } } - + //功能:通过 cursor 获取同步信息 + //实现:首先判断本地记录是否被修改,如果没有修改,再判断光标处的ID与最后一次修改是否相等 + //如果相等则返回“不同步”,如果不等则返回“同步” + //如果本地记录已经修改,则判断获取的ID是否匹配,若不匹配则返回同步动作失败 + //其它:“同步”是指将remote端更新至local端。另外为什么 if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) + //为什么这个判断最后 if else返回的都是SYNC_ACTION_UPDATE_REMOTE? public int getSyncAction(Cursor c) { try { if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { @@ -222,7 +232,10 @@ public class TaskList extends Node { public int getChildTaskCount() { return mChildren.size(); } - + //功能:在当前任务表中添加新的子任务 + //实现:如果传入的子任务不是空并且当前的子任务序列中不含有该任务,就将这个任务加入子任务中 + //然后设置兄弟任务优先级,若子任务为空,返回 NULL,否则返回子任务数量-1,最后设置该任务的父任务 + //返回值为是否成功添加任务 public boolean addChildTask(Task task) { boolean ret = false; if (task != null && !mChildren.contains(task)) { @@ -236,7 +249,9 @@ public class TaskList extends Node { } return ret; } - + //功能:在当前任务表的指定位置添加新的任务。 + //实现:先获取要添加的任务在任务表中的位置,在索引位置添加任务,update the task list + //然后让插入的任务与前后连在一块,将下一个任务设置兄弟任务优先级 public boolean addChildTask(Task task, int index) { if (index < 0 || index > mChildren.size()) { Log.e(TAG, "add child task: invalid index"); @@ -262,7 +277,8 @@ public class TaskList extends Node { return true; } - + //功能:删除TaskList中的一个Task + //实现:方法同addChildTask()类似 public boolean removeChildTask(Task task) { boolean ret = false; int index = mChildren.indexOf(task); @@ -283,7 +299,7 @@ public class TaskList extends Node { } return ret; } - + // 功能:将当前TaskList中含有的某个Task移到index位置 public boolean moveChildTask(Task task, int index) { if (index < 0 || index >= mChildren.size()) { @@ -301,7 +317,8 @@ public class TaskList extends Node { return true; return (removeChildTask(task) && addChildTask(task, index)); } - + //功能:通过Gid寻找子任务(未使用) + //实现:从头至尾遍历整个任务列表判断任务的gid与传入的gid是否相等 public Task findChildTaskByGid(String gid) { for (int i = 0; i < mChildren.size(); i++) { Task t = mChildren.get(i); @@ -311,11 +328,11 @@ public class TaskList extends Node { } return null; } - + // 获取子任务索引列表 public int getChildTaskIndex(Task task) { return mChildren.indexOf(task); } - + // 返回指定index的Task public Task getChildTaskByIndex(int index) { if (index < 0 || index >= mChildren.size()) { Log.e(TAG, "getTaskByIndex: invalid index"); @@ -323,7 +340,7 @@ public class TaskList extends Node { } return mChildren.get(index); } - + // 返回指定gid的Task public Task getChilTaskByGid(String gid) { for (Task task : mChildren) { if (task.getGid().equals(gid)) @@ -331,7 +348,7 @@ public class TaskList extends Node { } return null; } - + //获取子任务列表 public ArrayList getChildTaskList() { return this.mChildren; } diff --git a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/exception/ActionFailureException.java b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/exception/ActionFailureException.java index 15504be..b46c8c4 100644 --- a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/exception/ActionFailureException.java +++ b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/exception/ActionFailureException.java @@ -14,12 +14,23 @@ * limitations under the License. */ +// 小米便签行为异常处理 + package net.micode.notes.gtask.exception; +/** + * 异常处理类,继承自RuntimeException类 + */ public class ActionFailureException extends RuntimeException { + // 定义:serialVersionUID相当于java类的身份证,主要用于版本控制。 + // 作用:验证版本一致性,如果不一致会导致反序列化的时候版本不一致的异常。 private static final long serialVersionUID = 4425249765923293627L; + /** + * 构造函数(包括下面的两个构造函数) + */ public ActionFailureException() { + // super引用父类成分,this引用当前类 super(); } diff --git a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/exception/NetworkFailureException.java b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/exception/NetworkFailureException.java index b08cfb1..000f124 100644 --- a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/exception/NetworkFailureException.java +++ b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/exception/NetworkFailureException.java @@ -14,11 +14,19 @@ * limitations under the License. */ +// 小米便签网络异常处理 + package net.micode.notes.gtask.exception; +/** + * 网络异常类,继承自Exception类 + */ public class NetworkFailureException extends Exception { private static final long serialVersionUID = 2107610287180234136L; + /** + * 构造函数(以下三个都是) + */ public NetworkFailureException() { super(); } diff --git a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskASyncTask.java b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskASyncTask.java index 777f88c..d7503ef 100644 --- a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskASyncTask.java +++ b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskASyncTask.java @@ -1,3 +1,6 @@ +//【1】 AsyncTask,意为“异步任务”,是指执行某一功能但不要求立刻得到结果,因而可以正常进行其他操作。 +//【2】Android使用异步任务是为了使主线程保持较高的响应性,把耗时的任务交给AsyncTask等工作者线程处理。 +//【3】AsyncTask方便快捷,过程可控,适合简单的异步操作,实际可用于倒计时功能、进度条显示等。 /* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) @@ -24,90 +27,151 @@ import android.content.Context; import android.content.Intent; import android.os.AsyncTask; +// 自动生成的该程序包名下的R.java,用于引用已建立的资源 import net.micode.notes.R; import net.micode.notes.ui.NotesListActivity; import net.micode.notes.ui.NotesPreferenceActivity; - +/** + *【1】定义:GTaskASyncTask类继承于抽象类 AsyncTask。 + *【2】功能:处理GTask的异步任务,实现UI线程与后台线程间的通讯。 + *【3】主要方法: + private void showNotification(int tickerId, String content) ——向用户提示当前同步的状态,是一个用于交互的方法。 + protected Integer doInBackground(Void... unused) ——重点方法,在后台线程执行,定义了所要完成的任务,比较耗时。 + protected void onProgressUpdate(String... progress) —— 此方法在主线程执行,用于显示任务执行的进度条。 + protected void onPostExecute(Integer result) ——相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 + */ public class GTaskASyncTask extends AsyncTask { - + // 定义:static int变量存储GTask同步通知的ID。 private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; + /** + * 方法:声明一个OnCompleteListener接口,其onComplete ()用来初始化异步功能(在GTaskSyncService里实现,发送一个空广播)。 + */ public interface OnCompleteListener { void onComplete(); } private Context mContext; + // 对象: 实例化一个通知管理器类。 private NotificationManager mNotifiManager; private GTaskManager mTaskManager; private OnCompleteListener mOnCompleteListener; + /** + * 【1】作用:GTaskASyncTask的构造函数。 + * 【2】参数: + context类——可以访问application的资源和相关的类,比如说启动Activity、启动和停止Service、发送广播消息(Intent)、注册广播消息(Intent)接收者、访问apk中各种资源(如Resources和AssetManager)、访问Package的相关信息、APK的各种权限管理等。 + OnCompleteListener接口——上文已声明。 + */ public GTaskASyncTask(Context context, OnCompleteListener listener) { mContext = context; mOnCompleteListener = listener; + // getSystemService是Activity的一个方法,可根据传入的参数获得应服务的对象。这里以Context的NOTIFICATION_SERVICE为对象。 mNotifiManager = (NotificationManager) mContext .getSystemService(Context.NOTIFICATION_SERVICE); + // getInstance ()函数用于使用单例模式创建类的实例。 mTaskManager = GTaskManager.getInstance(); } + /** + *【1】功能:取消同步 + *【2】方法:对实例mTaskManager调用中断同步函数。 + */ public void cancelSync() { mTaskManager.cancelSync(); } + /** + *【1】功能:显示信息 + *【2】方法:通过publishProgress发布String []里的信息,系统将会调用onProgressUpdate ()方法更新进度条 + */ public void publishProgess(String message) { publishProgress(new String[] { message }); } + /** + * 【1】功能:显示通知 + * 【2】方法:向用户通知同步的进程,是一个用于交互的方法 + */ private void showNotification(int tickerId, String content) { + // 实例化一个通知对象 Notification notification = new Notification(R.drawable.notification, mContext .getString(tickerId), System.currentTimeMillis()); + // 添加默认呼吸灯提醒 notification.defaults = Notification.DEFAULT_LIGHTS; + // 用户点击清除按钮或点击通知后,提示会自动消失 notification.flags = Notification.FLAG_AUTO_CANCEL; + // 类: PendingIntent 是 Android 提供的一种用于外部程序调起自身程序的能力,生命周期不与主程序相关。 PendingIntent pendingIntent; + // 如果同步失败,就从系统取得一个用于启动NotesPreferenceActivity的PendingIntent对象。 if (tickerId != R.string.ticker_success) { pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesPreferenceActivity.class), 0); } else { + // 如果同步成功,就从系统取得一个用于启动NotesListActivity的PendingIntent对象。 pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesListActivity.class), 0); } + // 设置一个关于最新事件的通知。 //notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, // pendingIntent); + // 将通知加入状态栏,通过notify ()方法来执行一个notification的消息。 mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); } + //Java5的元数据,是自动加上去的一个标志,目的是告知你下面这个方法是从父类/接口继承过来的,需要重写一次。 @Override + /** + *【1】功能:异步执行后台线程将要完成的任务。 + *【2】实现:使用publishProgress ( )方法传入UI界面的相关参数,并调用onProgressUpdate()。 + *【3】参数:Void... unused 表示不会传入参数。(注:java类型后面跟三个点是代表可以接受多个实际参数,这里的多个指的是不限个数。) + */ protected Integer doInBackground(Void... unused) { + // 将同步账户的名字传递给登陆的进程,利用publishProgress方法来跟进该任务的执行进度。 publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity .getSyncAccountName(mContext))); + // 进行后台的同步工作,并返回其同步结果。 return mTaskManager.sync(mContext, this); } @Override + /** + *【1】功能:显示进度的更新。 + *【2】方法:在调用publishProgress时,该方法被执行,并将进度信息更新到UI组件中。 + *【3】参数:string类型数组。 + */ protected void onProgressUpdate(String... progress) { showNotification(R.string.ticker_syncing, progress[0]); + // 判断mContext是否是GTaskSyncService的实例,若是则发送一个广播。 if (mContext instanceof GTaskSyncService) { ((GTaskSyncService) mContext).sendBroadcast(progress[0]); } } @Override + /** + *【1】功能:执行完后台任务后更新UI,显示结果即进度条 。 + *【2】方法: 在dolnBackground执行完成后,该方法会被UI线程调用。根据不同的result(同步成功、网络错误、内部错误、同步取消)执行不同的操作,并将后台的计算结果传递到UI进程,在界面上展示出来。 + *【3】参数:integer类型,是AsyncTask初始化的第三个参数result。 + */ protected void onPostExecute(Integer result) { + // 若同步成功,则显示成功并展示出同步的账户与最新同步时间。 if (result == GTaskManager.STATE_SUCCESS) { showNotification(R.string.ticker_success, mContext.getString( R.string.success_sync_account, mTaskManager.getSyncAccount())); NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); - } else if (result == GTaskManager.STATE_NETWORK_ERROR) { + } else if (result == GTaskManager.STATE_NETWORK_ERROR) { // 因网络错误而同步失败 showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network)); - } else if (result == GTaskManager.STATE_INTERNAL_ERROR) { + } else if (result == GTaskManager.STATE_INTERNAL_ERROR) { // 因内部访问问题而同步失败 showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal)); - } else if (result == GTaskManager.STATE_SYNC_CANCELLED) { + } else if (result == GTaskManager.STATE_SYNC_CANCELLED) { // 因主动取消而同步失败 showNotification(R.string.ticker_cancel, mContext .getString(R.string.error_sync_cancelled)); } @@ -115,6 +179,7 @@ public class GTaskASyncTask extends AsyncTask { new Thread(new Runnable() { public void run() { + // 接口回调,返回到主线程中。 mOnCompleteListener.onComplete(); } }).start(); diff --git a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskClient.java b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskClient.java index c2ac22f..cffd796 100644 --- a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskClient.java +++ b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskClient.java @@ -60,10 +60,14 @@ import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; - +/** + * 功能:登录Google账户(可用于自动登录),更新账户信息,获取任务列表等 + */ public class GTaskClient { + // 设置本类的TAG,作用是日志的打印输出和标识此类 private static final String TAG = GTaskClient.class.getSimpleName(); + // 谷歌邮箱的URL private static final String GTASK_URL = "https://mail.google.com/tasks/"; private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; @@ -90,6 +94,10 @@ public class GTaskClient { private JSONArray mUpdateArray; + /** + * 定义:GTaskClient的构造函数 + * 功能:给GTaskClient类中各个成员赋初始值,即初始化客户端 + */ private GTaskClient() { mHttpClient = null; mGetUrl = GTASK_GET_URL; @@ -102,6 +110,10 @@ public class GTaskClient { mUpdateArray = null; } + /** + * 功能:使用单例模式创建GTaskClient的实例 + * 实现:当mInstance的值为NULL时,新建一个GTaskClient的实例赋值给mInstance + */ public static synchronized GTaskClient getInstance() { if (mInstance == null) { mInstance = new GTaskClient(); @@ -109,27 +121,39 @@ public class GTaskClient { return mInstance; } + /** + * 功能:实现登录 + * 方法:使用下面定义的loginGoogleAccount( )方法登录Google账户 ,使用下面定义的loginGtask( )方法登录gtask,登录成功返回true,登录失败返回false + * 参数:要执行登录操作的活动 + */ public boolean login(Activity activity) { // we suppose that the cookie would expire after 5 minutes // then we need to re-login + // 设置登录时间为5分钟,若超时则重新登录 final long interval = (long) 1000 * 60 * 5; if (mLastLoginTime + interval < System.currentTimeMillis()) { mLoggedin = false; } // need to re-login after account switch + // 功能:切换用户需重新登录 + // 方法:判断条件为,在已登录条件下,将已登录用户名和要切换的用户名进行比较,若字符串不相等,则登录失败,将登录状态设置为未登录(false) if (mLoggedin && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity .getSyncAccountName(activity))) { mLoggedin = false; } + // 若符合以上条件,则显示“已经登录成功”字样 if (mLoggedin) { Log.d(TAG, "already logged in"); return true; } + // 更新登陆的时间 mLastLoginTime = System.currentTimeMillis(); + // 功能:判断是否登入google账号 + // 方法:使用下面定义的loginGoogleAccount( )方法,返回登录令牌(string),即用用户提供的账户密码去登陆Google账户 String authToken = loginGoogleAccount(activity, false); if (authToken == null) { Log.e(TAG, "login google account failed"); @@ -137,6 +161,8 @@ public class GTaskClient { } // login with custom domain if necessary + // 功能:在google账号登陆成功后,尝试能否使用用户的域名登陆gtask + // 方法:判断输入的字符串结尾有无谷歌域名,然后设置用户的getUrl和postUrl if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() .endsWith("googlemail.com"))) { StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); @@ -152,6 +178,7 @@ public class GTaskClient { } // try to login with google official url + // 若前面的尝试失败,则尝试使用官方的域名登陆 if (!mLoggedin) { mGetUrl = GTASK_GET_URL; mPostUrl = GTASK_POST_URL; @@ -164,9 +191,16 @@ public class GTaskClient { return true; } + /** + * 功能:登录谷歌账号 + * 方法:使用令牌机制,使用AccountManager管理注册账号 + */ private String loginGoogleAccount(Activity activity, boolean invalidateToken) { + // 使用登录令牌核实用户信息和判断登录状态,既保证登录顺畅,又避免cookie无限期保存导致账号被盗 String authToken; + // 账户管理器集中管理已注册账号和密码 AccountManager accountManager = AccountManager.get(activity); + // 将账号管理对象中所有的谷歌账号存入account数组里备用 Account[] accounts = accountManager.getAccountsByType("com.google"); if (accounts.length == 0) { @@ -207,6 +241,11 @@ public class GTaskClient { return authToken; } + /** + * 功能:尝试登录Gtask,此方法作为登录的预判,用于判断令牌对于登录gtask账号是否有效 + * 方法:此函数使用后面定义的loginGtask( )方法和已获得的令牌进行一次登陆,如果成功则返回true,如果不成功则登陆google的账户重新获取令牌。 + * 参数:要进行登录的活动,登录gtask账号使用的令牌 + */ private boolean tryToLoginGtask(Activity activity, String authToken) { if (!loginGtask(authToken)) { // maybe the auth token is out of date, now let's invalidate the @@ -225,6 +264,11 @@ public class GTaskClient { return true; } + /** + * 功能:通过令牌登录Gtask,登录成功返回true,失败放回false + * 方法:使用mgeturl和令牌构造loginurl,通过get方法得到http的对象,获取cookie、版本号 + * 参数:已获得的令牌 + */ private boolean loginGtask(String authToken) { int timeoutConnection = 10000; int timeoutSocket = 15000; @@ -280,10 +324,17 @@ public class GTaskClient { return true; } + /** + * 功能:获取活动ID+1并将其返回 + */ private int getActionId() { return mActionId++; } + /** + * 功能:返回一个用于向网络传输数据的httpPost对象 + * 实现:新创建一个httpPost对象,设置头部信息 + */ private HttpPost createHttpPost() { HttpPost httpPost = new HttpPost(mPostUrl); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); @@ -291,6 +342,11 @@ public class GTaskClient { return httpPost; } + /** + * 功能:通过URL获取响应后返回的数据,也就是网络上的数据和资源,但可能会抛出异常 + * 实现:使用getContentEncoding ( )方法获取一个字节流,对内容进行格式转换,返回值是获取到的资源内容 + * 参数:HttpEntity对象 + */ private String getResponseContent(HttpEntity entity) throws IOException { String contentEncoding = null; if (entity.getContentEncoding() != null) { @@ -323,6 +379,11 @@ public class GTaskClient { } } + /** + * 功能:通过JSON向客户端发送请求并调用getResponseContent方法来获取返回的信息 + * 方法:通过JSON发送请求,利用UrlEncodedFormEntity entity和httpPost.setEntity把js中的内容放置到httpPost中,将资源存储到string中,再次放入json后返回 + * 参数:JSONObject对象 + */ private JSONObject postRequest(JSONObject js) throws NetworkFailureException { if (!mLoggedin) { Log.e(TAG, "please login first"); @@ -360,6 +421,10 @@ public class GTaskClient { } } + /** + * 功能: 创建单个任务,设置好action的链表、client_version、post + * 方法:创建单个任务,通过json获取TASK中的内容并创建对应的jsPost,通过postRequest方法获取任务的返回信息,使用setGid方法设置task的new_id + */ public void createTask(Task task) throws NetworkFailureException { commitUpdate(); try { @@ -386,6 +451,11 @@ public class GTaskClient { } } + /** + * 功能:创建任务列表,与createTask类似,区别在于最后设置的是tasklist的gid + * 方法:定义了JSONObject对象jsPost,通过jsPost设置好动作列表,用户名版本 + * 参数:Tasklist类对象(自定义类) + */ public void createTaskList(TaskList tasklist) throws NetworkFailureException { commitUpdate(); try { @@ -412,6 +482,10 @@ public class GTaskClient { } } + /** + * 功能:提交更新数据 + * 方法:判断mUpdateArray的值并进行相应操作。若更新则使用jsPost.put( )添加对应关系,提交UpdateArray和ClientVersion 。 + */ public void commitUpdate() throws NetworkFailureException { if (mUpdateArray != null) { try { @@ -433,6 +507,11 @@ public class GTaskClient { } } + /** + * 功能:添加更新的节点,对更新列表进行操作 + * 方法:调用commitUpdate ()实现 + * 参数:Node类对象(自定义类) + */ public void addUpdateNode(Node node) throws NetworkFailureException { if (node != null) { // too many update items may result in an error @@ -447,6 +526,10 @@ public class GTaskClient { } } + /** + * 功能:移动任务到不同的task列表中 + * 方法:先得到任务ID,之后更新任务移动后的相关属性值,最后将完成更新后的请求发送 + */ public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException { commitUpdate(); @@ -486,6 +569,10 @@ public class GTaskClient { } } + /** + * 功能:删除操作节点 + * 方法:利用JSON,删除后使用postRequest发送删除后的结果 + */ public void deleteNode(Node node) throws NetworkFailureException { commitUpdate(); try { @@ -509,6 +596,10 @@ public class GTaskClient { } } + /** + * 功能:获取任务列表 + * 方法:先通过GetURI使用getResponseContent从网上获取数据,然后筛选出 "_setup(" 到 “)}” 的部分,并从中获取GTASK_JSON_LISTS的内容返回 + */ public JSONArray getTaskLists() throws NetworkFailureException { if (!mLoggedin) { Log.e(TAG, "please login first"); @@ -547,6 +638,9 @@ public class GTaskClient { } } + /** + * 功能:获取任务列表 + */ public JSONArray getTaskList(String listGid) throws NetworkFailureException { commitUpdate(); try { @@ -575,10 +669,16 @@ public class GTaskClient { } } + /** + * 功能:获取同步账户 + */ public Account getSyncAccount() { return mAccount; } + /** + * 功能:重置更新内容 + */ public void resetUpdateArray() { mUpdateArray = null; } diff --git a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskManager.java b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskManager.java index 613f659..e358891 100644 --- a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskManager.java +++ b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskManager.java @@ -14,6 +14,8 @@ * limitations under the License. */ +// 实现同步功能的主函数 + package net.micode.notes.gtask.remote; import android.app.Activity; @@ -47,18 +49,20 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; - +/** + * 【1】定义:GTask管理类 + * 【2】功能:提供同步本地和远端的任务,初始化任务列表,同步内容、文件夹,添加、更新本地和远端结点,刷新本地同步任务ID等功能 + */ public class GTaskManager { + // 定义了一系列静态变量来显示GTask当前的状态 + //(1)设置GTask的TAG private static final String TAG = GTaskManager.class.getSimpleName(); + //(2)进程状态:用0、1、2、3、4分别表示成功、网络错误、内部错误、同步中、取消同步 public static final int STATE_SUCCESS = 0; - public static final int STATE_NETWORK_ERROR = 1; - public static final int STATE_INTERNAL_ERROR = 2; - public static final int STATE_SYNC_IN_PROGRESS = 3; - public static final int STATE_SYNC_CANCELLED = 4; private static GTaskManager mInstance = null; @@ -87,18 +91,32 @@ public class GTaskManager { private HashMap mNidToGid; + /** + * 类的构造函数,对其内部变量进行初始化 + */ private GTaskManager() { + // 正在同步标识,false代表未同步 mSyncing = false; + // 取消同步标识,false代表可以取消 mCancelled = false; + // 任务列表以哈希表的形式创立 mGTaskListHashMap = new HashMap(); + // 创建一个节点表 mGTaskHashMap = new HashMap(); + // 创建一个删除本地ID的map mMetaHashMap = new HashMap(); mMetaList = null; mLocalDeleteIdMap = new HashSet(); + // 创建Gid(google id)到Nid(节点 id)的映射 mGidToNid = new HashMap(); + // 与mGidToNid相反的映射 mNidToGid = new HashMap(); } + /** + * 功能:获取一个实例,通过GTaskManager()新建一个mInstance + *(注:synchronized指明该函数可以运行在多线程下) + */ public static synchronized GTaskManager getInstance() { if (mInstance == null) { mInstance = new GTaskManager(); @@ -106,20 +124,29 @@ public class GTaskManager { return mInstance; } + /** + * 功能:获取当前的操作并更新至GTask中 + */ public synchronized void setActivityContext(Activity activity) { // used for getting authtoken mActivity = activity; } + /** + * 功能:实现同步操作,包括同步前设置环境,进行同步,处理异常,同步结束清空缓存 + */ public int sync(Context context, GTaskASyncTask asyncTask) { + // 当前状态是正在同步中 if (mSyncing) { - Log.d(TAG, "Sync is in progress"); - return STATE_SYNC_IN_PROGRESS; + Log.d(TAG, "Sync is in progress"); // 声明同步正在运行 + return STATE_SYNC_IN_PROGRESS; // 返回同步状态 } + // 对同步时GTaskManager的属性进行更新 mContext = context; mContentResolver = mContext.getContentResolver(); mSyncing = true; mCancelled = false; + // 对各种结构进行清空 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -127,35 +154,42 @@ public class GTaskManager { mGidToNid.clear(); mNidToGid.clear(); + // 异常处理程序 try { + // 实例化一个GTask用户对象 GTaskClient client = GTaskClient.getInstance(); + // 重置更新数组 client.resetUpdateArray(); // login google task + // 若此时未取消同步操作,进行登录操作,尝试登录到google task if (!mCancelled) { + // 如果mActivity登录不上则抛出一个异常 if (!client.login(mActivity)) { throw new NetworkFailureException("login google task failed"); } } // get the task list from google + // 从google客户端获取任务列表 asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); initGTaskList(); // do content sync work + // 内容同步操作 asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); syncContent(); - } catch (NetworkFailureException e) { + } catch (NetworkFailureException e) { // 网络异常 Log.e(TAG, e.toString()); return STATE_NETWORK_ERROR; - } catch (ActionFailureException e) { + } catch (ActionFailureException e) { //操作未完成 Log.e(TAG, e.toString()); return STATE_INTERNAL_ERROR; - } catch (Exception e) { + } catch (Exception e) { //内部错误 Log.e(TAG, e.toString()); e.printStackTrace(); return STATE_INTERNAL_ERROR; - } finally { + } finally { //在同步操作结束后,更新GTaskManager的属性 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -165,37 +199,53 @@ public class GTaskManager { mSyncing = false; } + // 若在同步时操作未取消,则返回同步成功,否则返回同步操作取消 return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; } + /** + * 功能:初始化GTask列表,将google上的JSONTaskList转为本地任务列表 + * @throws NetworkFailureException + */ private void initGTaskList() throws NetworkFailureException { + // 判断是否取消该次操作 if (mCancelled) return; + // 实例化一个GTask用户对象 GTaskClient client = GTaskClient.getInstance(); try { + // 客户端获取任务列表jsTaskLists JSONArray jsTaskLists = client.getTaskLists(); // init meta list first + // 初始化元数据列表 mMetaList = null; + // 不断把任务列表里的每个任务通过远程JSON设置内容并把元数据都放到哈希表中 for (int i = 0; i < jsTaskLists.length(); i++) { - JSONObject object = jsTaskLists.getJSONObject(i); - String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); - String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); + JSONObject object = jsTaskLists.getJSONObject(i); // 取出单个JSON对象 + String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 获取它的id + String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); // 获取它的名字 + // 新建数组,并为新建的数组设定内容 if (name .equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { mMetaList = new TaskList(); mMetaList.setContentByRemoteJSON(object); // load meta data + // 获取元数据 JSONArray jsMetas = client.getTaskList(gid); + // 把jsMetas里的每一个有识别码的metaData都放到哈希表中 for (int j = 0; j < jsMetas.length(); j++) { object = (JSONObject) jsMetas.getJSONObject(j); MetaData metaData = new MetaData(); metaData.setContentByRemoteJSON(object); + // 判断前面获取的元数据有无价值加入列表中 if (metaData.isWorthSaving()) { mMetaList.addChildTask(metaData); + // 操作getGid:取得组识别码函数 if (metaData.getGid() != null) { + // 把元数据放到哈希表中 mMetaHashMap.put(metaData.getRelatedGid(), metaData); } } @@ -204,6 +254,7 @@ public class GTaskManager { } // create meta list if not existed + // 如果元数据列表不存在,则在客户端创建一个 if (mMetaList == null) { mMetaList = new TaskList(); mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX @@ -212,7 +263,9 @@ public class GTaskManager { } // init task list + // 循环将任务列表中的内容初始化,与前面对元数据列表的初始化过程类似 for (int i = 0; i < jsTaskLists.length(); i++) { + // 获取列表中每一个节点的属性 JSONObject object = jsTaskLists.getJSONObject(i); String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); @@ -221,11 +274,13 @@ public class GTaskManager { && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { TaskList tasklist = new TaskList(); + // 对任务列表的内容进行设置 tasklist.setContentByRemoteJSON(object); mGTaskListHashMap.put(gid, tasklist); mGTaskHashMap.put(gid, tasklist); // load tasks + // 获取任务id号 JSONArray jsTasks = client.getTaskList(gid); for (int j = 0; j < jsTasks.length(); j++) { object = (JSONObject) jsTasks.getJSONObject(j); @@ -240,46 +295,60 @@ public class GTaskManager { } } } - } catch (JSONException e) { + } catch (JSONException e) { // 初始化时捕捉异常 Log.e(TAG, e.toString()); e.printStackTrace(); + // 抛出异常,提交 JSONObject 失败 throw new ActionFailureException("initGTaskList: handing JSONObject failed"); } } + /** + * 功能:实现内容同步的操作 + * @throws NetworkFailureException + */ private void syncContent() throws NetworkFailureException { int syncType; Cursor c = null; String gid; Node node; + // 初始化本地删除列表 mLocalDeleteIdMap.clear(); + // 判断该操作是否已被取消 if (mCancelled) { return; } // for local deleted note + // 对于删除本地便签的操作的同步 try { + // 定位要删除的节点位置 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type<>? AND parent_id=?)", new String[] { String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) }, null); + // 若获取到的待删除便签不为空,则进行同步操作 if (c != null) { + // 方法:如果节点位置不为空,用while循环把节点一个一个的往后移动,并不多更新gid和哈希表,直到最后移动到列表尾不能再移动,把c指向的note放到本地删除节点ID的图里。 while (c.moveToNext()) { gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); if (node != null) { + // 将待删除的节点对应的google id从映射表中移除 mGTaskHashMap.remove(gid); + // 在远程删除对应节点 doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); } + // 在本地删除记录中添加这一项记录 mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); } - } else { + } else { // 或者发现没有待删除的节点,报错 Log.w(TAG, "failed to query trash folder"); } - } finally { + } finally { // 最后把c关闭并重置 if (c != null) { c.close(); c = null; @@ -287,10 +356,13 @@ public class GTaskManager { } // sync folder first + // 对文件夹进行同步 syncFolder(); // for note existing in database + // 对已经存在与数据库的节点进行同步 try { + // c指针指向待操作的便签位置 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type=? AND parent_id<>?)", new String[] { String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER) @@ -301,25 +373,31 @@ public class GTaskManager { node = mGTaskHashMap.get(gid); if (node != null) { mGTaskHashMap.remove(gid); + // 建立google id到节点id的映射 mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); + // 建立节点id到google id的映射 mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); + // 更新此时的同步类型 syncType = node.getSyncAction(c); } else { + // 若本地增加了内容,则远程也要增加内容 if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { // local add syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { // remote delete + // 本地删除 syncType = Node.SYNC_ACTION_DEL_LOCAL; } } doContentSync(syncType, node, c); } } else { + // 警告,询问数据库中已创建的便签失败 Log.w(TAG, "failed to query existing note in database"); } - } finally { + } finally { // 最后关闭c并重置 if (c != null) { c.close(); c = null; @@ -327,16 +405,19 @@ public class GTaskManager { } // go through remaining items + // 用迭代器扫描剩下的项目,逐个进行同步 Iterator> iter = mGTaskHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); node = entry.getValue(); + // 在本地增加这些节点 doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); } // mCancelled can be set by another thread, so we neet to check one by // one // clear local delete table + // 终止标识有可能被其他线程改变,因此需要一个个进行检查 if (!mCancelled) { if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { throw new ActionFailureException("failed to batch-delete local deleted notes"); @@ -344,6 +425,7 @@ public class GTaskManager { } // refresh local sync id + // 更新同步表 if (!mCancelled) { GTaskClient.getInstance().commitUpdate(); refreshLocalSyncId(); @@ -351,6 +433,10 @@ public class GTaskManager { } + /** + * 功能:同步文件夹 + * @throws NetworkFailureException + */ private void syncFolder() throws NetworkFailureException { Cursor c = null; String gid; @@ -363,27 +449,32 @@ public class GTaskManager { // for root folder try { + // 使指针指向根文件夹的位置 c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); if (c != null) { c.moveToNext(); + // 获取指针指向内容对应的gid gid = c.getString(SqlNote.GTASK_ID_COLUMN); + // 获取该gid所代表的节点 node = mGTaskHashMap.get(gid); if (node != null) { mGTaskHashMap.remove(gid); + // 双向添加映射表 mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); // for system folder, only update remote name if necessary + // 若当前访问的文件夹是系统文件夹则只需要更新 if (!node.getName().equals( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); - } else { + } else { // 若非系统文件夹则在远程进行增加节点操作 doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); } - } else { + } else { // 警告,询问根目录文件夹失败 Log.w(TAG, "failed to query root folder"); } - } finally { + } finally { // 结束操作之后,将指针指向内容关闭并将指针置空 if (c != null) { c.close(); c = null; @@ -391,8 +482,11 @@ public class GTaskManager { } // for call-note folder + // 对存放电话号码便签的文件夹的同步操作 try { + // 使指针指向文件夹的位置 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", + // 添加MIUI文件前缀 new String[] { String.valueOf(Notes.ID_CALL_RECORD_FOLDER) }, null); @@ -406,18 +500,19 @@ public class GTaskManager { mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); // for system folder, only update remote name if // necessary + // 若当前访问的文件夹是系统文件夹则只需要更新 if (!node.getName().equals( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); - } else { + } else { // 若非系统文件夹则在远程进行增加节点操作 doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); } } - } else { + } else { // 警告,询问通讯便签文件夹失败 Log.w(TAG, "failed to query call note folder"); } - } finally { + } finally { // 结束操作之后,将指针指向内容关闭并将指针置空 if (c != null) { c.close(); c = null; @@ -425,12 +520,15 @@ public class GTaskManager { } // for local existing folders + // 对于本地已存在的文件的操作 try { + // 使指针指向第一个文件夹的位置 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type=? AND parent_id<>?)", new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER) }, NoteColumns.TYPE + " DESC"); if (c != null) { + // 使指针遍历所有的文件夹 while (c.moveToNext()) { gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); @@ -438,19 +536,21 @@ public class GTaskManager { mGTaskHashMap.remove(gid); mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); + // 更新同步类型 syncType = node.getSyncAction(c); - } else { + } else { // 若本地增加了内容,则远程也要增加内容 if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { // local add syncType = Node.SYNC_ACTION_ADD_REMOTE; - } else { + } else { // 若远程删除了内容,则本地也要删除内容 // remote delete syncType = Node.SYNC_ACTION_DEL_LOCAL; } } + // 进行同步操作 doContentSync(syncType, node, c); } - } else { + } else { // 警告,询问已创建的文件夹失败 Log.w(TAG, "failed to query existing folder"); } } finally { @@ -461,6 +561,8 @@ public class GTaskManager { } // for remote add folders + // 对于在远程增添的内容,将其在本地同步 + // 使用迭代器对远程增添的内容进行遍历 Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); @@ -468,14 +570,21 @@ public class GTaskManager { node = entry.getValue(); if (mGTaskHashMap.containsKey(gid)) { mGTaskHashMap.remove(gid); + // 进行本地增添操作 doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); } } if (!mCancelled) + // 如果没有取消,在GTsk的客户端进行实例的提交更新 GTaskClient.getInstance().commitUpdate(); } + /** + * 功能:内容同步 + * 方法:依据不同的同步类型去调用不同的函数,以达到本地与远程同步的实际操作 + * @throws NetworkFailureException + */ private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -483,13 +592,17 @@ public class GTaskManager { MetaData meta; if (c != null) { + // 根据不同的同步类型来选择不同的操作 switch (syncType) { + // 本地添加 case Node.SYNC_ACTION_ADD_LOCAL: addLocalNode(node); break; + // 远程添加 case Node.SYNC_ACTION_ADD_REMOTE: addRemoteNode(node, c); break; + // 本地删除 case Node.SYNC_ACTION_DEL_LOCAL: meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); if (meta != null) { @@ -497,6 +610,7 @@ public class GTaskManager { } mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); break; + // 远程删除 case Node.SYNC_ACTION_DEL_REMOTE: meta = mMetaHashMap.get(node.getGid()); if (meta != null) { @@ -504,52 +618,72 @@ public class GTaskManager { } GTaskClient.getInstance().deleteNode(node); break; + // 本地更新 case Node.SYNC_ACTION_UPDATE_LOCAL: updateLocalNode(node, c); break; + // 远程更新 case Node.SYNC_ACTION_UPDATE_REMOTE: updateRemoteNode(node, c); break; + // 更新出现冲突时 case Node.SYNC_ACTION_UPDATE_CONFLICT: // merging both modifications maybe a good idea // right now just use local update simply updateRemoteNode(node, c); break; + // 空操作 case Node.SYNC_ACTION_NONE: break; + // 操作错误 case Node.SYNC_ACTION_ERROR: + // 抛出异常,未知的同步行为类型 default: throw new ActionFailureException("unkown sync action type"); } } } + /** + * 功能:添加本地节点 + * 参数:待添加的本地节点 + */ private void addLocalNode(Node node) throws NetworkFailureException { + // 检查是否取消这次操作 if (mCancelled) { return; } SqlNote sqlNote; + // 若待增添节点为任务列表中的节点,进一步操作 + //(instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。) if (node instanceof TaskList) { + // 在根目录中增加节点 if (node.getName().equals( + // 判断node节点的名字是否是MIUI系统文件夹的默认文件夹:preffix是访问页面的前缀,用来指定页面存放的文件夹,然后加上文件夹默认值。 GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); - } else if (node.getName().equals( + } else if (node.getName().equals( // 判断是否在存电话的文件夹里 GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); - } else { + } else { //若没有存放的文件夹,则将其放在根文件夹中 sqlNote = new SqlNote(mContext); + // 从本地任务列表中获取内容 sqlNote.setContent(node.getLocalJSONFromContent()); sqlNote.setParentId(Notes.ID_ROOT_FOLDER); } - } else { + } else { // 如果Node不在任务列表中,则删除不合法的节点和数据 sqlNote = new SqlNote(mContext); + // 从待增添节点中获取jsonobject对象 JSONObject js = node.getLocalJSONFromContent(); - try { + try { //异常判断 if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { + // 获取对应便签的jsonobject对象 JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + // 判断便签中是否有条目 if (note.has(NoteColumns.ID)) { long id = note.getLong(NoteColumns.ID); + // 若便签条目的id已无效,在便签中将其删除 if (DataUtils.existInNoteDatabase(mContentResolver, id)) { // the id is not available, have to create a new one note.remove(NoteColumns.ID); @@ -557,6 +691,7 @@ public class GTaskManager { } } + // 以下为判断便签中的数据条目 if (js.has(GTaskStringUtils.META_HEAD_DATA)) { JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); for (int i = 0; i < dataArray.length(); i++) { @@ -572,12 +707,15 @@ public class GTaskManager { } } - } catch (JSONException e) { + } catch (JSONException e) { // 对于异常进行处理 + // 获取异常类型和异常详细消息 Log.w(TAG, e.toString()); e.printStackTrace(); } + // 删除不合法的节点后,把js的内容更新到sqlNote中 sqlNote.setContent(js); + // 找到父任务的ID号,并作为sqlNote的父任务的ID,没有父任务则报错 Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); if (parentId == null) { Log.e(TAG, "cannot find task's parent id locally"); @@ -587,40 +725,59 @@ public class GTaskManager { } // create the local node + // 用getGid函数获取node节点的Gid,用于设置本地Gtask的ID sqlNote.setGtaskId(node.getGid()); sqlNote.commit(false); // update gid-nid mapping + // 更新gid与nid的映射表 mGidToNid.put(node.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), node.getGid()); // update meta + //更新远程的数据 updateRemoteMeta(node.getGid(), sqlNote); } + /** + * 功能:更新本地节点 + * 参数:一个是待更新的节点,一个是指向待增加位置的指针 + * @throws NetworkFailureException + */ private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } + // 新建一个sql节点并将内容存储进Node中 SqlNote sqlNote; // update the note locally sqlNote = new SqlNote(mContext, c); + // 利用待更新节点中的内容对数据库节点进行设置 sqlNote.setContent(node.getLocalJSONFromContent()); + // 设置父任务的ID Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) : new Long(Notes.ID_ROOT_FOLDER); + // 当不能找到父任务id时报错 if (parentId == null) { Log.e(TAG, "cannot find task's parent id locally"); throw new ActionFailureException("cannot update local node"); } + // 把上面更新的父任务ID赋值给sqlNote sqlNote.setParentId(parentId.longValue()); sqlNote.commit(true); // update meta info + // 更新远程的节点信息 updateRemoteMeta(node.getGid(), sqlNote); } + /** + * 功能:增加远程节点 + * 参数:一个是待更新的节点,一个是指向待增加位置的指针 + * @throws NetworkFailureException + */ private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -630,6 +787,7 @@ public class GTaskManager { Node n; // update remotely + // 如果sqlNote是节点类型,则设置好它的参数并更新哈希表,再把节点更新到远程数据里 if (sqlNote.isNoteType()) { Task task = new Task(); task.setContentByLocalJSON(sqlNote.getContent()); @@ -650,6 +808,7 @@ public class GTaskManager { TaskList tasklist = null; // we need to skip folder if it has already existed + // 如果文件夹已经存在则需要跳过,否则根据sqlNote的ID创建新的文件夹 String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) folderName += GTaskStringUtils.FOLDER_DEFAULT; @@ -658,12 +817,14 @@ public class GTaskManager { else folderName += sqlNote.getSnippet(); + // 使用迭代器对gtasklist进行遍历 Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); String gid = entry.getKey(); TaskList list = entry.getValue(); + // 若寻找到的任务列表已存在,则直接在里面更新 if (list.getName().equals(folderName)) { tasklist = list; if (mGTaskHashMap.containsKey(gid)) { @@ -674,6 +835,7 @@ public class GTaskManager { } // no match we can add now + // 若找不到任务列表,则创建一个新的任务列表 if (tasklist == null) { tasklist = new TaskList(); tasklist.setContentByLocalJSON(sqlNote.getContent()); @@ -684,42 +846,57 @@ public class GTaskManager { } // update local note + // 更新本地节点 sqlNote.setGtaskId(n.getGid()); sqlNote.commit(false); sqlNote.resetLocalModified(); sqlNote.commit(true); // gid-id mapping + // 更新gid与nid的映射表 mGidToNid.put(n.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), n.getGid()); } + /** + * 功能:更新远程结点 + * 参数:node是要更新的结点,c是数据库的指针 + * @throws NetworkFailureException + */ private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } + // 在指针指向处创建一个新的节点 SqlNote sqlNote = new SqlNote(mContext, c); // update remotely + // 对远程GTask的节点进行更新 node.setContentByLocalJSON(sqlNote.getContent()); GTaskClient.getInstance().addUpdateNode(node); // update meta + // 更新数据 updateRemoteMeta(node.getGid(), sqlNote); // move task if necessary + // 有必要的话对任务进行移动 if (sqlNote.isNoteType()) { Task task = (Task) node; + // 找到该任务列表的上一级列表 TaskList preParentList = task.getParent(); + // 获取上一级任务列表的gid String curParentGid = mNidToGid.get(sqlNote.getParentId()); if (curParentGid == null) { Log.e(TAG, "cannot find task's parent tasklist"); throw new ActionFailureException("cannot update remote task"); } + // 通过HashMap找到对应Gid的TaskList TaskList curParentList = mGTaskListHashMap.get(curParentGid); + // 若两个上一级任务列表不一致,进行任务的移动,从之前的任务列表中移动到该列表中 if (preParentList != curParentList) { preParentList.removeChildTask(task); curParentList.addChildTask(task); @@ -728,13 +905,23 @@ public class GTaskManager { } // clear local modified flag + // 清空本地已经被修改的标志 sqlNote.resetLocalModified(); sqlNote.commit(true); } + /** + * 功能:更新远程结点的数据(与上一个函数不同的是这里只更新数据) + * 参数:gid是要更新的数据对应的在数据库中的结点id,sqlnote用于获得数据内容 + * @throws NetworkFailureException + */ private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { + // 对节点的类型进行判断,只有节点类型符合要求才进一步操作 if (sqlNote != null && sqlNote.isNoteType()) { + // 通过gid获取元数据 MetaData metaData = mMetaHashMap.get(gid); + // 如果数据不为空,则设置好元,从客户端获取更新元节点后的信息内容 + // 否则新建一个元并设置好参数,设置好meta链表的子节点,更新哈希表,用户端获取用元数据创建任务后的信息内容 if (metaData != null) { metaData.setMeta(gid, sqlNote.getContent()); GTaskClient.getInstance().addUpdateNode(metaData); @@ -748,12 +935,17 @@ public class GTaskManager { } } + /** + * 功能:刷新本地同步的ID + * @throws NetworkFailureException + */ private void refreshLocalSyncId() throws NetworkFailureException { if (mCancelled) { return; } // get the latest gtask list + // 获取最新的gtask list mGTaskHashMap.clear(); mGTaskListHashMap.clear(); mMetaHashMap.clear(); @@ -761,6 +953,7 @@ public class GTaskManager { Cursor c = null; try { + // c作为光标定位,用query进行查询 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type<>? AND parent_id<>?)", new String[] { String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) @@ -782,9 +975,10 @@ public class GTaskManager { } } } else { + // 警告,询问本地待同步更新的便签 id 失败 Log.w(TAG, "failed to query local note to refresh sync id"); } - } finally { + } finally { // 结束操作之后,将指针指向内容关闭并将指针置空 if (c != null) { c.close(); c = null; @@ -792,10 +986,16 @@ public class GTaskManager { } } + /** + * 功能:获取同步账户,通过客户端调用函数获取名字作为信息返回 + */ public String getSyncAccount() { return GTaskClient.getInstance().getSyncAccount().name; } + /** + * 功能:取消同步,置mCancelled为true + */ public void cancelSync() { mCancelled = true; } diff --git a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskSyncService.java b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskSyncService.java index cca36f7..e98b563 100644 --- a/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskSyncService.java +++ b/src/Notes-master/app/src/main/java/net/micode/notes/gtask/remote/GTaskSyncService.java @@ -14,6 +14,8 @@ * limitations under the License. */ +// Service是Android四大组件之一,通常用作在后台处理耗时的逻辑,不用与用户进行交互,即使应用被销毁依然可以继续工作。 + package net.micode.notes.gtask.remote; import android.app.Activity; @@ -23,25 +25,48 @@ import android.content.Intent; import android.os.Bundle; import android.os.IBinder; +/**【1】定义:GTaskASyncService类继承于应用组件Service。 + * 【2】功能:提供GTask的同步服务。 + * 【3】主要方法: + * private void startSync () ——启动一个同步工作 + * private void cancelSync () ——取消同步 + * public void onCreate () + * public int onStartCommand (Intent intent, int flags, int startId) ——service生命周期的组成部分,相当于重启service(比如在被暂停之后),而不是创建一个新的service + * public void onLowMemory ()—— 在没有内存的情况下,如果存在service则结束该service + * public IBinder onBind () + * public void sendBroadcast (String msg)——发送同步的相关通知 + * public static void startSync (Activity activity) + * public static void cancelSync (Context context) + * public static boolean isSyncing ()——判读是否在进行同步 + * public static String getProgressString ()—— 获取当前进度的信息 + */ public class GTaskSyncService extends Service { + // 定义了一系列静态变量,用来表示同步操作的状态。 + //(1)同步行为类型 public final static String ACTION_STRING_NAME = "sync_action_type"; + //(2)同步状态:用数字0、1、2分别表示开始同步、取消同步、同步无效 public final static int ACTION_START_SYNC = 0; - public final static int ACTION_CANCEL_SYNC = 1; - public final static int ACTION_INVALID = 2; + //(3)服务广播的名称 public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; + //(4)表示正在同步中 public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; + //(5)进程消息 public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; private static GTaskASyncTask mSyncTask = null; private static String mSyncProgress = ""; + /** + * 功能:开始同步操作 + * 方法:如果当前没有同步工作,则申请一个task并把指针指向新任务,广播并执行。如果有同步工作则不做操作。 + */ private void startSync() { if (mSyncTask == null) { mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { @@ -56,6 +81,10 @@ public class GTaskSyncService extends Service { } } + /** + * 功能:取消同步操作 + * 方法: 判断同步的任务是否为空。如果正在同步,则取消同步工作。 + */ private void cancelSync() { if (mSyncTask != null) { mSyncTask.cancelSync(); @@ -63,13 +92,22 @@ public class GTaskSyncService extends Service { } @Override + /** + * 功能:初始化任务 + * 方法:直接把mSyncTask指针的值置空 + */ public void onCreate() { mSyncTask = null; } @Override + /** + * 功能:告诉系统如何重启服务,如判断是否异常终止后重新启动,在何种情况下异常终止等。 + */ public int onStartCommand(Intent intent, int flags, int startId) { + // Bundle类可携带数据,用于存放key-value明值对应形式的值。 Bundle bundle = intent.getExtras(); + // 判断当前的同步状态,根据开始或取消,执行对应操作 if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { case ACTION_START_SYNC: @@ -87,16 +125,26 @@ public class GTaskSyncService extends Service { } @Override + /** + * 功能:内存不足的处理函数 + * 方法:如果当前有任务在执行,则调用cancelSync ()取消这个任务 + */ public void onLowMemory() { if (mSyncTask != null) { mSyncTask.cancelSync(); } } + /** + * 功能: service服务中的绑定操作 + */ public IBinder onBind(Intent intent) { return null; } + /** + * 功能:执行发送广播内容的操作 + */ public void sendBroadcast(String msg) { mSyncProgress = msg; Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); @@ -105,6 +153,9 @@ public class GTaskSyncService extends Service { sendBroadcast(intent); } + /** + * 功能:开始同步 + */ public static void startSync(Activity activity) { GTaskManager.getInstance().setActivityContext(activity); Intent intent = new Intent(activity, GTaskSyncService.class); @@ -112,16 +163,25 @@ public class GTaskSyncService extends Service { activity.startService(intent); } + /** + * 功能:取消同步 + */ public static void cancelSync(Context context) { Intent intent = new Intent(context, GTaskSyncService.class); intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); context.startService(intent); } + /** + * 功能:判断此时是否处在同步状态 + */ public static boolean isSyncing() { return mSyncTask != null; } + /** + * 功能:返回当前的执行状态 + */ public static String getProgressString() { return mSyncProgress; } diff --git a/src/Notes-master/local.properties b/src/Notes-master/local.properties index 11be78c..fd1d5d6 100644 --- a/src/Notes-master/local.properties +++ b/src/Notes-master/local.properties @@ -4,5 +4,5 @@ # Location of the SDK. This is only used by Gradle. # For customization when using a Version Control System, please read the # header note. -#Sat Nov 21 11:26:00 CST 2020 -sdk.dir=D\:\\Sdk +#Wed Dec 02 20:10:32 CST 2020 +sdk.dir=C\:\\Users\\95459\\AppData\\Local\\Android\\Sdk