diff --git a/app/src/main/java/com/tonyodev/fetchdemo/App.java b/app/src/main/java/com/tonyodev/fetchdemo/App.java index d5a80ad1..09bd174e 100644 --- a/app/src/main/java/com/tonyodev/fetchdemo/App.java +++ b/app/src/main/java/com/tonyodev/fetchdemo/App.java @@ -22,6 +22,7 @@ public void onCreate() { new Fetch.Settings(this) .setAllowedNetwork(Fetch.NETWORK_ALL) .enableLogging(true) + .setConcurrentDownloadsLimit(1) .apply(); fetch = Fetch.getInstance(this); diff --git a/app/src/main/java/com/tonyodev/fetchdemo/Data.java b/app/src/main/java/com/tonyodev/fetchdemo/Data.java index 600ade9a..4a120bc6 100644 --- a/app/src/main/java/com/tonyodev/fetchdemo/Data.java +++ b/app/src/main/java/com/tonyodev/fetchdemo/Data.java @@ -19,7 +19,7 @@ public final class Data { "http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_640x360.m4v", "http://media.mongodb.org/zips.json", "http://www.example/some/unknown/123/Errorlink.txt", - "http://www.drodd.com/gump/boxachoc.mp3", + "http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_640x360.m4v", "http://storage.googleapis.com/ix_choosemuse/uploads/2016/02/android-logo.png"}; private Data() { @@ -38,7 +38,7 @@ static List getFetchRequests() { return requests; } - private static String getFilePath(String url) { + public static String getFilePath(String url) { Uri uri = Uri.parse(url); diff --git a/fetch/src/main/java/com/tonyodev/fetch/DatabaseHelper.java b/fetch/src/main/java/com/tonyodev/fetch/DatabaseHelper.java index 0a108584..33d5fd1d 100644 --- a/fetch/src/main/java/com/tonyodev/fetch/DatabaseHelper.java +++ b/fetch/src/main/java/com/tonyodev/fetch/DatabaseHelper.java @@ -567,6 +567,25 @@ synchronized Cursor getNextPendingRequest() { + FetchConst.STATUS_QUEUED + " LIMIT 1" ,null); } + synchronized boolean hasPendingRequests() { + + Cursor cursor = db.rawQuery("SELECT " + COLUMN_ID + " FROM " + + TABLE_NAME + " WHERE " + COLUMN_STATUS + " = " + + FetchConst.STATUS_QUEUED + " LIMIT 1" ,null); + + boolean hasPending = false; + + if(cursor != null && cursor.getCount() > 0) { + hasPending = true; + } + + if(cursor != null) { + cursor.close(); + } + + return hasPending; + } + synchronized void verifyOK() { try { diff --git a/fetch/src/main/java/com/tonyodev/fetch/Fetch.java b/fetch/src/main/java/com/tonyodev/fetch/Fetch.java index 7552ca8e..456c7a59 100644 --- a/fetch/src/main/java/com/tonyodev/fetch/Fetch.java +++ b/fetch/src/main/java/com/tonyodev/fetch/Fetch.java @@ -976,6 +976,23 @@ public void enableLogging(boolean enabled) { new Settings(context).enableLogging(enabled).apply(); } + /** + * Sets the allowed concurrent downloads value between 1-7. The default is 1 + * and the max is 7. The Fetch Service will only allow up to the MAX concurrent downloads. + * + *

See Fetch.DEFAULT_DOWNLOADS_LIMIT and Fetch.MAX_DOWNLOADS_LIMIT + * + * @param limit concurrent downloads limit + * + * @throws NotUsableException if the release method has been called on Fetch. + * + * */ + public void setConcurrentDownloadsLimit(int limit) { + + Utils.throwIfNotUsable(this); + new Settings(context).setConcurrentDownloadsLimit(limit).apply(); + } + /** * The Settings class is used to apply * settings to Fetch and the FetchService. @@ -1039,8 +1056,8 @@ public Settings setAllowedNetwork(int networkType) { } /** - * Sets the allowed concurrent downloads by the Fetch Service between 1-7. The default is 1 - * and the max is 7. The Fetch Service will only allow up to 7 concurrent downloads. + * Sets the allowed concurrent downloads value between 1-7. The default is 1 + * and the max is 7. The Fetch Service will only allow up to the MAX concurrent downloads. * *

See Fetch.DEFAULT_DOWNLOADS_LIMIT and Fetch.MAX_DOWNLOADS_LIMIT * diff --git a/fetch/src/main/java/com/tonyodev/fetch/FetchRunnable.java b/fetch/src/main/java/com/tonyodev/fetch/FetchRunnable.java index c9cd3416..502b6e58 100644 --- a/fetch/src/main/java/com/tonyodev/fetch/FetchRunnable.java +++ b/fetch/src/main/java/com/tonyodev/fetch/FetchRunnable.java @@ -134,8 +134,6 @@ public void run() { progress = Utils.getProgress(downloadedBytes,fileSize); } - databaseHelper.updateStatus(id,FetchConst.STATUS_DOWNLOADING,FetchConst.DEFAULT_EMPTY_VALUE); - output = new RandomAccessFile(filePath,"rw"); if(responseCode == HttpURLConnection.HTTP_PARTIAL) { output.seek(downloadedBytes); @@ -148,7 +146,10 @@ public void run() { databaseHelper.updateFileBytes(id,downloadedBytes,fileSize); - if(downloadedBytes >= fileSize && !isInterrupted()) { + if (isInterrupted()) { + throw new DownloadInterruptedException("DIE",ErrorUtils.DOWNLOAD_INTERRUPTED); + + }else if(downloadedBytes >= fileSize && !isInterrupted()) { if(fileSize < 1) { fileSize = Utils.getFileSize(filePath); diff --git a/fetch/src/main/java/com/tonyodev/fetch/FetchService.java b/fetch/src/main/java/com/tonyodev/fetch/FetchService.java index 919fd675..0ba71b3f 100644 --- a/fetch/src/main/java/com/tonyodev/fetch/FetchService.java +++ b/fetch/src/main/java/com/tonyodev/fetch/FetchService.java @@ -101,8 +101,6 @@ public final class FetchService extends Service implements FetchConst { private LocalBroadcastManager broadcastManager; private SharedPreferences sharedPreferences; - private volatile FetchRunnable fetchRunnable; - private volatile boolean fetchRunnableQueued = false; private volatile boolean runningTask = false; private volatile boolean shuttingDown = false; private int downloadsLimit = DEFAULT_DOWNLOADS_LIMIT; @@ -110,7 +108,7 @@ public final class FetchService extends Service implements FetchConst { private int preferredNetwork = NETWORK_ALL; private final ExecutorService executor = Executors.newSingleThreadExecutor(); private final List registeredReceivers = new ArrayList<>(); - private final ConcurrentHashMap fetchRunnableMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap activeDownloads = new ConcurrentHashMap<>(); public static void sendToService(@NonNull Context context,@Nullable Bundle extras) { @@ -178,6 +176,7 @@ public void onCreate() { @Override public void run() { databaseHelper.clean(); + databaseHelper.verifyOK(); } }); } @@ -211,9 +210,7 @@ public void onDestroy() { executor.shutdown(); } - if(fetchRunnable != null) { - fetchRunnable.interrupt(); - } + interruptActiveDownloads(); for (BroadcastReceiver registeredReceiver : registeredReceivers) { broadcastManager.unregisterReceiver(registeredReceiver); @@ -310,22 +307,23 @@ public void run() { private synchronized void startDownload() { - if(shuttingDown || fetchRunnableQueued || runningTask) { + if(shuttingDown || runningTask) { return; } - databaseHelper.verifyOK(); boolean networkAvailable = Utils.isNetworkAvailable(context); boolean onWiFi = Utils.isOnWiFi(context); - if((!networkAvailable || (preferredNetwork == NETWORK_WIFI && !onWiFi)) - && fetchRunnable != null) { + if((!networkAvailable || (preferredNetwork == NETWORK_WIFI && !onWiFi)) && activeDownloads.size() > 0) { - fetchRunnable.interrupt(); + runningTask = true; + interruptActiveDownloads(); + runningTask = false; - }else if(networkAvailable && !fetchRunnableQueued) { + }else if(networkAvailable && !runningTask && activeDownloads.size() < downloadsLimit + && databaseHelper.hasPendingRequests()) { - fetchRunnableQueued = true; + runningTask = true; try { @@ -335,14 +333,14 @@ private synchronized void startDownload() { RequestInfo requestInfo = Utils.cursorToRequestInfo(cursor,true,loggingEnabled); - fetchRunnable = new FetchRunnable(context,requestInfo.getId(), + FetchRunnable fetchRunnable = new FetchRunnable(context,requestInfo.getId(), requestInfo.getUrl(), requestInfo.getFilePath() ,requestInfo.getHeaders(),requestInfo.getFileSize(),loggingEnabled); - new Thread(fetchRunnable).start(); + databaseHelper.updateStatus(requestInfo.getId(),FetchService.STATUS_DOWNLOADING,DEFAULT_EMPTY_VALUE); + activeDownloads.put(fetchRunnable.getId(),fetchRunnable); - }else { - stopSelf(); + new Thread(fetchRunnable).start(); } }catch (Exception e) { @@ -350,13 +348,39 @@ private synchronized void startDownload() { if(loggingEnabled) { e.printStackTrace(); } + } - fetchRunnableQueued = false; + runningTask = false; + + if(activeDownloads.size() < downloadsLimit && databaseHelper.hasPendingRequests()) { startDownload(); } } } + private void interruptActiveDownloads() { + + for (Long id : activeDownloads.keySet()) { + + FetchRunnable fetchRunnable = activeDownloads.get(id); + + if(fetchRunnable != null) { + fetchRunnable.interrupt(); + } + } + } + + private void interruptActiveDownload(long id) { + + if(activeDownloads.containsKey(id)) { + FetchRunnable fetchRunnable = activeDownloads.get(id); + + if(fetchRunnable != null) { + fetchRunnable.interrupt(); + } + } + } + private void enqueue(String url,String filePath,ArrayList headers,int priority) { try { @@ -406,7 +430,7 @@ private void enqueue(String url,String filePath,ArrayList headers,int pr private void resume(final long id) { - if(fetchRunnable != null && fetchRunnable.getId() == id) { + if(activeDownloads.containsKey(id)) { return; } @@ -431,7 +455,7 @@ private void resume(final long id) { private void pause(final long id) { - if(fetchRunnable != null && fetchRunnable.getId() == id) { + if(activeDownloads.containsKey(id)) { runningTask = true; @@ -441,18 +465,18 @@ public void onReceive(Context context, Intent intent) { if(FetchRunnable.getIdFromIntent(intent) == id) { pauseAction(id); - } - broadcastManager.unregisterReceiver(this); - registeredReceivers.remove(this); - runningTask = false; - startDownload(); + broadcastManager.unregisterReceiver(this); + registeredReceivers.remove(this); + runningTask = false; + startDownload(); + } } }; registeredReceivers.add(broadcastReceiver); broadcastManager.registerReceiver(broadcastReceiver,FetchRunnable.getDoneFilter()); - fetchRunnable.interrupt(); + interruptActiveDownload(id); }else { pauseAction(id); @@ -480,7 +504,7 @@ private void pauseAction(long id) { private void remove(final long id) { - if (fetchRunnable != null && fetchRunnable.getId() == id) { + if (activeDownloads.containsKey(id)) { runningTask = true; @@ -490,18 +514,18 @@ public void onReceive(Context context, Intent intent) { if(FetchRunnable.getIdFromIntent(intent) == id) { removeAction(id); - } - broadcastManager.unregisterReceiver(this); - registeredReceivers.remove(this); - runningTask = false; - startDownload(); + broadcastManager.unregisterReceiver(this); + registeredReceivers.remove(this); + runningTask = false; + startDownload(); + } } }; registeredReceivers.add(broadcastReceiver); broadcastManager.registerReceiver(broadcastReceiver,FetchRunnable.getDoneFilter()); - fetchRunnable.interrupt(); + interruptActiveDownload(id); }else { removeAction(id); startDownload(); @@ -524,7 +548,7 @@ private void removeAction(long id) { private void removeAll() { - if (fetchRunnable != null) { + if (activeDownloads.size() > 0) { runningTask = true; @@ -532,18 +556,24 @@ private void removeAll() { @Override public void onReceive(Context context, Intent intent) { - removeAllAction(); + if(intent != null) { + long id = FetchRunnable.getIdFromIntent(intent); + removeAction(id); + } - broadcastManager.unregisterReceiver(this); - registeredReceivers.remove(this); - runningTask = false; - startDownload(); + if(activeDownloads.size() == 0) { + removeAllAction(); + broadcastManager.unregisterReceiver(this); + registeredReceivers.remove(this); + runningTask = false; + startDownload(); + } } }; registeredReceivers.add(broadcastReceiver); broadcastManager.registerReceiver(broadcastReceiver,FetchRunnable.getDoneFilter()); - fetchRunnable.interrupt(); + interruptActiveDownloads(); }else { removeAllAction(); startDownload(); @@ -593,8 +623,8 @@ private void query(int queryType,long queryId,long requestId,int status) { private void setRequestPriority(long id, int priority) { - if(databaseHelper.setPriority(id,priority) && fetchRunnable != null) { - fetchRunnable.interrupt(); + if(databaseHelper.setPriority(id,priority) && activeDownloads.size() > 0) { + interruptActiveDownloads(); } startDownload(); @@ -602,11 +632,11 @@ private void setRequestPriority(long id, int priority) { private void setAllowedNetwork(final int networkType) { - sharedPreferences.edit().putInt(EXTRA_NETWORK_ID,networkType).apply(); preferredNetwork = networkType; + sharedPreferences.edit().putInt(EXTRA_NETWORK_ID,networkType).apply(); - if(fetchRunnable != null) { - fetchRunnable.interrupt(); + if(activeDownloads.size() > 0) { + interruptActiveDownloads(); } startDownload(); @@ -614,7 +644,7 @@ private void setAllowedNetwork(final int networkType) { private void retry(long id) { - if(fetchRunnable != null && fetchRunnable.getId() == id) { + if(activeDownloads.containsKey(id)) { return; } @@ -642,9 +672,16 @@ private int getAllowedNetwork() { @Override public void onReceive(Context context, Intent intent) { - fetchRunnable = null; - fetchRunnableQueued = false; - startDownload(); + if(intent != null) { + + long id = FetchRunnable.getIdFromIntent(intent); + + if(activeDownloads.containsKey(id)) { + activeDownloads.remove(id); + } + + startDownload(); + } } }; @@ -685,14 +722,20 @@ private void setDownloadsLimit(int limit) { limit = DEFAULT_DOWNLOADS_LIMIT; } - sharedPreferences.edit().putInt(EXTRA_CONCURRENT_DOWNLOADS_LIMIT,limit).apply(); downloadsLimit = limit; + sharedPreferences.edit().putInt(EXTRA_CONCURRENT_DOWNLOADS_LIMIT,limit).apply(); + + if(activeDownloads.size() > 0) { + interruptActiveDownloads(); + } + startDownload(); } private void setLoggingEnabled(boolean enabled) { - sharedPreferences.edit().putBoolean(EXTRA_LOGGING_ID,enabled).apply(); + loggingEnabled = enabled; + sharedPreferences.edit().putBoolean(EXTRA_LOGGING_ID,enabled).apply(); databaseHelper.setLoggingEnabled(loggingEnabled); startDownload(); }