diff --git a/app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt b/app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt index 1ed21f167..716126b55 100644 --- a/app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt +++ b/app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt @@ -54,6 +54,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() { /** * 判断Cronet是否安装完成 */ + @Synchronized fun install(): Boolean { if (cacheInstall) { return true diff --git a/app/src/main/java/io/legado/app/utils/ThrowableExtensions.kt b/app/src/main/java/io/legado/app/utils/ThrowableExtensions.kt index 409152490..a5bfaa8fc 100644 --- a/app/src/main/java/io/legado/app/utils/ThrowableExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/ThrowableExtensions.kt @@ -1,5 +1,7 @@ package io.legado.app.utils +import java.io.IOException + val Throwable.msg: String get() { val stackTrace = stackTraceToString() @@ -8,4 +10,10 @@ val Throwable.msg: String stackTrace.isNotEmpty() -> stackTrace else -> lMsg } - } \ No newline at end of file + } + +fun Throwable.rethrowAsIOException(): IOException { + val newException = IOException(this.message) + newException.initCause(this) + throw newException +} \ No newline at end of file diff --git a/epublib/src/main/java/me/ag2s/epublib/zip/AndroidRandomReadableFile.java b/epublib/src/main/java/me/ag2s/epublib/zip/AndroidRandomReadableFile.java index 6fd7e668e..7401b00f9 100644 --- a/epublib/src/main/java/me/ag2s/epublib/zip/AndroidRandomReadableFile.java +++ b/epublib/src/main/java/me/ag2s/epublib/zip/AndroidRandomReadableFile.java @@ -1,8 +1,9 @@ package me.ag2s.epublib.zip; +import static me.ag2s.utils.ThrowableUtils.rethrowAsIOException; + import android.content.Context; import android.net.Uri; -import android.os.Build; import android.os.ParcelFileDescriptor; import android.system.ErrnoException; import android.system.OsConstants; @@ -22,12 +23,18 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; -import me.ag2s.epublib.util.AndroidCloseGuard; - +import me.ag2s.utils.AndroidCloseGuard; +@SuppressWarnings("unused") public class AndroidRandomReadableFile implements DataInput, Closeable { private final ParcelFileDescriptor pfd; private FileInputStream fis; + private final Object lock = new Object(); + + /** + * 读取基本类型的buffer + */ + private final byte[] readBuffer = new byte[8]; private long pos = 0; private final AndroidCloseGuard guard = AndroidCloseGuard.getInstance(); @@ -35,7 +42,6 @@ public class AndroidRandomReadableFile implements DataInput, Closeable { public AndroidRandomReadableFile(@NonNull Context context, @NonNull Uri treeUri) throws FileNotFoundException { pfd = context.getContentResolver().openFileDescriptor(treeUri, "r"); fis = new FileInputStream(pfd.getFileDescriptor()); - //dis = new DataInputStream(fis); guard.open("close"); } @@ -45,18 +51,42 @@ public class AndroidRandomReadableFile implements DataInput, Closeable { return pfd.getFileDescriptor(); } + + /** + * Returns the unique {@link java.nio.channels.FileChannel FileChannel} + * object associated with this file. + * + *
The {@link java.nio.channels.FileChannel#position()
+ * position} of the returned channel will always be equal to
+ * this object's file-pointer offset as returned by the {@link
+ * #getFilePointer getFilePointer} method. Changing this object's
+ * file-pointer offset, whether explicitly or by reading or writing bytes,
+ * will change the position of the channel, and vice versa. Changing the
+ * file's length via this object will change the length seen via the file
+ * channel, and vice versa.
+ *
+ * @return the file channel associated with this file
+ * @spec JSR-51
+ * @since 1.4
+ */
public final FileChannel getChannel() {
- if (fis == null || pos != getPos()) {
- fis = new FileInputStream(pfd.getFileDescriptor());
+ synchronized (lock) {
+ if (fis == null || pos != getPos()) {
+ fis = new FileInputStream(pfd.getFileDescriptor());
+ }
}
+
return fis.getChannel();
}
public final FileInputStream getFileInputStream() {
- if (fis == null || this.pos != getPos()) {
- this.pos = getPos();
- fis = new FileInputStream(pfd.getFileDescriptor());
+ synchronized (lock) {
+ if (fis == null || this.pos != getPos()) {
+ this.pos = getPos();
+ fis = new FileInputStream(pfd.getFileDescriptor());
+ }
}
+
return fis;
}
@@ -69,8 +99,23 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
* @throws IOException if an I/O error occurs.
*/
public int read() throws IOException {
- byte[] b = new byte[1];
- return (read(b, 0, 1) != -1) ? b[0] & 0xff : -1;
+ return (read(readBuffer, 0, 1) != -1) ? readBuffer[0] & 0xff : -1;
+ }
+
+ /**
+ * Reads a sub array as a sequence of bytes.
+ *
+ * @param b the buffer into which the data is read.
+ * @param off the start offset of the data.
+ * @param len the number of bytes to read.
+ * @throws IOException If an I/O error has occurred.
+ */
+ private int readBytes(byte[] b, int off, int len) throws IOException {
+ try {
+ return android.system.Os.read(pfd.getFileDescriptor(), b, off, len);
+ } catch (ErrnoException e) {
+ throw rethrowAsIOException(e);
+ }
}
@@ -86,8 +131,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
* @throws IOException if an I/O error occurs.
*/
public int read(byte[] b) throws IOException {
- return read(b, 0, b.length);
-
+ return readBytes(b, 0, b.length);
}
/**
@@ -109,11 +153,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
* @throws IOException if an I/O error occurs.
*/
public int read(byte[] b, int off, int len) throws IOException {
- try {
- return android.system.Os.read(pfd.getFileDescriptor(), b, off, len);
- } catch (Exception e) {
- throw new IOException(e);
- }
+ return readBytes(b, off, len);
}
@@ -126,8 +166,8 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
public void seek(long pos) throws IOException {
try {
android.system.Os.lseek(pfd.getFileDescriptor(), pos, OsConstants.SEEK_SET);
- } catch (Exception e) {
- throw new IOException(e);
+ } catch (ErrnoException e) {
+ throw rethrowAsIOException(e);
}
}
@@ -135,21 +175,37 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
public long length() throws IOException {
try {
return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_END);
- } catch (Exception e) {
- throw new IOException(e);
+ } catch (ErrnoException e) {
+ throw rethrowAsIOException(e);
}
}
+ /**
+ * Returns the current offset in this file.
+ *
+ * @return the offset from the beginning of the file, in bytes,
+ * at which the next read or write occurs.
+ * @throws IOException if an I/O error occurs.
+ */
+ public long getFilePointer() throws IOException {
+ try {
+ return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_CUR);
+ } catch (ErrnoException e) {
+ throw rethrowAsIOException(e);
+ }
+ }
+
public long getPos() {
try {
return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_CUR);
- } catch (Exception e) {
+ } catch (ErrnoException e) {
return -1;
}
}
+
/**
* Reads some bytes from an input
* stream and stores them into the buffer
@@ -192,13 +248,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
*/
@Override
public void readFully(byte[] b) throws IOException {
- try {
- android.system.Os.read(pfd.getFileDescriptor(), b, 0, b.length);
- } catch (ErrnoException e) {
- e.printStackTrace();
- }
-
-
+ readFully(b, 0, b.length);
}
/**
@@ -246,12 +296,13 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
*/
@Override
public void readFully(byte[] b, int off, int len) throws IOException {
- try {
- android.system.Os.read(pfd.getFileDescriptor(), b, off, len);
- //syncInputStream();
- } catch (ErrnoException e) {
- throw new IOException(e);
- }
+ int n = 0;
+ do {
+ int count = this.read(b, off + n, len - n);
+ if (count < 0)
+ throw new EOFException();
+ n += count;
+ } while (n < len);
}
/**
@@ -277,15 +328,33 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
*/
@Override
public int skipBytes(int n) throws IOException {
- try {
- byte[] b = new byte[n];
- return android.system.Os.read(pfd.getFileDescriptor(), b, 0, b.length);
- } catch (Exception e) {
- return -1;
+ long pos;
+ long len;
+ long newpos;
+
+ if (n <= 0) {
+ return 0;
+ }
+ pos = getFilePointer();
+ len = length();
+ newpos = pos + n;
+ if (newpos > len) {
+ newpos = len;
}
+ seek(newpos);
+
+ /* return the actual number of bytes skipped */
+ return (int) (newpos - pos);
+// try {
+// byte[] b = new byte[n];
+// return android.system.Os.read(pfd.getFileDescriptor(), b, 0, b.length);
+// } catch (Exception e) {
+// return -1;
+// }
}
+
/**
* Reads one input byte and returns
* {@code true} if that byte is nonzero,
@@ -357,7 +426,6 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
return ch;
}
- private final byte[] readBuffer = new byte[8];
/**
* Reads two input bytes and returns
@@ -432,7 +500,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
@Override
public char readChar() throws IOException {
readFully(readBuffer, 0, 2);
- return (char) ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get();
+ return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asCharBuffer().get();
}
/**
@@ -543,73 +611,60 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
return Double.longBitsToDouble(readLong());
}
- private char[] lineBuffer;
/**
- * See the general contract of the readLine
- * method of DataInput
.
- *
- * Bytes - * for this operation are read from the contained - * input stream. + * Reads the next line of text from this file. This method successively + * reads bytes from the file, starting at the current file pointer, + * until it reaches a line terminator or the end + * of the file. Each byte is converted into a character by taking the + * byte's value for the lower eight bits of the character and setting the + * high eight bits of the character to zero. This method does not, + * therefore, support the full Unicode character set. * - * @return the next line of text from this input stream. + *
A line of text is terminated by a carriage-return character + * ({@code '\u005Cr'}), a newline character ({@code '\u005Cn'}), a + * carriage-return character immediately followed by a newline character, + * or the end of the file. Line-terminating characters are discarded and + * are not included as part of the string returned. + * + *
This method blocks until a newline character is read, a carriage
+ * return and the byte following it are read (to see if it is a newline),
+ * the end of the file is reached, or an exception is thrown.
+ *
+ * @return the next line of text from this file, or null if end
+ * of file is encountered before even one byte is read.
* @throws IOException if an I/O error occurs.
- * @see java.io.BufferedReader#readLine()
- * @see java.io.FilterInputStream##in
- * @deprecated This method does not properly convert bytes to characters.
- * As of JDK 1.1, the preferred way to read lines of text is via the
- * BufferedReader.readLine()
method. Programs that use the
- * DataInputStream
class to read lines can be converted to use
- * the BufferedReader
class by replacing code of the form:
- *
- * with: - *- * DataInputStream d = new DataInputStream(in); - *
*/ - @Deprecated + @Override public String readLine() throws IOException { - char[] buf = lineBuffer; - - if (buf == null) { - buf = lineBuffer = new char[128]; - } + StringBuilder input = new StringBuilder(); + int c = -1; + boolean eol = false; - int room = buf.length; - int offset = 0; - int c; - - loop: - while (true) { - switch (c = this.read()) { + while (!eol) { + switch (c = read()) { case -1: case '\n': - break loop; - + eol = true; + break; case '\r': - int c2 = this.read(); - break loop; - - default: - if (--room < 0) { - buf = new char[offset + 128]; - room = buf.length - offset - 1; - System.arraycopy(lineBuffer, 0, buf, 0, offset); - lineBuffer = buf; + eol = true; + long cur = getFilePointer(); + if ((read()) != '\n') { + seek(cur); } - buf[offset++] = (char) c; + break; + default: + input.append((char) c); break; } } - if ((c == -1) && (offset == 0)) { + + if ((c == -1) && (input.length() == 0)) { return null; } - return String.copyValueOf(buf, 0, offset); + return input.toString(); } /** @@ -641,9 +696,6 @@ public class AndroidRandomReadableFile implements DataInput, Closeable { guard.close(); - if (Build.VERSION.SDK_INT >= 28) { - //Reference.reachabilityFence(this); - } try { if (fis != null) { fis.close(); @@ -669,6 +721,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable { guard.warnIfOpen(); + close(); } finally { diff --git a/epublib/src/main/java/me/ag2s/epublib/util/Android11CloseGuard.java b/epublib/src/main/java/me/ag2s/utils/Android11CloseGuard.java similarity index 95% rename from epublib/src/main/java/me/ag2s/epublib/util/Android11CloseGuard.java rename to epublib/src/main/java/me/ag2s/utils/Android11CloseGuard.java index 8e2d0ca50..a318315ca 100644 --- a/epublib/src/main/java/me/ag2s/epublib/util/Android11CloseGuard.java +++ b/epublib/src/main/java/me/ag2s/utils/Android11CloseGuard.java @@ -1,4 +1,4 @@ -package me.ag2s.epublib.util; +package me.ag2s.utils; import android.os.Build; import android.util.CloseGuard; diff --git a/epublib/src/main/java/me/ag2s/epublib/util/AndroidCloseGuard.java b/epublib/src/main/java/me/ag2s/utils/AndroidCloseGuard.java similarity index 92% rename from epublib/src/main/java/me/ag2s/epublib/util/AndroidCloseGuard.java rename to epublib/src/main/java/me/ag2s/utils/AndroidCloseGuard.java index ba8b963e3..0472bfcc6 100644 --- a/epublib/src/main/java/me/ag2s/epublib/util/AndroidCloseGuard.java +++ b/epublib/src/main/java/me/ag2s/utils/AndroidCloseGuard.java @@ -1,4 +1,4 @@ -package me.ag2s.epublib.util; +package me.ag2s.utils; import android.os.Build; @@ -8,7 +8,7 @@ import androidx.annotation.NonNull; public interface AndroidCloseGuard { - public static AndroidCloseGuard getInstance() { + static AndroidCloseGuard getInstance() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { return new Android11CloseGuard(); } else { diff --git a/epublib/src/main/java/me/ag2s/epublib/util/AndroidRefCloseGuard.java b/epublib/src/main/java/me/ag2s/utils/AndroidRefCloseGuard.java similarity index 98% rename from epublib/src/main/java/me/ag2s/epublib/util/AndroidRefCloseGuard.java rename to epublib/src/main/java/me/ag2s/utils/AndroidRefCloseGuard.java index 8daee7837..deda4f3e4 100644 --- a/epublib/src/main/java/me/ag2s/epublib/util/AndroidRefCloseGuard.java +++ b/epublib/src/main/java/me/ag2s/utils/AndroidRefCloseGuard.java @@ -1,4 +1,4 @@ -package me.ag2s.epublib.util; +package me.ag2s.utils; import androidx.annotation.NonNull; diff --git a/epublib/src/main/java/me/ag2s/utils/ThrowableUtils.java b/epublib/src/main/java/me/ag2s/utils/ThrowableUtils.java new file mode 100644 index 000000000..a5e7b1685 --- /dev/null +++ b/epublib/src/main/java/me/ag2s/utils/ThrowableUtils.java @@ -0,0 +1,16 @@ +package me.ag2s.utils; + +import androidx.annotation.NonNull; + +import java.io.IOException; + +public class ThrowableUtils { + + + public static @NonNull + IOException rethrowAsIOException(Throwable throwable) throws IOException { + IOException newException = new IOException(throwable.getMessage()); + newException.initCause(throwable); + throw newException; + } +}- * BufferedReader d - * = new BufferedReader(new InputStreamReader(in)); - *