Use SparseArray instead of HashMap in vCard code
TESTED:
- prepare three vCard files: large.vcf, middle.vcf, and small.vcf
- import them orderly
- cancel middle.vcf during importing large.vcf
- after finishing everything, check
-- the service finishes
-- only large.vcf and small.vcf are imported
Bug: 6013599
Change-Id: I00c3fc1ee804001ccc937ea13a5755daca938f89
diff --git a/src/com/android/contacts/vcard/VCardService.java b/src/com/android/contacts/vcard/VCardService.java
index 71b0214..34c4acb 100644
--- a/src/com/android/contacts/vcard/VCardService.java
+++ b/src/com/android/contacts/vcard/VCardService.java
@@ -15,8 +15,6 @@
*/
package com.android.contacts.vcard;
-import com.android.contacts.R;
-
import android.app.Service;
import android.content.Intent;
import android.content.res.Resources;
@@ -24,17 +22,18 @@
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.contacts.R;
import java.io.File;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -108,8 +107,7 @@
// Stores all unfinished import/export jobs which will be executed by mExecutorService.
// Key is jobId.
- private final Map<Integer, ProcessorBase> mRunningJobMap =
- new HashMap<Integer, ProcessorBase>();
+ private final SparseArray<ProcessorBase> mRunningJobMap = new SparseArray<ProcessorBase>();
// Stores ScannerConnectionClient objects until they finish scanning requested files.
// Uses List class for simplicity. It's not costly as we won't have multiple objects in
// almost all cases.
@@ -272,7 +270,9 @@
VCardImportExportListener listener) {
final int jobId = request.jobId;
if (DEBUG) Log.d(LOG_TAG, String.format("Received cancel request. (id: %d)", jobId));
- final ProcessorBase processor = mRunningJobMap.remove(jobId);
+
+ final ProcessorBase processor = mRunningJobMap.get(jobId);
+ mRunningJobMap.remove(jobId);
if (processor != null) {
processor.cancel(true);
@@ -321,16 +321,37 @@
*/
private synchronized void stopServiceIfAppropriate() {
if (mRunningJobMap.size() > 0) {
- for (final Map.Entry<Integer, ProcessorBase> entry : mRunningJobMap.entrySet()) {
- final int jobId = entry.getKey();
- final ProcessorBase processor = entry.getValue();
- if (processor.isDone()) {
- mRunningJobMap.remove(jobId);
- } else {
+ final int size = mRunningJobMap.size();
+
+ // Check if there are processors which aren't finished yet. If we still have ones to
+ // process, we cannot stop the service yet. Also clean up already finished processors
+ // here.
+
+ // Job-ids to be removed. At first all elements in the array are invalid and will
+ // be filled with real job-ids from the array's top. When we find a not-yet-finished
+ // processor, then we start removing those finished jobs. In that case latter half of
+ // this array will be invalid.
+ final int[] toBeRemoved = new int[size];
+ for (int i = 0; i < size; i++) {
+ final int jobId = mRunningJobMap.keyAt(i);
+ final ProcessorBase processor = mRunningJobMap.valueAt(i);
+ if (!processor.isDone()) {
Log.i(LOG_TAG, String.format("Found unfinished job (id: %d)", jobId));
+
+ // Remove processors which are already "done", all of which should be before
+ // processors which aren't done yet.
+ for (int j = 0; j < i; j++) {
+ mRunningJobMap.remove(toBeRemoved[j]);
+ }
return;
}
+
+ // Remember the finished processor.
+ toBeRemoved[i] = jobId;
}
+
+ // We're sure we can remove all. Instead of removing one by one, just call clear().
+ mRunningJobMap.clear();
}
if (!mRemainingScannerConnections.isEmpty()) {
@@ -374,9 +395,7 @@
Log.d(LOG_TAG, String.format("Received vCard import finish notification (id: %d). "
+ "Result: %b", jobId, (successful ? "success" : "failure")));
}
- if (mRunningJobMap.remove(jobId) == null) {
- Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
- }
+ mRunningJobMap.remove(jobId);
stopServiceIfAppropriate();
}
@@ -386,7 +405,8 @@
Log.d(LOG_TAG, String.format("Received vCard export finish notification (id: %d). "
+ "Result: %b", jobId, (successful ? "success" : "failure")));
}
- final ProcessorBase job = mRunningJobMap.remove(jobId);
+ final ProcessorBase job = mRunningJobMap.get(jobId);
+ mRunningJobMap.remove(jobId);
if (job == null) {
Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
} else if (!(job instanceof ExportProcessor)) {
@@ -408,8 +428,8 @@
* Mainly called from onDestroy().
*/
private synchronized void cancelAllRequestsAndShutdown() {
- for (final Map.Entry<Integer, ProcessorBase> entry : mRunningJobMap.entrySet()) {
- entry.getValue().cancel(true);
+ for (int i = 0; i < mRunningJobMap.size(); i++) {
+ mRunningJobMap.valueAt(i).cancel(true);
}
mRunningJobMap.clear();
mExecutorService.shutdown();