Downloads concurrency crash fix

This commit is contained in:
Alexey Kulish 2015-10-14 19:51:59 +03:00
parent c472c05249
commit 72cccbde57
8 changed files with 118 additions and 99 deletions

View file

@ -27,6 +27,7 @@ import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -119,23 +120,40 @@ public class BaseDownloadActivity extends ActionBarProgressActivity {
} }
public ItemsListBuilder getItemsBuilder() { public ItemsListBuilder getItemsBuilder() {
return getItemsBuilder(""); return getItemsBuilder("", false);
} }
public ItemsListBuilder getItemsBuilder(String regionId) { public ItemsListBuilder getVoicePromptsBuilder() {
if (downloadListIndexThread.isDataPrepared()) { return getItemsBuilder("", true);
return new ItemsListBuilder(getMyApplication(), regionId, downloadListIndexThread.getResourcesByRegions(), }
downloadListIndexThread.getVoiceRecItems(), downloadListIndexThread.getVoiceTTSItems());
public ItemsListBuilder getItemsBuilder(String regionId, boolean voicePromptsOnly) {
if (downloadListIndexThread.getResourcesLock().tryLock()) {
try {
ItemsListBuilder builder = new ItemsListBuilder(getMyApplication(), regionId, downloadListIndexThread.getResourcesByRegions(),
downloadListIndexThread.getVoiceRecItems(), downloadListIndexThread.getVoiceTTSItems());
if (!voicePromptsOnly) {
return builder.build();
} else {
return builder;
}
} finally {
downloadListIndexThread.getResourcesLock().unlock();
}
} else { } else {
return null; return null;
} }
} }
public Map<String, IndexItem> getIndexItemsByRegion(WorldRegion region) { public List<IndexItem> getIndexItemsByRegion(WorldRegion region) {
if (downloadListIndexThread.isDataPrepared()) { if (downloadListIndexThread.getResourcesLock().tryLock()) {
return downloadListIndexThread.getResourcesByRegions().get(region); try {
return new LinkedList<>(downloadListIndexThread.getResourcesByRegions().get(region).values());
} finally {
downloadListIndexThread.getResourcesLock().unlock();
}
} else { } else {
return null; return new LinkedList<>();
} }
} }

View file

@ -52,6 +52,7 @@ import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
@SuppressLint("NewApi") @SuppressLint("NewApi")
public class DownloadIndexesThread { public class DownloadIndexesThread {
@ -73,7 +74,7 @@ public class DownloadIndexesThread {
private List<IndexItem> voiceRecItems = new LinkedList<>(); private List<IndexItem> voiceRecItems = new LinkedList<>();
private List<IndexItem> voiceTTSItems = new LinkedList<>(); private List<IndexItem> voiceTTSItems = new LinkedList<>();
private boolean dataPrepared; private final ReentrantLock resourcesLock = new ReentrantLock();
DatabaseHelper dbHelper; DatabaseHelper dbHelper;
@ -85,6 +86,10 @@ public class DownloadIndexesThread {
dbHelper = new DatabaseHelper(app); dbHelper = new DatabaseHelper(app);
} }
public ReentrantLock getResourcesLock() {
return resourcesLock;
}
public DatabaseHelper getDbHelper() { public DatabaseHelper getDbHelper() {
return dbHelper; return dbHelper;
} }
@ -152,10 +157,6 @@ public class DownloadIndexesThread {
return itemsToUpdate; return itemsToUpdate;
} }
public boolean isDataPrepared() {
return dataPrepared;
}
public Map<WorldRegion, Map<String, IndexItem>> getResourcesByRegions() { public Map<WorldRegion, Map<String, IndexItem>> getResourcesByRegions() {
return resourcesByRegions; return resourcesByRegions;
} }
@ -169,45 +170,52 @@ public class DownloadIndexesThread {
} }
private boolean prepareData(List<IndexItem> resources) { private boolean prepareData(List<IndexItem> resources) {
List<IndexItem> resourcesInRepository; resourcesLock.lock();
if (resources != null) { try {
resourcesInRepository = resources;
} else {
resourcesInRepository = DownloadActivity.downloadListIndexThread.getCachedIndexFiles();
}
if (resourcesInRepository == null) {
return false;
}
resourcesByRegions.clear(); List<IndexItem> resourcesInRepository;
voiceRecItems.clear(); if (resources != null) {
voiceTTSItems.clear(); resourcesInRepository = resources;
} else {
for (WorldRegion region : app.getWorldRegion().getFlattenedSubregions()) { resourcesInRepository = DownloadActivity.downloadListIndexThread.getCachedIndexFiles();
processRegion(resourcesInRepository, false, region);
}
processRegion(resourcesInRepository, true, app.getWorldRegion());
final Collator collator = OsmAndCollator.primaryCollator();
final OsmandRegions osmandRegions = app.getRegions();
Collections.sort(voiceRecItems, new Comparator<IndexItem>() {
@Override
public int compare(IndexItem lhs, IndexItem rhs) {
return collator.compare(lhs.getVisibleName(app.getApplicationContext(), osmandRegions),
rhs.getVisibleName(app.getApplicationContext(), osmandRegions));
} }
}); if (resourcesInRepository == null) {
return false;
Collections.sort(voiceTTSItems, new Comparator<IndexItem>() {
@Override
public int compare(IndexItem lhs, IndexItem rhs) {
return collator.compare(lhs.getVisibleName(app.getApplicationContext(), osmandRegions),
rhs.getVisibleName(app.getApplicationContext(), osmandRegions));
} }
});
return true; resourcesByRegions.clear();
voiceRecItems.clear();
voiceTTSItems.clear();
for (WorldRegion region : app.getWorldRegion().getFlattenedSubregions()) {
processRegion(resourcesInRepository, false, region);
}
processRegion(resourcesInRepository, true, app.getWorldRegion());
final Collator collator = OsmAndCollator.primaryCollator();
final OsmandRegions osmandRegions = app.getRegions();
Collections.sort(voiceRecItems, new Comparator<IndexItem>() {
@Override
public int compare(IndexItem lhs, IndexItem rhs) {
return collator.compare(lhs.getVisibleName(app.getApplicationContext(), osmandRegions),
rhs.getVisibleName(app.getApplicationContext(), osmandRegions));
}
});
Collections.sort(voiceTTSItems, new Comparator<IndexItem>() {
@Override
public int compare(IndexItem lhs, IndexItem rhs) {
return collator.compare(lhs.getVisibleName(app.getApplicationContext(), osmandRegions),
rhs.getVisibleName(app.getApplicationContext(), osmandRegions));
}
});
return true;
} finally {
resourcesLock.unlock();
}
} }
private void processRegion(List<IndexItem> resourcesInRepository, boolean processVoiceFiles, WorldRegion region) { private void processRegion(List<IndexItem> resourcesInRepository, boolean processVoiceFiles, WorldRegion region) {
@ -531,7 +539,6 @@ public class DownloadIndexesThread {
currentRunningTask.add(this); currentRunningTask.add(this);
super.onPreExecute(); super.onPreExecute();
this.message = ctx.getString(R.string.downloading_list_indexes); this.message = ctx.getString(R.string.downloading_list_indexes);
dataPrepared = false;
} }
@Override @Override
@ -548,7 +555,6 @@ public class DownloadIndexesThread {
protected void onPostExecute(IndexFileList result) { protected void onPostExecute(IndexFileList result) {
indexFiles = result; indexFiles = result;
if (indexFiles != null && uiActivity != null) { if (indexFiles != null && uiActivity != null) {
dataPrepared = resourcesByRegions.size() > 0;
boolean basemapExists = uiActivity.getMyApplication().getResourceManager().containsBasemap(); boolean basemapExists = uiActivity.getMyApplication().getResourceManager().containsBasemap();
IndexItem basemap = indexFiles.getBasemap(); IndexItem basemap = indexFiles.getBasemap();
if (basemap != null) { if (basemap != null) {

View file

@ -1,8 +1,6 @@
package net.osmand.plus.download.items; package net.osmand.plus.download.items;
import android.content.Context; import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.map.OsmandRegions; import net.osmand.map.OsmandRegions;
@ -15,7 +13,6 @@ import net.osmand.plus.download.IndexItem;
import net.osmand.plus.srtmplugin.SRTMPlugin; import net.osmand.plus.srtmplugin.SRTMPlugin;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import java.io.Serializable;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.LinkedList; import java.util.LinkedList;
@ -24,7 +21,7 @@ import java.util.Map;
public class ItemsListBuilder { public class ItemsListBuilder {
public static final String WORLD_BASEMAP_KEY = "world_basemap.obf.zip"; //public static final String WORLD_BASEMAP_KEY = "world_basemap.obf.zip";
public static final String WORLD_SEAMARKS_KEY = "world_seamarks_basemap.obf.zip"; public static final String WORLD_SEAMARKS_KEY = "world_seamarks_basemap.obf.zip";
private Map<WorldRegion, Map<String, IndexItem>> resourcesByRegions; private Map<WorldRegion, Map<String, IndexItem>> resourcesByRegions;
@ -96,7 +93,7 @@ public class ItemsListBuilder {
public enum VoicePromptsType { public enum VoicePromptsType {
NONE, NONE,
RECORDED, RECORDED,
TTS; TTS
} }
private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(ItemsListBuilder.class); private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(ItemsListBuilder.class);
@ -130,12 +127,12 @@ public class ItemsListBuilder {
return list; return list;
} }
public String getVoicePromtName(VoicePromptsType type) { public static String getVoicePromtName(Context ctx, VoicePromptsType type) {
switch (type) { switch (type) {
case RECORDED: case RECORDED:
return app.getResources().getString(R.string.index_name_voice); return ctx.getResources().getString(R.string.index_name_voice);
case TTS: case TTS:
return app.getResources().getString(R.string.index_name_tts_voice); return ctx.getResources().getString(R.string.index_name_tts_voice);
default: default:
return ""; return "";
} }
@ -167,8 +164,8 @@ public class ItemsListBuilder {
List<IndexItem> voiceRecItems, List<IndexItem> voiceTTSItems) { List<IndexItem> voiceRecItems, List<IndexItem> voiceTTSItems) {
this.app = app; this.app = app;
this.resourcesByRegions = resourcesByRegions; this.resourcesByRegions = resourcesByRegions;
this.voiceRecItems = voiceRecItems; this.voiceRecItems = new LinkedList<>(voiceRecItems);
this.voiceTTSItems = voiceTTSItems; this.voiceTTSItems = new LinkedList<>(voiceTTSItems);
regionMapItems = new LinkedList<>(); regionMapItems = new LinkedList<>();
allResourceItems = new LinkedList<>(); allResourceItems = new LinkedList<>();
@ -177,8 +174,12 @@ public class ItemsListBuilder {
region = app.getWorldRegion().getRegionById(regionId); region = app.getWorldRegion().getRegionById(regionId);
} }
public boolean build() { public ItemsListBuilder build() {
return obtainDataAndItems(); if (obtainDataAndItems()) {
return this;
} else {
return null;
}
} }
private boolean obtainDataAndItems() { private boolean obtainDataAndItems() {
@ -223,7 +224,7 @@ public class ItemsListBuilder {
} }
List<ResourceItem> regionMapArray = new LinkedList<>(); List<ResourceItem> regionMapArray = new LinkedList<>();
List<Object> allResourcesArray = new LinkedList<Object>(); List<Object> allResourcesArray = new LinkedList<>();
Context context = app.getApplicationContext(); Context context = app.getApplicationContext();
OsmandRegions osmandRegions = app.getRegions(); OsmandRegions osmandRegions = app.getRegions();

View file

@ -73,8 +73,8 @@ public class RegionItemsFragment extends OsmandExpandableListFragment {
setListView(listView); setListView(listView);
if (regionId.length() > 0) { if (regionId.length() > 0) {
ItemsListBuilder builder = getDownloadActivity().getItemsBuilder(regionId); ItemsListBuilder builder = getDownloadActivity().getItemsBuilder(regionId, false);
if (builder != null && builder.build()) { if (builder != null) {
fillRegionItemsAdapter(builder); fillRegionItemsAdapter(builder);
listAdapter.notifyDataSetChanged(); listAdapter.notifyDataSetChanged();
expandAllGroups(); expandAllGroups();

View file

@ -287,16 +287,11 @@ public class SearchItemsFragment extends Fragment {
} }
for (WorldRegion region : regions) { for (WorldRegion region : regions) {
Map<String, IndexItem> indexItems = getDownloadActivity().getIndexItemsByRegion(region);
List<IndexItem> items = new LinkedList<>();
if (region.getSubregions().size() > 0) { if (region.getSubregions().size() > 0) {
filter.add(region); filter.add(region);
} }
for (IndexItem item : indexItems.values()) {
items.add(item);
}
List<IndexItem> items = getDownloadActivity().getIndexItemsByRegion(region);
if (items.size() > 1) { if (items.size() > 1) {
if (!filter.contains(region)) { if (!filter.contains(region)) {
filter.add(region); filter.add(region);

View file

@ -71,8 +71,7 @@ public class VoiceDialogFragment extends DialogFragment {
VoiceItemsFragment.createInstance(voicePromptsType)).commit(); VoiceItemsFragment.createInstance(voicePromptsType)).commit();
} }
ItemsListBuilder builder = getDownloadActivity().getItemsBuilder(); toolbar.setTitle(ItemsListBuilder.getVoicePromtName(getActivity(), voicePromptsType));
toolbar.setTitle(builder.getVoicePromtName(voicePromptsType));
} }
((DownloadActivity)getActivity()).registerFreeVersionBanner(view); ((DownloadActivity)getActivity()).registerFreeVersionBanner(view);

View file

@ -71,7 +71,7 @@ public class VoiceItemsFragment extends OsmandExpandableListFragment {
setListView(listView); setListView(listView);
if (voicePromptsType != VoicePromptsType.NONE) { if (voicePromptsType != VoicePromptsType.NONE) {
ItemsListBuilder builder = getDownloadActivity().getItemsBuilder(); ItemsListBuilder builder = getDownloadActivity().getVoicePromptsBuilder();
if (builder != null) { if (builder != null) {
fillVoiceItemsAdapter(builder); fillVoiceItemsAdapter(builder);
listAdapter.notifyDataSetChanged(); listAdapter.notifyDataSetChanged();

View file

@ -1,10 +1,20 @@
package net.osmand.plus.download.items; package net.osmand.plus.download.items;
import java.util.ArrayList; import android.content.Context;
import java.util.LinkedHashMap; import android.content.res.Resources;
import java.util.LinkedList; import android.content.res.TypedArray;
import java.util.List; import android.graphics.drawable.Drawable;
import java.util.Map; import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ExpandableListView;
import android.widget.TextView;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
@ -23,21 +33,11 @@ import net.osmand.plus.srtmplugin.SRTMPlugin;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import android.content.Context; import java.util.ArrayList;
import android.content.res.Resources; import java.util.LinkedHashMap;
import android.content.res.TypedArray; import java.util.LinkedList;
import android.graphics.drawable.Drawable; import java.util.List;
import android.os.Bundle; import java.util.Map;
import android.support.v4.view.MenuItemCompat;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ExpandableListView;
import android.widget.TextView;
public class WorldItemsFragment extends OsmandExpandableListFragment { public class WorldItemsFragment extends OsmandExpandableListFragment {
public static final String TAG = "WorldItemsFragment"; public static final String TAG = "WorldItemsFragment";
@ -112,11 +112,11 @@ public class WorldItemsFragment extends OsmandExpandableListFragment {
int unusedSubIndex = 0; int unusedSubIndex = 0;
List<String> voicePromptsItems = new LinkedList<>(); List<String> voicePromptsItems = new LinkedList<>();
if (!builder.isVoicePromptsItemsEmpty(VoicePromptsType.RECORDED)) { if (!builder.isVoicePromptsItemsEmpty(VoicePromptsType.RECORDED)) {
voicePromptsItems.add(builder.getVoicePromtName(VoicePromptsType.RECORDED)); voicePromptsItems.add(ItemsListBuilder.getVoicePromtName(getActivity(), VoicePromptsType.RECORDED));
voicePromptsItemsRecordedSubIndex = unusedSubIndex++; voicePromptsItemsRecordedSubIndex = unusedSubIndex++;
} }
if (!builder.isVoicePromptsItemsEmpty(VoicePromptsType.TTS)) { if (!builder.isVoicePromptsItemsEmpty(VoicePromptsType.TTS)) {
voicePromptsItems.add(builder.getVoicePromtName(VoicePromptsType.TTS)); voicePromptsItems.add(ItemsListBuilder.getVoicePromtName(getActivity(), VoicePromptsType.TTS));
voicePromptsItemsTTSSubIndex = unusedSubIndex; voicePromptsItemsTTSSubIndex = unusedSubIndex;
} }
if (!voicePromptsItems.isEmpty()) { if (!voicePromptsItems.isEmpty()) {
@ -187,8 +187,8 @@ public class WorldItemsFragment extends OsmandExpandableListFragment {
} }
public void onCategorizationFinished() { public void onCategorizationFinished() {
ItemsListBuilder builder = getDownloadActivity().getItemsBuilder(); ItemsListBuilder builder = getDownloadActivity().getItemsBuilder();
if (builder != null && builder.build()) { if (builder != null) {
fillWorldItemsAdapter(builder); fillWorldItemsAdapter(builder);
listAdapter.notifyDataSetChanged(); listAdapter.notifyDataSetChanged();
expandAllGroups(); expandAllGroups();