diff --git a/README.md b/README.md index 2f983fc..f080651 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ repositories { maven { url 'https://dl.bintray.com/alexeydanilov/maven' } } dependencies { - compile 'com.danikula:videocache:2.0.7' + compile 'com.danikula:videocache:2.0.9' } ``` diff --git a/library/build.gradle b/library/build.gradle index aa90848..ba7f651 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -26,7 +26,7 @@ publish { userOrg = 'alexeydanilov' groupId = 'com.danikula' artifactId = 'videocache' - publishVersion = '2.0.7' + publishVersion = '2.0.9' description = 'Cache support for android VideoView' website = 'https://github.com/danikula/AndroidVideoCache' } diff --git a/library/src/main/java/com/danikula/videocache/HttpProxyCacheServer.java b/library/src/main/java/com/danikula/videocache/HttpProxyCacheServer.java index a9b6d94..6ddd95e 100644 --- a/library/src/main/java/com/danikula/videocache/HttpProxyCacheServer.java +++ b/library/src/main/java/com/danikula/videocache/HttpProxyCacheServer.java @@ -150,6 +150,7 @@ private void processSocket(Socket socket) { onError(new ProxyCacheException("Error processing request", e)); } finally { releaseSocket(socket); + Log.d(LOG_TAG, "Opened connections: " + getClientsCount()); } } @@ -164,6 +165,16 @@ private HttpProxyCacheServerClients getClients(String url) throws ProxyCacheExce } } + private int getClientsCount() { + synchronized (clientsLock) { + int count = 0; + for (HttpProxyCacheServerClients clients : clientsMap.values()) { + count += clients.getClientsCount(); + } + return count; + } + } + private void releaseSocket(Socket socket) { closeSocketInput(socket); closeSocketOutput(socket); diff --git a/library/src/main/java/com/danikula/videocache/HttpProxyCacheServerClients.java b/library/src/main/java/com/danikula/videocache/HttpProxyCacheServerClients.java index 333f0fe..f46147d 100644 --- a/library/src/main/java/com/danikula/videocache/HttpProxyCacheServerClients.java +++ b/library/src/main/java/com/danikula/videocache/HttpProxyCacheServerClients.java @@ -34,16 +34,23 @@ public HttpProxyCacheServerClients(String url, FileNameGenerator fileNameGenerat } public void processRequest(GetRequest request, Socket socket) throws ProxyCacheException, IOException { - proxyCache = proxyCache == null ? newHttpProxyCache() : proxyCache; + startProcessRequest(); try { clientsCount.incrementAndGet(); proxyCache.processRequest(request, socket); } finally { - int count = clientsCount.decrementAndGet(); - if (count <= 0) { - proxyCache.shutdown(); - proxyCache = null; - } + finishProcessRequest(); + } + } + + private synchronized void startProcessRequest() throws ProxyCacheException { + proxyCache = proxyCache == null ? newHttpProxyCache() : proxyCache; + } + + private synchronized void finishProcessRequest() { + if (clientsCount.decrementAndGet() <= 0) { + proxyCache.shutdown(); + proxyCache = null; } } @@ -65,6 +72,10 @@ public void shutdown() { clientsCount.set(0); } + public int getClientsCount() { + return clientsCount.get(); + } + private HttpProxyCache newHttpProxyCache() throws ProxyCacheException { HttpUrlSource source = new HttpUrlSource(url); FileCache cache = new FileCache(fileNameGenerator.generate(url)); diff --git a/library/src/main/java/com/danikula/videocache/HttpUrlSource.java b/library/src/main/java/com/danikula/videocache/HttpUrlSource.java index 20379ae..92b5cd8 100644 --- a/library/src/main/java/com/danikula/videocache/HttpUrlSource.java +++ b/library/src/main/java/com/danikula/videocache/HttpUrlSource.java @@ -3,12 +3,14 @@ import android.text.TextUtils; import android.util.Log; +import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.net.HttpURLConnection; import java.net.URL; +import static com.danikula.videocache.ProxyCacheUtils.DEFAULT_BUFFER_SIZE; import static com.danikula.videocache.ProxyCacheUtils.LOG_TAG; import static java.net.HttpURLConnection.HTTP_OK; import static java.net.HttpURLConnection.HTTP_PARTIAL; @@ -36,7 +38,7 @@ public HttpUrlSource(String url, String mime) { } @Override - public int available() throws ProxyCacheException { + public synchronized int available() throws ProxyCacheException { if (available == Integer.MIN_VALUE) { fetchContentInfo(); } @@ -52,7 +54,7 @@ public void open(int offset) throws ProxyCacheException { connection.setRequestProperty("Range", "bytes=" + offset + "-"); } mime = connection.getContentType(); - inputStream = connection.getInputStream(); + inputStream = new BufferedInputStream(connection.getInputStream(), DEFAULT_BUFFER_SIZE); available = readSourceAvailableBytes(connection, offset); } catch (IOException e) { throw new ProxyCacheException("Error opening connection for " + url + " with offset " + offset, e); @@ -91,22 +93,27 @@ public int read(byte[] buffer) throws ProxyCacheException { private void fetchContentInfo() throws ProxyCacheException { Log.d(LOG_TAG, "Read content info from " + url); HttpURLConnection urlConnection = null; + InputStream inputStream = null; try { urlConnection = (HttpURLConnection) new URL(url).openConnection(); + urlConnection.setConnectTimeout(10000); + urlConnection.setReadTimeout(10000); urlConnection.setRequestMethod("HEAD"); available = urlConnection.getContentLength(); mime = urlConnection.getContentType(); - Log.i(LOG_TAG, "Info read: " + this); + inputStream = urlConnection.getInputStream(); + Log.i(LOG_TAG, "Content info for `" + url + "`: mime: " + mime + ", content-length: " + available); } catch (IOException e) { throw new ProxyCacheException("Error fetching Content-Length from " + url); } finally { + ProxyCacheUtils.close(inputStream); if (urlConnection != null) { urlConnection.disconnect(); } } } - public String getMime() throws ProxyCacheException { + public synchronized String getMime() throws ProxyCacheException { if (TextUtils.isEmpty(mime)) { fetchContentInfo(); } @@ -115,10 +122,6 @@ public String getMime() throws ProxyCacheException { @Override public String toString() { - return "HttpUrlSource{" + - "url='" + url + '\'' + - ", available=" + available + - ", mime='" + mime + '\'' + - '}'; + return "HttpUrlSource{url='" + url + "}"; } } diff --git a/library/src/main/java/com/danikula/videocache/ProxyCache.java b/library/src/main/java/com/danikula/videocache/ProxyCache.java index 6cbf5cf..50c355c 100644 --- a/library/src/main/java/com/danikula/videocache/ProxyCache.java +++ b/library/src/main/java/com/danikula/videocache/ProxyCache.java @@ -40,19 +40,11 @@ public int read(byte[] buffer, long offset, int length) throws ProxyCacheExcepti while (!cache.isCompleted() && cache.available() < (offset + length) && !stopped) { readSourceAsync(); waitForSourceData(); - checkIsCacheValid(); checkReadSourceErrorsCount(); } return cache.read(buffer, offset, length); } - private void checkIsCacheValid() throws ProxyCacheException { - int sourceAvailable = source.available(); - if (sourceAvailable > 0 && cache.available() > sourceAvailable) { - throw new ProxyCacheException("Unexpected cache: cache [" + cache.available() + " bytes] > source[" + sourceAvailable + " bytes]"); - } - } - private void checkReadSourceErrorsCount() throws ProxyCacheException { int errorsCount = readSourceErrorsCount.get(); if (errorsCount >= MAX_READ_SOURCE_ATTEMPTS) { @@ -76,10 +68,10 @@ public void shutdown() { } } - private void readSourceAsync() throws ProxyCacheException { + private synchronized void readSourceAsync() throws ProxyCacheException { boolean readingInProgress = sourceReaderThread != null && sourceReaderThread.getState() != Thread.State.TERMINATED; if (!stopped && !cache.isCompleted() && !readingInProgress) { - sourceReaderThread = new Thread(new SourceReaderRunnable(), "Source reader for ProxyCache"); + sourceReaderThread = new Thread(new SourceReaderRunnable(), "Source reader for " + source); sourceReaderThread.start(); } } @@ -102,7 +94,7 @@ private void notifyNewCacheDataAvailable(int cachePercentage) { } } - protected void onCacheAvailable(int percents){ + protected void onCacheAvailable(int percents) { } private void readSource() { diff --git a/library/src/main/java/com/danikula/videocache/ProxyCacheUtils.java b/library/src/main/java/com/danikula/videocache/ProxyCacheUtils.java index 3a61f7b..be57331 100644 --- a/library/src/main/java/com/danikula/videocache/ProxyCacheUtils.java +++ b/library/src/main/java/com/danikula/videocache/ProxyCacheUtils.java @@ -1,8 +1,10 @@ package com.danikula.videocache; import android.text.TextUtils; +import android.util.Log; import android.webkit.MimeTypeMap; +import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -74,4 +76,14 @@ static String decode(String url) { throw new RuntimeException("Error decoding url", e); } } + + static void close(Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException e) { + Log.e(LOG_TAG, "Error closing resource", e); + } + } + } } diff --git a/sample/build.gradle b/sample/build.gradle index 8ffffc2..8e04185 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -39,7 +39,7 @@ dependencies { // compile project(':library') compile 'com.android.support:support-v4:23.0.0' compile 'org.androidannotations:androidannotations-api:3.3.2' - compile 'com.danikula:videocache:2.0.7' + compile 'com.danikula:videocache:2.0.9' compile 'com.viewpagerindicator:library:2.4.2-SNAPSHOT@aar' apt 'org.androidannotations:androidannotations:3.3.2' } diff --git a/sample/src/main/java/com/danikula/videocache/sample/GalleryVideoFragment.java b/sample/src/main/java/com/danikula/videocache/sample/GalleryVideoFragment.java index 10a631a..d5ead29 100644 --- a/sample/src/main/java/com/danikula/videocache/sample/GalleryVideoFragment.java +++ b/sample/src/main/java/com/danikula/videocache/sample/GalleryVideoFragment.java @@ -100,6 +100,7 @@ public void onPause() { public void onDestroy() { super.onDestroy(); + videoView.stopPlayback(); App.getProxy(getActivity()).unregisterCacheListener(this); } diff --git a/sample/src/main/java/com/danikula/videocache/sample/VideoFragment.java b/sample/src/main/java/com/danikula/videocache/sample/VideoFragment.java index d35c4f1..c605eff 100644 --- a/sample/src/main/java/com/danikula/videocache/sample/VideoFragment.java +++ b/sample/src/main/java/com/danikula/videocache/sample/VideoFragment.java @@ -70,6 +70,7 @@ public void onPause() { public void onDestroy() { super.onDestroy(); + videoView.stopPlayback(); App.getProxy(getActivity()).unregisterCacheListener(this); } diff --git a/test/src/test/java/com/danikula/videocache/ProxyCacheTest.java b/test/src/test/java/com/danikula/videocache/ProxyCacheTest.java index 3445f91..5654f33 100644 --- a/test/src/test/java/com/danikula/videocache/ProxyCacheTest.java +++ b/test/src/test/java/com/danikula/videocache/ProxyCacheTest.java @@ -5,7 +5,6 @@ import com.danikula.videocache.support.PhlegmaticByteArraySource; import com.danikula.videocache.test.BuildConfig; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricGradleTestRunner; @@ -91,17 +90,6 @@ public void testReuseCache() throws Exception { assertThat(fetchedData).isEqualTo(sourceCopy); } - @Test(expected = ProxyCacheException.class) - public void testNoMoreSource() throws Exception { - int sourceSize = 942; - int cacheSize = 6157; - ByteArraySource source = new ByteArraySource(generate(sourceSize)); - ByteArrayCache cache = new ByteArrayCache(generate(cacheSize)); - ProxyCache proxyCache = new ProxyCache(source, cache); - proxyCache.read(new byte[sourceSize + cacheSize], sourceSize + cacheSize + 1, 10); - Assert.fail(); - } - @Test public void testProxyWithPhlegmaticSource() throws Exception { int dataSize = 100000;