From f5986782d8c2c2aa24dd2113f598838a166c5c4e Mon Sep 17 00:00:00 2001 From: XF1001 <1214358029@qq.com> Date: Fri, 4 Dec 2020 11:38:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../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 ++++- 6 files changed, 485 insertions(+), 41 deletions(-) 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; }