Restored enforced synchronization on BitmapRegionDecoder for SDK < 21, which did not have internal synchronization
diff --git a/library/src/main/java/com/davemorrissey/labs/subscaleview/decoder/SkiaImageRegionDecoder.java b/library/src/main/java/com/davemorrissey/labs/subscaleview/decoder/SkiaImageRegionDecoder.java
index 9d216f6..28f5d80 100644
--- a/library/src/main/java/com/davemorrissey/labs/subscaleview/decoder/SkiaImageRegionDecoder.java
+++ b/library/src/main/java/com/davemorrissey/labs/subscaleview/decoder/SkiaImageRegionDecoder.java
@@ -7,10 +7,12 @@
 import android.content.res.Resources;
 import android.graphics.*;
 import android.net.Uri;
+import android.os.Build;
 import android.text.TextUtils;
 
 import java.io.InputStream;
 import java.util.List;
+import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
@@ -21,9 +23,9 @@
  * however it has some problems with grayscale, indexed and CMYK images.
  *
  * A {@link ReadWriteLock} is used to delegate responsibility for multi threading behaviour to the
- * {@link BitmapRegionDecoder} instance, whilst allowing this class to block until no tiles are being
- * loaded before recycling the decoder. In practice, {@link BitmapRegionDecoder} is single threaded
- * so this has no real impact on performance.
+ * {@link BitmapRegionDecoder} instance on SDK >= 21, whilst allowing this class to block until no
+ * tiles are being loaded before recycling the decoder. In practice, {@link BitmapRegionDecoder} is
+ * synchronized internally so this has no real impact on performance.
  */
 public class SkiaImageRegionDecoder implements ImageRegionDecoder {
 
@@ -97,7 +99,7 @@
 
     @Override
     public Bitmap decodeRegion(Rect sRect, int sampleSize) {
-        decoderLock.readLock().lock();
+        getDecodeLock().lock();
         try {
             if (decoder != null && !decoder.isRecycled()) {
                 BitmapFactory.Options options = new BitmapFactory.Options();
@@ -112,27 +114,36 @@
                 throw new IllegalStateException("Cannot decode region after decoder has been recycled");
             }
         } finally {
-            decoderLock.readLock().unlock();
+            getDecodeLock().unlock();
         }
     }
 
     @Override
-    public boolean isReady() {
-        decoderLock.readLock().lock();
-        try {
-            return decoder != null && !decoder.isRecycled();
-        } finally {
-            decoderLock.readLock().unlock();
-        }
+    public synchronized boolean isReady() {
+        return decoder != null && !decoder.isRecycled();
     }
 
     @Override
-    public void recycle() {
+    public synchronized void recycle() {
         decoderLock.writeLock().lock();
         try {
             decoder.recycle();
+            decoder = null;
         } finally {
             decoderLock.writeLock().unlock();
         }
     }
+
+    /**
+     * Before SDK 21, BitmapRegionDecoder was not synchronized internally. Any attempt to decode
+     * regions from multiple threads with one decoder instance causes a segfault. For old versions
+     * use the write lock to enforce single threaded decoding.
+     */
+    private Lock getDecodeLock() {
+        if (Build.VERSION.SDK_INT < 21) {
+            return decoderLock.writeLock();
+        } else {
+            return decoderLock.readLock();
+        }
+    }
 }