这是gtask的全部代码和它的注释;
-
次代码精读我只对gtask包进行了注释,有错误希望网友能指点一二,共同进步。一、data包 1、MetaData.java package net.micode.notes.gtask.data; public class MetaData extends Task { /* * 功能描述:得到类的简写名称存入字符串TAG中 * 实现过程:调用getSimpleName ()函数 */ private final static String TAG = MetaData.class.getSimpleName(); private String mRelatedGid = null; /* * 功能描述:设置数据,即生成元数据库 * 实现过程:调用JSONObject库函数put (),Task类中的setNotes ()和setName ()函数 * 参数注解: */ public void setMeta(String gid, JSONObject metaInfo) { //对函数块进行注释 try { metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); /* * 将这对键值放入metaInfo这个jsonobject对象中 */ } catch (JSONException e) { Log.e(TAG, "failed to put related gid"); /* * 输出错误信息 */ } setNotes(metaInfo.toString()); setName(GTaskStringUtils.META_NOTE_NAME); } /* * 功能描述:获取相关联的Gid */ public String getRelatedGid() { return mRelatedGid; } /* * 功能描述:判断当前数据是否为空,若为空则返回真即值得保存 * Made By CuiCan */ @Override public boolean isWorthSaving() { return getNotes() != null; } /* * 功能描述:使用远程json数据对象设置元数据内容 * 实现过程:调用父类Task中的setContentByRemoteJSON ()函数,并 * 参数注解: */ @Override public void setContentByRemoteJSON(JSONObject js) { 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; } } } /* * 功能描述:使用本地json数据对象设置元数据内容,一般不会用到,若用到,则抛出异常 * Made By CuiCan */ @Override public void setContentByLocalJSON(JSONObject js) { // this function should not be called throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); /* * 传递非法参数异常 */ } /* * 功能描述:从元数据内容中获取本地json对象,一般不会用到,若用到,则抛出异常 * Made By CuiCan */ @Override public JSONObject getLocalJSONFromContent() { throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); /* * 传递非法参数异常 * Made By Cui Can */ } /* * 功能描述:获取同步动作状态,一般不会用到,若用到,则抛出异常 * Made By CuiCan */ @Override public int getSyncAction(Cursor c) { throw new IllegalAccessError("MetaData:getSyncAction should not be called"); /* * 传递非法参数异常 * Made By Cui Can */ } } 1、Node.java package net.micode.notes.gtask.data; import android.database.Cursor; import org.json.JSONObject; /** * 应该是同步操作的基础数据类型,定义了相关指示同步操作的常量 * 关键字:abstract */ public abstract class Node { //定义了各种用于表征同步状态的常量 public static final int SYNC_ACTION_NONE = 0;// 本地和云端都无可更新内容(即本地和云端内容一致) public static final int SYNC_ACTION_ADD_REMOTE = 1;// 需要在远程云端增加内容 public static final int SYNC_ACTION_ADD_LOCAL = 2;// 需要在本地增加内容 public static final int SYNC_ACTION_DEL_REMOTE = 3;// 需要在远程云端删除内容 public static final int SYNC_ACTION_DEL_LOCAL = 4;// 需要在本地删除内容 public static final int SYNC_ACTION_UPDATE_REMOTE = 5;// 需要将本地内容更新到远程云端 public static final int SYNC_ACTION_UPDATE_LOCAL = 6;// 需要将远程云端内容更新到本地 public static final int SYNC_ACTION_UPDATE_CONFLICT = 7;// 同步出现冲突 public static final int SYNC_ACTION_ERROR = 8;// 同步出现错误 private String mGid; private String mName; private long mLastModified;//记录最后一次修改时间 private boolean mDeleted;//表征是否被删除 public Node() { mGid = null; mName = ""; mLastModified = 0; mDeleted = false; } public abstract JSONObject getCreateAction(int actionId); public abstract JSONObject getUpdateAction(int actionId); public abstract void setContentByRemoteJSON(JSONObject js); public abstract void setContentByLocalJSON(JSONObject js); public abstract JSONObject getLocalJSONFromContent(); public abstract int getSyncAction(Cursor c); public void setGid(String gid) { this.mGid = gid; } public void setName(String name) { this.mName = name; } public void setLastModified(long lastModified) { this.mLastModified = lastModified; } public void setDeleted(boolean deleted) { this.mDeleted = deleted; } public String getGid() { return this.mGid; } public String getName() { return this.mName; } public long getLastModified() { return this.mLastModified; } public boolean getDeleted() { return this.mDeleted; } } 2、SqlData.java /* * Description:用于支持小米便签最底层的数据库相关操作,和sqlnote的关系上是子集关系,即data是note的子集(节点)。 * SqlData其实就是也就是所谓数据中的数据 */ package net.micode.notes.gtask.data; /* * 功能描述: * 实现过程: * 参数注解: * Made By CuiCan */ public class SqlData { /* * 功能描述:得到类的简写名称存入字符串TAG中 * 实现过程:调用getSimpleName ()函数 * Made By CuiCan */ private static final String TAG = SqlData.class.getSimpleName(); private static final int INVALID_ID = -99999; =为mDataId置初始值-99999 /** * 来自Notes类中定义的DataColumn中的一些常量 */ // 集合了interface DataColumns中所有SF常量 public static final String[] PROJECTION_DATA = new String[] { DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, DataColumns.DATA3 }; /** * 以下五个变量作为sql表中5列的编号 */ public static final int DATA_ID_COLUMN = 0; public static final int DATA_MIME_TYPE_COLUMN = 1; public static final int DATA_CONTENT_COLUMN = 2; public static final int DATA_CONTENT_DATA_1_COLUMN = 3; public static final int DATA_CONTENT_DATA_3_COLUMN = 4; private ContentResolver mContentResolver; //判断是否直接用Content生成,是为true,否则为false private boolean mIsCreate; private long mDataId; private String mDataMimeType; private String mDataContent; private long mDataContentData1; private String mDataContentData3; private ContentValues mDiffDataValues; /* * 功能描述:构造函数,用于初始化数据 * 参数注解:mContentResolver用于获取ContentProvider提供的数据 * 参数注解: mIsCreate表征当前数据是用哪种方式创建(两种构造函数的参数不同) * 参数注解: * Made By CuiCan */ public SqlData(Context context) { mContentResolver = context.getContentResolver(); mIsCreate = true; mDataId = INVALID_ID;//mDataId置初始值-99999 mDataMimeType = DataConstants.NOTE; mDataContent = ""; mDataContentData1 = 0; mDataContentData3 = ""; mDiffDataValues = new ContentValues(); } /* * 功能描述:构造函数,初始化数据 * 参数注解:mContentResolver用于获取ContentProvider提供的数据 * 参数注解: mIsCreate表征当前数据是用哪种方式创建(两种构造函数的参数不同) * 参数注解: * Made By CuiCan */ public SqlData(Context context, Cursor c) { mContentResolver = context.getContentResolver(); mIsCreate = false; loadFromCursor(c); mDiffDataValues = new ContentValues(); } /* * 功能描述:从光标处加载数据 * 从当前的光标处将五列的数据加载到该类的对象 * Made By CuiCan */ private void loadFromCursor(Cursor c) { mDataId = c.getLong(DATA_ID_COLUMN); mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); mDataContent = c.getString(DATA_CONTENT_COLUMN); mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); } /* * 功能描述:设置用于共享的数据,并提供异常抛出与处理机制 * 参数注解: * Made By CuiCan */ public void setContent(JSONObject js) throws JSONException { //如果传入的JSONObject对象中有DataColumns.ID这一项,则设置,否则设为INVALID_ID long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; if (mIsCreate || mDataId != dataId) { mDiffDataValues.put(DataColumns.ID, dataId); } mDataId = dataId; String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) : DataConstants.NOTE; if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); } mDataMimeType = dataMimeType; String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; if (mIsCreate || !mDataContent.equals(dataContent)) { mDiffDataValues.put(DataColumns.CONTENT, dataContent); } mDataContent = dataContent; long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; if (mIsCreate || mDataContentData1 != dataContentData1) { mDiffDataValues.put(DataColumns.DATA1, dataContentData1); } mDataContentData1 = dataContentData1; String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { mDiffDataValues.put(DataColumns.DATA3, dataContentData3); } mDataContentData3 = dataContentData3; } /* * 功能描述:获取共享的数据内容,并提供异常抛出与处理机制 * 参数注解: * Made By CuiCan */ public JSONObject getContent() throws JSONException { if (mIsCreate) { Log.e(TAG, "it seems that we haven't created this in database yet"); return null; } //创建JSONObject对象。并将相关数据放入其中,并返回。 JSONObject js = new JSONObject(); js.put(DataColumns.ID, mDataId); js.put(DataColumns.MIME_TYPE, mDataMimeType); js.put(DataColumns.CONTENT, mDataContent); js.put(DataColumns.DATA1, mDataContentData1); js.put(DataColumns.DATA3, mDataContentData3); return js; } /* * 功能描述:commit函数用于把当前造作所做的修改保存到数据库 * 参数注解: * Made By CuiCan */ public void commit(long noteId, boolean validateVersion, long version) { if (mIsCreate) { if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { mDiffDataValues.remove(DataColumns.ID); } mDiffDataValues.put(DataColumns.NOTE_ID, noteId); Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); try { mDataId = Long.valueOf(uri.getPathSegments().get(1)); } catch (NumberFormatException e) { Log.e(TAG, "Get note id error :" + e.toString()); throw new ActionFailureException("create note failed"); } } else { if (mDiffDataValues.size() > 0) { int result = 0; if (!validateVersion) { result = mContentResolver.update(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); } else { result = mContentResolver.update(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { String.valueOf(noteId), String.valueOf(version) }); } if (result == 0) { Log.w(TAG, "there is no update. maybe user updates note when syncing"); } } } mDiffDataValues.clear(); mIsCreate = false; } /* * 功能描述:获取当前id * 实现过程: * 参数注解: * Made By CuiCan */ public long getId() { return mDataId; } } 3、SqlNote.java /* * Description:用于支持小米便签最底层的数据库相关操作,和sqlnote的关系上是子集关系,即data是note的子集(节点)。 * SqlData其实就是也就是所谓数据中的数据 */ package net.micode.notes.gtask.data; /* * 功能描述: * 实现过程: * 参数注解: * Made By CuiCan */ public class SqlData { /* * 功能描述:得到类的简写名称存入字符串TAG中 * 实现过程:调用getSimpleName ()函数 * Made By CuiCan */ private static final String TAG = SqlData.class.getSimpleName(); private static final int INVALID_ID = -99999;//为mDataId置初始值-99999 /** * 来自Notes类中定义的DataColumn中的一些常量 */ // 集合了interface DataColumns中所有SF常量 public static final String[] PROJECTION_DATA = new String[] { DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, DataColumns.DATA3 }; /** * 以下五个变量作为sql表中5列的编号 */ public static final int DATA_ID_COLUMN = 0; public static final int DATA_MIME_TYPE_COLUMN = 1; public static final int DATA_CONTENT_COLUMN = 2; public static final int DATA_CONTENT_DATA_1_COLUMN = 3; public static final int DATA_CONTENT_DATA_3_COLUMN = 4; private ContentResolver mContentResolver; //判断是否直接用Content生成,是为true,否则为false private boolean mIsCreate; private long mDataId; private String mDataMimeType; private String mDataContent; private long mDataContentData1; private String mDataContentData3; private ContentValues mDiffDataValues; /* * 功能描述:构造函数,用于初始化数据 * 参数注解:mContentResolver用于获取ContentProvider提供的数据 * 参数注解: mIsCreate表征当前数据是用哪种方式创建(两种构造函数的参数不同) * 参数注解: * Made By CuiCan */ public SqlData(Context context) { mContentResolver = context.getContentResolver(); mIsCreate = true; mDataId = INVALID_ID;//mDataId置初始值-99999 mDataMimeType = DataConstants.NOTE; mDataContent = ""; mDataContentData1 = 0; mDataContentData3 = ""; mDiffDataValues = new ContentValues(); } /* * 功能描述:构造函数,初始化数据 * 参数注解:mContentResolver用于获取ContentProvider提供的数据 * 参数注解: mIsCreate表征当前数据是用哪种方式创建(两种构造函数的参数不同) * 参数注解: * Made By CuiCan */ public SqlData(Context context, Cursor c) { mContentResolver = context.getContentResolver(); mIsCreate = false; loadFromCursor(c); mDiffDataValues = new ContentValues(); } /* * 功能描述:从光标处加载数据 * 从当前的光标处将五列的数据加载到该类的对象 * Made By CuiCan */ private void loadFromCursor(Cursor c) { mDataId = c.getLong(DATA_ID_COLUMN); mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); mDataContent = c.getString(DATA_CONTENT_COLUMN); mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); } /* * 功能描述:设置用于共享的数据,并提供异常抛出与处理机制 * 参数注解: * Made By CuiCan */ public void setContent(JSONObject js) throws JSONException { //如果传入的JSONObject对象中有DataColumns.ID这一项,则设置,否则设为INVALID_ID long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; if (mIsCreate || mDataId != dataId) { mDiffDataValues.put(DataColumns.ID, dataId); } mDataId = dataId; String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) : DataConstants.NOTE; if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); } mDataMimeType = dataMimeType; String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; if (mIsCreate || !mDataContent.equals(dataContent)) { mDiffDataValues.put(DataColumns.CONTENT, dataContent); } mDataContent = dataContent; long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; if (mIsCreate || mDataContentData1 != dataContentData1) { mDiffDataValues.put(DataColumns.DATA1, dataContentData1); } mDataContentData1 = dataContentData1; String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { mDiffDataValues.put(DataColumns.DATA3, dataContentData3); } mDataContentData3 = dataContentData3; } /* * 功能描述:获取共享的数据内容,并提供异常抛出与处理机制 * 参数注解: * Made By CuiCan */ public JSONObject getContent() throws JSONException { if (mIsCreate) { Log.e(TAG, "it seems that we haven't created this in database yet"); return null; } //创建JSONObject对象。并将相关数据放入其中,并返回。 JSONObject js = new JSONObject(); js.put(DataColumns.ID, mDataId); js.put(DataColumns.MIME_TYPE, mDataMimeType); js.put(DataColumns.CONTENT, mDataContent); js.put(DataColumns.DATA1, mDataContentData1); js.put(DataColumns.DATA3, mDataContentData3); return js; } /* * 功能描述:commit函数用于把当前造作所做的修改保存到数据库 * 参数注解: * Made By CuiCan */ public void commit(long noteId, boolean validateVersion, long version) { if (mIsCreate) { if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { mDiffDataValues.remove(DataColumns.ID); } mDiffDataValues.put(DataColumns.NOTE_ID, noteId); Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); try { mDataId = Long.valueOf(uri.getPathSegments().get(1)); } catch (NumberFormatException e) { Log.e(TAG, "Get note id error :" + e.toString()); throw new ActionFailureException("create note failed"); } } else { if (mDiffDataValues.size() > 0) { int result = 0; if (!validateVersion) { result = mContentResolver.update(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); } else { result = mContentResolver.update(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { String.valueOf(noteId), String.valueOf(version) }); } if (result == 0) { Log.w(TAG, "there is no update. maybe user updates note when syncing"); } } } mDiffDataValues.clear(); mIsCreate = false; } /* * 功能描述:获取当前id * 实现过程: * 参数注解: * Made By CuiCan */ public long getId() { return mDataId; } } 4、Task.java package net.micode.notes.gtask.data; public class Task extends Node { private static final String TAG = Task.class.getSimpleName(); private boolean mCompleted;//是否完成 private String mNotes; private JSONObject mMetaInfo;//将在实例中存储数据的类型 private Task mPriorSibling;//对应的优先兄弟Task的指针(待完善) private TaskList mParent;//所在的任务列表的指针 public Task() { super(); mCompleted = false; mNotes = null; mPriorSibling = null;//TaskList中当前Task前面的Task的指针 mParent = null;//当前Task所在的TaskList mMetaInfo = null; } public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); try { // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); // index js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this)); // entity_delta 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); if (getNotes() != null) { entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); } js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); // parent_id if (mParent!= null) { js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid()); } // dest_parent_type js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE, GTaskStringUtils.GTASK_JSON_TYPE_GROUP); // list_id if (mParent!= null) { js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); } // prior_sibling_id if (mPriorSibling != null) { js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); } } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("fail to generate task-create jsonobject"); } return js; } public JSONObject getUpdateAction(int actionId) { JSONObject js = new JSONObject(); try { // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); // id js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); if (getNotes() != null) { entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); } entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("fail to generate task-update jsonobject"); } return js; } public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try { // id if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); } // last_modified if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); } // name if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); } // notes if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES)); } // deleted if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); } // completed if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) { setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED)); } } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("fail to get task content from jsonobject"); } } } public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) || !js.has(GTaskStringUtils.META_HEAD_DATA)) { Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); } try { JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) { Log.e(TAG, "invalid type"); return; } for (int i = 0; i < dataArray.length(); i++) { JSONObject data = dataArray.getJSONObject(i); if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { setName(data.getString(DataColumns.CONTENT)); break; } } } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); } } public JSONObject getLocalJSONFromContent() { String name = getName(); try { if (mMetaInfo == null) { // new task created from web if (name == null) { Log.w(TAG, "the note seems to be an empty one"); return null; } JSONObject js = new JSONObject(); JSONObject note = new JSONObject(); JSONArray dataArray = new JSONArray(); JSONObject data = new JSONObject(); data.put(DataColumns.CONTENT, name); dataArray.put(data); js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); js.put(GTaskStringUtils.META_HEAD_NOTE, note); return js; } else { // synced task JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA); for (int i = 0; i < dataArray.length(); i++) { JSONObject data = dataArray.getJSONObject(i); if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { data.put(DataColumns.CONTENT, getName()); break; } } note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); return mMetaInfo; } } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); return null; } } public void setMetaInfo(MetaData metaData) { if (metaData != null && metaData.getNotes() != null) { try { mMetaInfo = new JSONObject(metaData.getNotes()); } catch (JSONException e) { Log.w(TAG, e.toString()); mMetaInfo = null; } } } public int getSyncAction(Cursor c) { try { JSONObject noteInfo = null; if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) { noteInfo = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); } if (noteInfo == null) { Log.w(TAG, "it seems that note meta has been deleted"); return SYNC_ACTION_UPDATE_REMOTE; } if (!noteInfo.has(NoteColumns.ID)) { Log.w(TAG, "remote note id seems to be deleted"); return SYNC_ACTION_UPDATE_LOCAL; } // validate the note id now if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) { Log.w(TAG, "note id doesn't match"); return SYNC_ACTION_UPDATE_LOCAL; } if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { // there is no local update if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { // no update both side return SYNC_ACTION_NONE; } else { // apply remote to local return SYNC_ACTION_UPDATE_LOCAL; } } else { // validate gtask id if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { Log.e(TAG, "gtask id doesn't match"); return SYNC_ACTION_ERROR; } if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { // local modification only return SYNC_ACTION_UPDATE_REMOTE; } else { return SYNC_ACTION_UPDATE_CONFLICT; } } } catch (Exception e) { Log.e(TAG, e.toString()); e.printStackTrace(); } return SYNC_ACTION_ERROR; } public boolean isWorthSaving() { return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) || (getNotes() != null && getNotes().trim().length() > 0); } public void setCompleted(boolean completed) { this.mCompleted = completed; } public void setNotes(String notes) { this.mNotes = notes; } public void setPriorSibling(Task priorSibling) { this.mPriorSibling = priorSibling; } public void setParent(TaskList parent) { this.mParent = parent; } public boolean getCompleted() { return this.mCompleted; } public String getNotes() { return this.mNotes; } public Task getPriorSibling() { return this.mPriorSibling; } public TaskList getParent() { return this.mParent; } } 5、TaskList.java package net.micode.notes.gtask.data; public class TaskList extends Node { private static final String TAG = TaskList.class.getSimpleName();//tag标记 private int mIndex;//当前TaskList的指针 private ArrayList<Task> mChildren;//类中主要的保存数据的单元,用来实现一个以Task为元素的ArrayList public TaskList() { super(); mChildren = new ArrayList<Task>(); mIndex = 1; } /* (non-Javadoc) * @see net.micode.notes.gtask.data.Node#getCreateAction(int) * 生成并返回一个包含了一定数据的JSONObject实体 */ public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); try { // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); // index js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); // entity_delta JSONObject entity = new JSONObject();//entity实体 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_GROUP); js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("fail to generate tasklist-create jsonobject"); } return js; } /* (non-Javadoc) * @see net.micode.notes.gtask.data.Node#getUpdateAction(int) * 生成并返回一个包含了一定数据的JSONObject实体 */ public JSONObject getUpdateAction(int actionId) { JSONObject js = new JSONObject(); try { // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); // id js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("fail to generate tasklist-update jsonobject"); } return js; } public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try { // id if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); } // last_modified if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); } // name if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); } } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("fail to get tasklist content from jsonobject"); } } } public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); } try { JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { String name = folder.getString(NoteColumns.SNIPPET); setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name); } else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER) setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT); else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER) setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE); else Log.e(TAG, "invalid system folder"); } else { Log.e(TAG, "error type"); } } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); } } public JSONObject getLocalJSONFromContent() { try { JSONObject js = new JSONObject(); JSONObject folder = new JSONObject(); String folderName = getName(); if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)) folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), folderName.length()); folder.put(NoteColumns.SNIPPET, folderName); if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); else folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); js.put(GTaskStringUtils.META_HEAD_NOTE, folder); return js; } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); return null; } } public int getSyncAction(Cursor c) { try { if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { // there is no local update if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { // no update both side return SYNC_ACTION_NONE; } else { // apply remote to local return SYNC_ACTION_UPDATE_LOCAL; } } else { // validate gtask id if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { Log.e(TAG, "gtask id doesn't match"); return SYNC_ACTION_ERROR; } if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { // local modification only return SYNC_ACTION_UPDATE_REMOTE; } else { // for folder conflicts, just apply local modification return SYNC_ACTION_UPDATE_REMOTE; } } } catch (Exception e) { Log.e(TAG, e.toString()); e.printStackTrace(); } return SYNC_ACTION_ERROR; } /** * @return * 功能:获得TaskList的大小,即mChildren的大小 */ public int getChildTaskCount() { return mChildren.size(); } /** * @param task * @return 返回值为是否成功添加任务。 * 功能:在当前任务表末尾添加新的任务。 */ public boolean addChildTask(Task task) { boolean ret = false; if (task != null && !mChildren.contains(task)) { ret = mChildren.add(task); if (ret) { // need to set prior sibling and parent task.setPriorSibling(mChildren.isEmpty() ? null : mChildren .get(mChildren.size() - 1)); task.setParent(this); //注意:每一次ArrayList的变化都要紧跟相关Task中PriorSibling的更改 //,接下来几个函数都有相关操作 } } return ret; } /** * @param task * @param index * @return * 功能:在当前任务表的指定位置添加新的任务。 */ public boolean addChildTask(Task task, int index) { if (index < 0 || index > mChildren.size()) { Log.e(TAG, "add child task: invalid index"); return false; } int pos = mChildren.indexOf(task); if (task != null && pos == -1) { mChildren.add(index, task); // update the task list Task preTask = null; Task afterTask = null; if (index != 0) preTask = mChildren.get(index - 1); if (index != mChildren.size() - 1) afterTask = mChildren.get(index + 1); task.setPriorSibling(preTask); if (afterTask != null) afterTask.setPriorSibling(task); } return true; } /** * @param task * @return 返回删除是否成功 * 功能:删除TaskList中的一个Task */ public boolean removeChildTask(Task task) { boolean ret = false; int index = mChildren.indexOf(task); if (index != -1) { ret = mChildren.remove(task); if (ret) { // reset prior sibling and parent task.setPriorSibling(null); task.setParent(null); // update the task list if (index != mChildren.size()) { mChildren.get(index).setPriorSibling( index == 0 ? null : mChildren.get(index - 1)); } } } return ret; } /** * @param task * @param index * @return * 功能:将当前TaskList中含有的某个Task移到index位置 */ public boolean moveChildTask(Task task, int index) { if (index < 0 || index >= mChildren.size()) { Log.e(TAG, "move child task: invalid index"); return false; } int pos = mChildren.indexOf(task); if (pos == -1) { Log.e(TAG, "move child task: the task should in the list"); return false; } if (pos == index) return true; return (removeChildTask(task) && addChildTask(task, index)); //利用已实现好的功能完成当下功能; } /** * @param gid * @return返回寻找结果 * 功能:按gid寻找Task */ public Task findChildTaskByGid(String gid) { for (int i = 0; i < mChildren.size(); i++) { Task t = mChildren.get(i); if (t.getGid().equals(gid)) { return t; } } return null; } /** * @param task * @return * 功能:返回指定Task的index */ public int getChildTaskIndex(Task task) { return mChildren.indexOf(task); } /** * @param index * @return * 功能:返回指定index的Task */ public Task getChildTaskByIndex(int index) { if (index < 0 || index >= mChildren.size()) { Log.e(TAG, "getTaskByIndex: invalid index"); return null; } return mChildren.get(index); } /** * @param gid * @return * 功能:返回指定gid的Task */ public Task getChilTaskByGid(String gid) { for (Task task : mChildren) {//一种常见的ArrayList的遍历方法(四种,见精读笔记) if (task.getGid().equals(gid)) return task; } return null; } public ArrayList<Task> getChildTaskList() { return this.mChildren; } public void setIndex(int index) { this.mIndex = index; } public int getIndex() { return this.mIndex; } } 二、exception包 1、ActionFailureException.java /* * Description:支持小米便签运行过程中的运行异常处理。 */ package net.micode.notes.gtask.exception; public class ActionFailureException extends RuntimeException { private static final long serialVersionUID = 4425249765923293627L; /* * serialVersionUID相当于java类的身份证。主要用于版本控制。 * serialVersionUID作用是序列化时保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。 * Made By Cuican */ public ActionFailureException() { super(); } /* * 在JAVA类中使用super来引用父类的成分,用this来引用当前对象. * 如果一个类从另外一个类继承,我们new这个子类的实例对象的时候,这个子类对象里面会有一个父类对象。 * 怎么去引用里面的父类对象呢?使用super来引用 * 也就是说,此处super()以及super (paramString)可认为是Exception ()和Exception (paramString) * Made By Cuican */ public ActionFailureException(String paramString) { super(paramString); } public ActionFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); } } 2、NetworkFailureException.java /* * Description:支持小米便签运行过程中的网络异常处理。 */ package net.micode.notes.gtask.exception; public class NetworkFailureException extends Exception { private static final long serialVersionUID = 2107610287180234136L; /* * serialVersionUID相当于java类的身份证。主要用于版本控制。 * serialVersionUID作用是序列化时保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。 * Made By Cuican */ public NetworkFailureException() { super(); } /* * 在JAVA类中使用super来引用父类的成分,用this来引用当前对象. * 如果一个类从另外一个类继承,我们new这个子类的实例对象的时候,这个子类对象里面会有一个父类对象。 * 怎么去引用里面的父类对象呢?使用super来引用 * 也就是说,此处super()以及super (paramString)可认为是Exception ()和Exception (paramString) * Made By Cuican */ public NetworkFailureException(String paramString) { super(paramString); } public NetworkFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); } } 三、remote包 1、GTaskASyncTask.java package net.micode.notes.gtask.remote; /*异步操作类,实现GTask的异步操作过程 * 主要方法: * 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<Void, String, Integer> { private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; public interface OnCompleteListener { void onComplete(); } private Context mContext; private NotificationManager mNotifiManager; private GTaskManager mTaskManager; private OnCompleteListener mOnCompleteListener; public GTaskASyncTask(Context context, OnCompleteListener listener) { mContext = context; mOnCompleteListener = listener; mNotifiManager = (NotificationManager) mContext .getSystemService(Context.NOTIFICATION_SERVICE); mTaskManager = GTaskManager.getInstance(); } public void cancelSync() { mTaskManager.cancelSync(); } public void publishProgess(String message) { // 发布进度单位,系统将会调用onProgressUpdate()方法更新这些值 publishProgress(new String[] { message }); } 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 pendingIntent; //一个描述了想要启动一个Activity、Broadcast或是Service的意图 if (tickerId != R.string.ticker_success) { pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesPreferenceActivity.class), 0); //如果同步不成功,那么从系统取得一个用于启动一个NotesPreferenceActivity的PendingIntent对象 } else { pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesListActivity.class), 0); //如果同步成功,那么从系统取得一个用于启动一个NotesListActivity的PendingIntent对象 } notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, pendingIntent); mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification);//通过NotificationManager对象的notify()方法来执行一个notification的消息 } @Override protected Integer doInBackground(Void... unused) { publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity .getSyncAccountName(mContext))); //利用getString,将把 NotesPreferenceActivity.getSyncAccountName(mContext))的字符串内容传进sync_progress_login中 return mTaskManager.sync(mContext, this); //进行后台同步具体操作 } @Override protected void onProgressUpdate(String... progress) { showNotification(R.string.ticker_syncing, progress[0]); if (mContext instanceof GTaskSyncService) { //instanceof 判断mContext是否是GTaskSyncService的实例 ((GTaskSyncService) mContext).sendBroadcast(progress[0]); } } @Override protected void onPostExecute(Integer result) { //用于在执行完后台任务后更新UI,显示结果 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) { showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network)); } 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) { showNotification(R.string.ticker_cancel, mContext .getString(R.string.error_sync_cancelled)); } //几种不同情况下的结果显示 if (mOnCompleteListener != null) { new Thread(new Runnable() { //这里好像是方法内的一个线程,但是并不太懂什么意思 public void run() { //完成后的操作,使用onComplete()将所有值都重新初始化,相当于完成一次操作 mOnCompleteListener.onComplete(); } }).start(); } } } 3、GTaskClient.java package net.micode.notes.gtask.remote; /* * 主要功能:实现GTASK的登录操作,进行GTASK任务的创建,创建任务列表,从网络上获取任务和任务列表的内容 * 主要使用类或技术:accountManager JSONObject HttpParams authToken Gid */ public class GTaskClient { private static final String TAG = GTaskClient.class.getSimpleName(); private static final String GTASK_URL = "https://mail.google.com/tasks/"; //这个是指定的URL private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; private static GTaskClient mInstance = null; private DefaultHttpClient mHttpClient; private String mGetUrl; private String mPostUrl; private long mClientVersion; private boolean mLoggedin; private long mLastLoginTime; private int mActionId; private Account mAccount; private JSONArray mUpdateArray; private GTaskClient() { mHttpClient = null; mGetUrl = GTASK_GET_URL; mPostUrl = GTASK_POST_URL; mClientVersion = -1; mLoggedin = false; mLastLoginTime = 0; mActionId = 1; mAccount = null; mUpdateArray = null; } /*用来获取的实例化对象 * 使用 getInstance() * 返回mInstance这个实例化对象 */ public static synchronized GTaskClient getInstance() { if (mInstance == null) { mInstance = new GTaskClient(); } return mInstance; } /*用来实现登录操作的函数,传入的参数是一个Activity * 设置登录操作限制时间,如果超时则需要重新登录 * 有两种登录方式,使用用户自己的URL登录或者使用谷歌官方的URL登录 * 返回true或者false,即最后是否登陆成功 */ public boolean login(Activity activity) { //判断距离最后一次登录操作是否超过5分钟 final long interval = 1000 * 60 * 5; if (mLastLoginTime + interval < System.currentTimeMillis()) { mLoggedin = false; } // need to re-login after account switch 重新登录操作 if (mLoggedin && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity .getSyncAccountName(activity))) { mLoggedin = false; } //如果没超过时间,则不需要重新登录 if (mLoggedin) { Log.d(TAG, "already logged in"); return true; } mLastLoginTime = System.currentTimeMillis(); //更新最后登录时间,改为系统当前的时间 String authToken = loginGoogleAccount(activity, false); //判断是否登录到谷歌账户 if (authToken == null) { Log.e(TAG, "login google account failed"); return false; } // login with custom domain if necessary //尝试使用用户自己的域名登录 if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() //将用户账号名改为统一格式(小写)后判断是否为一个谷歌账号地址 .endsWith("googlemail.com"))) { StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); int index = mAccount.name.indexOf('@') + 1; String suffix = mAccount.name.substring(index); url.append(suffix + "/"); mGetUrl = url.toString() + "ig"; //设置用户对应的getUrl mPostUrl = url.toString() + "r/ig"; //设置用户对应的postUrl if (tryToLoginGtask(activity, authToken)) { mLoggedin = true; } } // try to login with google official url //如果用户账户无法登录,则使用谷歌官方的URI进行登录 if (!mLoggedin) { mGetUrl = GTASK_GET_URL; mPostUrl = GTASK_POST_URL; if (!tryToLoginGtask(activity, authToken)) { return false; } } mLoggedin = true; return true; } /*具体实现登录谷歌账户的方法 * 使用令牌机制 * 使用AccountManager来管理注册账号 * 返回值是账号的令牌 */ private String loginGoogleAccount(Activity activity, boolean invalidateToken) { String authToken; //令牌,是登录操作保证安全性的一个方法 AccountManager accountManager = AccountManager.get(activity) //AccountManager这个类给用户提供了集中注册账号的接口 Account[] accounts = accountManager.getAccountsByType("com.google"); //获取全部以com.google结尾的account if (accounts.length == 0) { Log.e(TAG, "there is no available google account"); return null; } String accountName = NotesPreferenceActivity.getSyncAccountName(activity); Account account = null; //遍历获得的accounts信息,寻找已经记录过的账户信息 for (Account a : accounts) { if (a.name.equals(accountName)) { account = a; break; } } if (account != null) { mAccount = account; } else { Log.e(TAG, "unable to get an account with the same name in the settings"); return null; } // get the token now //获取选中账号的令牌 AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account, "goanna_mobile", null, activity, null, null); try { Bundle authTokenBundle = accountManagerFuture.getResult(); authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); //如果是invalidateToken,那么需要调用invalidateAuthToken(String, String)方法废除这个无效token if (invalidateToken) { accountManager.invalidateAuthToken("com.google", authToken); loginGoogleAccount(activity, false); } } catch (Exception e) { Log.e(TAG, "get auth token failed"); authToken = null; } return authToken; } //尝试登陆Gtask,这只是一个预先判断令牌是否是有效以及是否能登上GTask的方法,而不是具体实现登陆的方法 private boolean tryToLoginGtask(Activity activity, String authToken) { if (!loginGtask(authToken)) { // maybe the auth token is out of authTokedate, now let's invalidate the // token and try again //删除过一个无效的authToken,申请一个新的后再次尝试登陆 authToken = loginGoogleAccount(activity, true); if (authToken == null) { Log.e(TAG, "login google account failed"); return false; } if (!loginGtask(authToken)) { Log.e(TAG, "login gtask failed"); return false; } } return true; } //实现登录GTask的具体操作 private boolean loginGtask(String authToken) { int timeoutConnection = 10000; int timeoutSocket = 15000; //socket是一种通信连接实现数据的交换的端口 HttpParams httpParameters = new BasicHttpParams(); //实例化一个新的HTTP参数类 HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); //设置连接超时时间 HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); //设置设置端口超时时间 mHttpClient = new DefaultHttpClient(httpParameters); BasicCookieStore localBasicCookieStore = new BasicCookieStore(); //设置本地cookie mHttpClient.setCookieStore(localBasicCookieStore); HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); // login gtask try { String loginUrl = mGetUrl + "?auth=" + authToken; //设置登录的url HttpGet httpGet = new HttpGet(loginUrl); //通过登录的uri实例化网页上资源的查找 HttpResponse response = null; response = mHttpClient.execute(httpGet); // get the cookie now //获取CookieStore里存放的cookie,看如果存有“GTL(不知道什么意思)”,则说明有验证成功的有效的cookie List<Cookie> cookies = mHttpClient.getCookieStore().getCookies(); boolean hasAuthCookie = false; for (Cookie cookie : cookies) { if (cookie.getName().contains("GTL")) { hasAuthCookie = true; } } if (!hasAuthCookie) { Log.w(TAG, "it seems that there is no auth cookie"); } //获取client的内容,具体操作是在返回的Content中截取从_setup(开始到)}</script>中间的字符串内容,也就是gtask_url的内容 String resString = getResponseContent(response.getEntity()); String jsBegin = "_setup("; String jsEnd = ")}</script>"; int begin = resString.indexOf(jsBegin); int end = resString.lastIndexOf(jsEnd); String jsString = null; if (begin != -1 && end != -1 && begin < end) { jsString = resString.substring(begin + jsBegin.length(), end); } JSONObject js = new JSONObject(jsString); mClientVersion = js.getLong("v"); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); return false; } catch (Exception e) { // simply catch all exceptions Log.e(TAG, "httpget gtask_url failed"); return false; } return true; } 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"); httpPost.setHeader("AT", "1"); return httpPost; } /*通过URL获取响应后返回的数据,也就是网络上的数据和资源 * 使用getContentEncoding()获取网络上的资源和数据 * 返回值就是获取到的资源 */ private String getResponseContent(HttpEntity entity) throws IOException { String contentEncoding = null; if (entity.getContentEncoding() != null) { //通过URL得到HttpEntity对象,如果不为空则使用getContent()方法创建一个流将数据从网络都过来 contentEncoding = entity.getContentEncoding().getValue(); Log.d(TAG, "encoding: " + contentEncoding); } InputStream input = entity.getContent(); if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { //GZIP是使用DEFLATE进行压缩数据的另一个压缩库 input = new GZIPInputStream(entity.getContent()); } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) { //DEFLATE是一个无专利的压缩算法,它可以实现无损数据压缩 Inflater inflater = new Inflater(true); input = new InflaterInputStream(entity.getContent(), inflater); } try { InputStreamReader isr = new InputStreamReader(input); BufferedReader br = new BufferedReader(isr); //是一个包装类,它可以包装字符流,将字符流放入缓存里,先把字符读到缓存里,到缓存满了时候,再读入内存,是为了提供读的效率而设计的 StringBuilder sb = new StringBuilder(); while (true) { String buff = br.readLine(); if (buff == null) { return sb.toString(); } sb = sb.append(buff); } } finally { input.close(); } } /*通过JSON发送请求 * 请求的具体内容在json的实例化对象js中然后传入 * 利用UrlEncodedFormEntity entity和httpPost.setEntity(entity)方法把js中的内容放置到httpPost中 * 执行请求后使用getResponseContent方法得到返回的数据和资源 * 将资源再次放入json后返回 */ private JSONObject postRequest(JSONObject js) throws NetworkFailureException { if (!mLoggedin) {//未登录 Log.e(TAG, "please login first"); throw new ActionFailureException("not logged in"); } //实例化一个httpPost的对象用来向服务器传输数据,在这里就是发送请求,而请求的内容在js里 HttpPost httpPost = createHttpPost(); try { LinkedList<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>(); list.add(new BasicNameValuePair("r", js.toString())); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); //UrlEncodedFormEntity()的形式比较单一,是普通的键值对 httpPost.setEntity(entity); //执行这个请求 HttpResponse response = mHttpClient.execute(httpPost); String jsString = getResponseContent(response.getEntity()); return new JSONObject(jsString); } catch (ClientProtocolException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new NetworkFailureException("postRequest failed"); } catch (IOException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new NetworkFailureException("postRequest failed"); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("unable to convert response content to jsonobject"); } catch (Exception e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("error occurs when posting request"); } } /*创建单个任务 * 传入参数是一个.gtask.data.Task包里Task类的对象 * 利用json获取Task里的内容,并且创建相应的jsPost * 利用postRequest得到任务的返回信息 * 使用task.setGid设置task的new_ID */ public void createTask(Task task) throws NetworkFailureException { commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); // action_list actionList.put(task.getCreateAction(getActionId())); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // client_version jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // post JSONObject jsResponse = postRequest(jsPost); JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( GTaskStringUtils.GTASK_JSON_RESULTS).get(0); task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("create task: handing jsonobject failed"); } } /* * 创建一个任务列表,与createTask几乎一样,区别就是最后设置的是tasklist的gid */ public void createTaskList(TaskList tasklist) throws NetworkFailureException { commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); // action_list actionList.put(tasklist.getCreateAction(getActionId())); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // client version jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // post JSONObject jsResponse = postRequest(jsPost); JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( GTaskStringUtils.GTASK_JSON_RESULTS).get(0); tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("create tasklist: handing jsonobject failed"); } } /* * 同步更新操作 * 使用JSONObject进行数据存储,使用jsPost.put,Put的信息包括UpdateArray和ClientVersion * 使用postRequest发送这个jspost,进行处理 */ public void commitUpdate() throws NetworkFailureException { if (mUpdateArray != null) { try { JSONObject jsPost = new JSONObject(); // action_list jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); // client_version jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); postRequest(jsPost); mUpdateArray = null; } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("commit update: handing jsonobject failed"); } } } /* * 添加更新的事项 * 调用commitUpdate()实现 */ public void addUpdateNode(Node node) throws NetworkFailureException { if (node != null) { // too many update items may result in an error // set max to 10 items if (mUpdateArray != null && mUpdateArray.length() > 10) { commitUpdate(); } if (mUpdateArray == null) mUpdateArray = new JSONArray(); mUpdateArray.put(node.getUpdateAction(getActionId())); } } /* * 移动task,比如讲task移动到不同的task列表中去 * 通过getGid获取task所属列表的gid * 通过JSONObject.put(String name, Object value)函数设置移动后的task的相关属性值,从而达到移动的目的 * 最后还是通过postRequest进行更新后的发送 */ public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException { commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); JSONObject action = new JSONObject(); // action_list action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE); action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); if (preParent == curParent && task.getPriorSibling() != null) { // put prioring_sibing_id only if moving within the tasklist and //设置优先级ID,只有当移动是发生在文件中 action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling()); } action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); //设置移动前所属列表 action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); //设置当前所属列表 if (preParent != curParent) { // put the dest_list only if moving between tasklists action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); } actionList.put(action); //最后将ACTION_LIST加入到jsPost中 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // client_version jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); postRequest(jsPost); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("move task: handing jsonobject failed"); } } /* * 删除操作节点 * 还是利用JSON * 删除过后使用postRequest发送删除后的结果 */ public void deleteNode(Node node) throws NetworkFailureException { commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); // action_list node.setDeleted(true); actionList.put(node.getUpdateAction(getActionId())); //这里会获取到删除操作的ID,加入到actionLiast中 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // client_version jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); postRequest(jsPost); mUpdateArray = null; } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("delete node: handing jsonobject failed"); } } /* * 获取任务列表 * 首先通过GetURI使用getResponseContent从网上获取数据 * 然后筛选出"_setup("到)}</script>的部分,并且从中获取GTASK_JSON_LISTS的内容返回 */ public JSONArray getTaskLists() throws NetworkFailureException { if (!mLoggedin) { Log.e(TAG, "please login first"); throw new ActionFailureException("not logged in"); } try { HttpGet httpGet = new HttpGet(mGetUrl); HttpResponse response = null; response = mHttpClient.execute(httpGet); // get the task list //筛选工作,把筛选出的字符串放入jsString String resString = getResponseContent(response.getEntity()); String jsBegin = "_setup("; String jsEnd = ")}</script>"; int begin = resString.indexOf(jsBegin); int end = resString.lastIndexOf(jsEnd); String jsString = null; if (begin != -1 && end != -1 && begin < end) { jsString = resString.substring(begin + jsBegin.length(), end); } JSONObject js = new JSONObject(jsString); //获取GTASK_JSON_LISTS return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); } catch (ClientProtocolException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new NetworkFailureException("gettasklists: httpget failed"); } catch (IOException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new NetworkFailureException("gettasklists: httpget failed"); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("get task lists: handing jasonobject failed"); } } /* * 通过传入的TASKList的gid,从网络上获取相应属于这个任务列表的任务 */ public JSONArray getTaskList(String listGid) throws NetworkFailureException { commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); JSONObject action = new JSONObject(); // action_list action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); //这里设置为传入的listGid action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); actionList.put(action); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // client_version jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); JSONObject jsResponse = postRequest(jsPost); return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("get task list: handing jsonobject failed"); } } public Account getSyncAccount() { return mAccount; } //重置更新的内容 public void resetUpdateArray() { mUpdateArray = null; } } 4、GTaskManager.java package net.micode.notes.gtask.remote; public class GTaskManager { private static final String TAG = GTaskManager.class.getSimpleName(); 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; private Activity mActivity; private Context mContext; private ContentResolver mContentResolver; private boolean mSyncing; private boolean mCancelled; private HashMap<String, TaskList> mGTaskListHashMap; private HashMap<String, Node> mGTaskHashMap; private HashMap<String, MetaData> mMetaHashMap; private TaskList mMetaList; private HashSet<Long> mLocalDeleteIdMap; private HashMap<String, Long> mGidToNid; private HashMap<Long, String> mNidToGid; private GTaskManager() { //对象初始化函数 mSyncing = false; //正在同步,flase代表未执行 mCancelled = false; //全局标识,flase代表可以执行 mGTaskListHashMap = new HashMap<String, TaskList>(); //<>代表Java的泛型,就是创建一个用类型作为参数的类。 mGTaskHashMap = new HashMap<String, Node>(); mMetaHashMap = new HashMap<String, MetaData>(); mMetaList = null; mLocalDeleteIdMap = new HashSet<Long>(); mGidToNid = new HashMap<String, Long>(); //GoogleID to NodeID?? mNidToGid = new HashMap<Long, String>(); //NodeID to GoogleID???通过hashmap散列表建立映射 } /** * 包含关键字synchronized,语言级同步,指明该函数可能运行在多线程的环境下。 * 功能:类初始化函数 * @author TTS * @return GtaskManger */ public static synchronized GTaskManager getInstance() { //可能运行在多线程环境下,使用语言级同步--synchronized if (mInstance == null) { mInstance = new GTaskManager(); } return mInstance; } /** * 包含关键字synchronized,语言级同步,指明该函数可能运行在多线程的环境下。 * @author TTS * @param activity */ public synchronized void setActivityContext(Activity activity) { // used for getting auth token mActivity = activity; } /** * 核心函数 * 功能:实现了本地同步操作和远端同步操作 * @author TTS * @param context-----获取上下文 * @param asyncTask-------用于同步的异步操作类 * @return int */ public int sync(Context context, GTaskASyncTask asyncTask) { //核心函数 if (mSyncing) { Log.d(TAG, "Sync is in progress"); //创建日志文件(调试信息),debug return STATE_SYNC_IN_PROGRESS; } mContext = context; mContentResolver = mContext.getContentResolver(); mSyncing = true; mCancelled = false; mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); mLocalDeleteIdMap.clear(); mGidToNid.clear(); mNidToGid.clear(); try { GTaskClient client = GTaskClient.getInstance(); //getInstance即为创建一个实例,client--客户机 client.resetUpdateArray(); //JSONArray类型,reset即置为NULL // login google task if (!mCancelled) { if (!client.login(mActivity)) { throw new NetworkFailureException("login google task failed"); } } // get the task list from google asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); initGTaskList(); //获取Google上的JSONtasklist转为本地TaskList // do content sync work asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); syncContent(); } catch (NetworkFailureException e) { //分为两种异常,此类异常为网络异常 Log.e(TAG, e.toString()); //创建日志文件(调试信息),error return STATE_NETWORK_ERROR; } catch (ActionFailureException e) { //此类异常为操作异常 Log.e(TAG, e.toString()); return STATE_INTERNAL_ERROR; } catch (Exception e) { Log.e(TAG, e.toString()); e.printStackTrace(); return STATE_INTERNAL_ERROR; } finally { mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); mLocalDeleteIdMap.clear(); mGidToNid.clear(); mNidToGid.clear(); mSyncing = false; } return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; } /** *功能:初始化GtaskList,获取Google上的JSONtasklist转为本地TaskList。 *获得的数据存储在mMetaList,mGTaskListHashMap,mGTaskHashMap *@author TTS *@exception NetworkFailureException *@return void */ private void initGTaskList() throws NetworkFailureException { if (mCancelled) return; GTaskClient client = GTaskClient.getInstance(); //getInstance即为创建一个实例,client应指远端客户机 try { //Json对象是Name Value对(即子元素)的无序集合,相当于一个Map对象。JsonObject类是bantouyan-json库对Json对象的抽象,提供操纵Json对象的各种方法。 //其格式为{"key1":value1,"key2",value2....};key 必须是字符串。 //因为ajax请求不刷新页面,但配合js可以实现局部刷新,因此json常常被用来作为异步请求的返回对象使用。 JSONArray jsTaskLists = client.getTaskLists(); //原注释为get task list------lists // init meta list first mMetaList = null; //TaskList类型 for (int i = 0; i < jsTaskLists.length(); i++) { JSONObject object = jsTaskLists.getJSONObject(i); //JSONObject与JSONArray一个为对象,一个为数组。此处取出单个JASONObject String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); if (name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { mMetaList = new TaskList(); //MetaList意为元表,Tasklist类型,此处为初始化 mMetaList.setContentByRemoteJSON(object); //将JSON中部分数据复制到自己定义的对象中相对应的数据:name->mname... // load meta data JSONArray jsMetas = client.getTaskList(gid); //原注释为get action_list------list for (int j = 0; j < jsMetas.length(); j++) { object = (JSONObject) jsMetas.getJSONObject(j); MetaData metaData = new MetaData(); //继承自Node metaData.setContentByRemoteJSON(object); if (metaData.isWorthSaving()) { //if not worth to save,metadata将不加入mMetaList mMetaList.addChildTask(metaData); if (metaData.getGid() != null) { mMetaHashMap.put(metaData.getRelatedGid(), metaData); } } } } } // create meta list if not existed if (mMetaList == null) { mMetaList = new TaskList(); mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META); GTaskClient.getInstance().createTaskList(mMetaList); } // init task list for (int i = 0; i < jsTaskLists.length(); i++) { JSONObject object = jsTaskLists.getJSONObject(i); String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); //通过getString函数传入本地某个标志数据的名称,获取其在远端的名称。 String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { TaskList tasklist = new TaskList(); //继承自Node tasklist.setContentByRemoteJSON(object); mGTaskListHashMap.put(gid, tasklist); mGTaskHashMap.put(gid, tasklist); //加两遍 // load tasks JSONArray jsTasks = client.getTaskList(gid); for (int j = 0; j < jsTasks.length(); j++) { object = (JSONObject) jsTasks.getJSONObject(j); gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); Task task = new Task(); task.setContentByRemoteJSON(object); if (task.isWorthSaving()) { task.setMetaInfo(mMetaHashMap.get(gid)); tasklist.addChildTask(task); mGTaskHashMap.put(gid, task); } } } } } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("initGTaskList: handing JSONObject failed"); } } /** * 功能:本地内容同步操作 * @throws NetworkFailureException * @return 无返回值 */ private void syncContent() throws NetworkFailureException { //本地内容同步操作 int syncType; Cursor c = null; //数据库指针 String gid; //GoogleID Node node; //Node包含Sync_Action的不同类型 mLocalDeleteIdMap.clear(); //HashSet<Long>类型 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 (c.moveToNext()) { gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); if (node != null) { mGTaskHashMap.remove(gid); doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); } mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); } } else { Log.w(TAG, "failed to query trash folder"); } } finally { if (c != null) { c.close(); c = null; } } // sync folder first syncFolder(); // for note existing in database try { 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) }, NoteColumns.TYPE + " DESC"); if (c != null) { while (c.moveToNext()) { gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); if (node != null) { mGTaskHashMap.remove(gid); mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); //通过hashmap建立联系 mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); //通过hashmap建立联系 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 { if (c != null) { c.close(); c = null; } } // go through remaining items Iterator<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator(); //Iterator迭代器 while (iter.hasNext()) { Map.Entry<String, Node> 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 //thread----线程 // one // clear local delete table if (!mCancelled) { if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { throw new ActionFailureException("failed to batch-delete local deleted notes"); } } // refresh local sync id if (!mCancelled) { GTaskClient.getInstance().commitUpdate(); refreshLocalSyncId(); } } /** * 功能: * @author TTS * @throws NetworkFailureException */ private void syncFolder() throws NetworkFailureException { Cursor c = null; String gid; Node node; int syncType; if (mCancelled) { return; } // 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 = c.getString(SqlNote.GTASK_ID_COLUMN); 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 { doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); } } else { Log.w(TAG, "failed to query root folder"); } } finally { if (c != null) { c.close(); c = null; } } // for call-note folder try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", new String[] { String.valueOf(Notes.ID_CALL_RECORD_FOLDER) }, null); if (c != null) { if (c.moveToNext()) { gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); if (node != null) { mGTaskHashMap.remove(gid); mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); 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 { doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); } } } else { Log.w(TAG, "failed to query call note folder"); } } finally { if (c != null) { c.close(); c = null; } } // 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); if (node != null) { mGTaskHashMap.remove(gid); mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); 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 folder"); } } finally { if (c != null) { c.close(); c = null; } } // for remote add folders Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String, TaskList> entry = iter.next(); gid = entry.getKey(); node = entry.getValue(); if (mGTaskHashMap.containsKey(gid)) { mGTaskHashMap.remove(gid); doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); } } if (!mCancelled) GTaskClient.getInstance().commitUpdate(); } /** * 功能:syncType分类,addLocalNode,addRemoteNode,deleteNode,updateLocalNode,updateRemoteNode * @author TTS * @param syncType * @param node * @param c * @throws NetworkFailureException */ private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } MetaData meta; 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) { GTaskClient.getInstance().deleteNode(meta); } mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); break; case Node.SYNC_ACTION_DEL_REMOTE: meta = mMetaHashMap.get(node.getGid()); if (meta != null) { GTaskClient.getInstance().deleteNode(meta); } 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"); } } /** * 功能:本地增加Node * @author TTS * @param node * @throws NetworkFailureException */ private void addLocalNode(Node node) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote; if (node instanceof TaskList) { if (node.getName().equals( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); } else if (node.getName().equals( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); } else { sqlNote = new SqlNote(mContext); sqlNote.setContent(node.getLocalJSONFromContent()); sqlNote.setParentId(Notes.ID_ROOT_FOLDER); } } else { sqlNote = new SqlNote(mContext); JSONObject js = node.getLocalJSONFromContent(); try { if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); if (note.has(NoteColumns.ID)) { long id = note.getLong(NoteColumns.ID); if (DataUtils.existInNoteDatabase(mContentResolver, id)) { // the id is not available, have to create a new one note.remove(NoteColumns.ID); } } } if (js.has(GTaskStringUtils.META_HEAD_DATA)) { JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); for (int i = 0; i < dataArray.length(); i++) { JSONObject data = dataArray.getJSONObject(i); if (data.has(DataColumns.ID)) { long dataId = data.getLong(DataColumns.ID); if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { // the data id is not available, have to create // a new one data.remove(DataColumns.ID); } } } } } catch (JSONException e) { Log.w(TAG, e.toString()); e.printStackTrace(); } sqlNote.setContent(js); Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); if (parentId == null) { Log.e(TAG, "cannot find task's parent id locally"); throw new ActionFailureException("cannot add local node"); } sqlNote.setParentId(parentId.longValue()); } // create the local node sqlNote.setGtaskId(node.getGid()); sqlNote.commit(false); // update gid-nid mapping mGidToNid.put(node.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), node.getGid()); // update meta updateRemoteMeta(node.getGid(), sqlNote); } /** * 功能:update本地node * @author TTS * @param node * ----同步操作的基础数据类型 * @param c * ----Cursor * @throws NetworkFailureException */ private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote; // update the note locally sqlNote = new SqlNote(mContext, c); sqlNote.setContent(node.getLocalJSONFromContent()); Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) : new Long(Notes.ID_ROOT_FOLDER); if (parentId == null) { Log.e(TAG, "cannot find task's parent id locally"); throw new ActionFailureException("cannot update local node"); } sqlNote.setParentId(parentId.longValue()); sqlNote.commit(true); // update meta info updateRemoteMeta(node.getGid(), sqlNote); } /** * 功能:远程增加Node * 需要updateRemoteMeta * @author TTS * @param node * ----同步操作的基础数据类型 * @param c * --Cursor * @throws NetworkFailureException */ private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote = new SqlNote(mContext, c); //从本地mContext中获取内容 Node n; // update remotely if (sqlNote.isNoteType()) { Task task = new Task(); task.setContentByLocalJSON(sqlNote.getContent()); String parentGid = mNidToGid.get(sqlNote.getParentId()); if (parentGid == null) { Log.e(TAG, "cannot find task's parent tasklist"); //调试信息 throw new ActionFailureException("cannot add remote task"); } mGTaskListHashMap.get(parentGid).addChildTask(task); //在本地生成的GTaskList中增加子结点 //登录远程服务器,创建Task GTaskClient.getInstance().createTask(task); n = (Node) task; // add meta updateRemoteMeta(task.getGid(), sqlNote); } else { TaskList tasklist = null; // we need to skip folder if it has already existed String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) folderName += GTaskStringUtils.FOLDER_DEFAULT; else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER) folderName += GTaskStringUtils.FOLDER_CALL_NOTE; else folderName += sqlNote.getSnippet(); //iterator迭代器,通过统一的接口迭代所有的map元素 Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String, TaskList> entry = iter.next(); String gid = entry.getKey(); TaskList list = entry.getValue(); if (list.getName().equals(folderName)) { tasklist = list; if (mGTaskHashMap.containsKey(gid)) { mGTaskHashMap.remove(gid); } break; } } // no match we can add now if (tasklist == null) { tasklist = new TaskList(); tasklist.setContentByLocalJSON(sqlNote.getContent()); GTaskClient.getInstance().createTaskList(tasklist); mGTaskListHashMap.put(tasklist.getGid(), tasklist); } n = (Node) tasklist; } // update local note sqlNote.setGtaskId(n.getGid()); sqlNote.commit(false); sqlNote.resetLocalModified(); sqlNote.commit(true); // gid-id mapping //创建id间的映射 mGidToNid.put(n.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), n.getGid()); } /** * 功能:更新远端的Node,包含meta更新(updateRemoteMeta) * @author TTS * @param node * ----同步操作的基础数据类型 * @param c * --Cursor * @throws NetworkFailureException */ private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote = new SqlNote(mContext, c); // update remotely node.setContentByLocalJSON(sqlNote.getContent()); GTaskClient.getInstance().addUpdateNode(node); //GTaskClient用途为从本地登陆远端服务器 // update meta updateRemoteMeta(node.getGid(), sqlNote); // move task if necessary if (sqlNote.isNoteType()) { Task task = (Task) node; TaskList preParentList = task.getParent(); //preParentList为通过node获取的父节点列表 String curParentGid = mNidToGid.get(sqlNote.getParentId()); //curParentGid为通过光标在数据库中找到sqlNote的mParentId,再通过mNidToGid由long类型转为String类型的Gid if (curParentGid == null) { Log.e(TAG, "cannot find task's parent tasklist"); throw new ActionFailureException("cannot update remote task"); } TaskList curParentList = mGTaskListHashMap.get(curParentGid); //通过HashMap找到对应Gid的TaskList if (preParentList != curParentList) { preParentList.removeChildTask(task); curParentList.addChildTask(task); GTaskClient.getInstance().moveTask(task, preParentList, curParentList); } } // clear local modified flag sqlNote.resetLocalModified(); //commit到本地数据库 sqlNote.commit(true); } /** * 功能:升级远程meta。 meta---元数据----计算机文件系统管理数据---管理数据的数据。 * @author TTS * @param gid * ---GoogleID为String类型 * @param sqlNote * ---同步前的数据库操作,故使用类SqlNote * @throws NetworkFailureException */ private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { if (sqlNote != null && sqlNote.isNoteType()) { MetaData metaData = mMetaHashMap.get(gid); if (metaData != null) { metaData.setMeta(gid, sqlNote.getContent()); GTaskClient.getInstance().addUpdateNode(metaData); } else { metaData = new MetaData(); metaData.setMeta(gid, sqlNote.getContent()); mMetaList.addChildTask(metaData); mMetaHashMap.put(gid, metaData); GTaskClient.getInstance().createTask(metaData); } } } /** * 功能:刷新本地,给sync的ID对应上最后更改过的对象 * @author TTS * @return void * @throws NetworkFailureException */ private void refreshLocalSyncId() throws NetworkFailureException { if (mCancelled) { return; } // get the latest gtask list //获取最近的(最晚的) gtask list mGTaskHashMap.clear(); mGTaskListHashMap.clear(); mMetaHashMap.clear(); initGTaskList(); Cursor c = null; 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) }, NoteColumns.TYPE + " DESC"); //query语句:五个参数,NoteColumns.TYPE + " DESC"-----为按类型递减顺序返回查询结果。new String[] {String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)}------为选择参数。"(type<>? AND parent_id<>?)"-------指明返回行过滤器。SqlNote.PROJECTION_NOTE--------应返回的数据列的名字。Notes.CONTENT_NOTE_URI--------contentProvider包含所有数据集所对应的uri if (c != null) { while (c.moveToNext()) { String gid = c.getString(SqlNote.GTASK_ID_COLUMN); Node node = mGTaskHashMap.get(gid); if (node != null) { mGTaskHashMap.remove(gid); ContentValues values = new ContentValues(); //在ContentValues中创建键值对。准备通过contentResolver写入数据 values.put(NoteColumns.SYNC_ID, node.getLastModified()); mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, //进行批量更改,选择参数为NULL,应该可以用insert替换,参数分别为表名和需要更新的value对象。 c.getLong(SqlNote.ID_COLUMN)), values, null, null); } else { Log.e(TAG, "something is missed"); throw new ActionFailureException( "some local items don't have gid after sync"); } } } else { Log.w(TAG, "failed to query local note to refresh sync id"); } } finally { if (c != null) { c.close(); c = null; } } } /** * 功能:获取同步账号,mAccount.name * @author TTS * @return String */ public String getSyncAccount() { return GTaskClient.getInstance().getSyncAccount().name; } /** * 功能:取消同步,置mCancelled为true * @author TTS */ public void cancelSync() { mCancelled = true; } } 5、GTaskSyncService.java package net.micode.notes.gtask.remote; /* * Service是在一段不定的时间运行在后台,不和用户交互的应用组件 * 主要方法: * 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 { public final static String ACTION_STRING_NAME = "sync_action_type"; public final static int ACTION_START_SYNC = 0; public final static int ACTION_CANCEL_SYNC = 1; public final static int ACTION_INVALID = 2; public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; private static GTaskASyncTask mSyncTask = null; private static String mSyncProgress = ""; //开始一个同步的工作 private void startSync() { if (mSyncTask == null) { mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { public void onComplete() { mSyncTask = null; sendBroadcast(""); stopSelf(); } }); sendBroadcast(""); mSyncTask.execute(); //这个函数让任务是以单线程队列方式或线程池队列方式运行 } } private void cancelSync() { if (mSyncTask != null) { mSyncTask.cancelSync(); } } @Override public void onCreate() { //初始化一个service mSyncTask = null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { Bundle bundle = intent.getExtras(); if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { //两种情况,开始同步或者取消同步 case ACTION_START_SYNC: startSync(); break; case ACTION_CANCEL_SYNC: cancelSync(); break; default: break; } return START_STICKY; //等待新的intent来是这个service继续运行 } return super.onStartCommand(intent, flags, startId); } @Override public void onLowMemory() { if (mSyncTask != null) { mSyncTask.cancelSync(); } } public IBinder onBind(Intent intent) { //不知道干吗用的 return null; } public void sendBroadcast(String msg) { mSyncProgress = msg; Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); //创建一个新的Intent intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); //附加INTENT中的相应参数的值 intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); sendBroadcast(intent); //发送这个通知 } public static void startSync(Activity activity) {//执行一个service,service的内容里的同步动作就是开始同步 GTaskManager.getInstance().setActivityContext(activity); Intent intent = new Intent(activity, GTaskSyncService.class); intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); activity.startService(intent); } public static void cancelSync(Context context) {//执行一个service,service的内容里的同步动作就是取消同步 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; } }