pull/1759/head
ag2s20150909 3 years ago
parent 7ef57c03b9
commit 36c4e9e685
  1. 1
      app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt
  2. 8
      app/src/main/java/io/legado/app/utils/ThrowableExtensions.kt
  3. 235
      epublib/src/main/java/me/ag2s/epublib/zip/AndroidRandomReadableFile.java
  4. 2
      epublib/src/main/java/me/ag2s/utils/Android11CloseGuard.java
  5. 4
      epublib/src/main/java/me/ag2s/utils/AndroidCloseGuard.java
  6. 2
      epublib/src/main/java/me/ag2s/utils/AndroidRefCloseGuard.java
  7. 16
      epublib/src/main/java/me/ag2s/utils/ThrowableUtils.java

@ -54,6 +54,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
/** /**
* 判断Cronet是否安装完成 * 判断Cronet是否安装完成
*/ */
@Synchronized
fun install(): Boolean { fun install(): Boolean {
if (cacheInstall) { if (cacheInstall) {
return true return true

@ -1,5 +1,7 @@
package io.legado.app.utils package io.legado.app.utils
import java.io.IOException
val Throwable.msg: String val Throwable.msg: String
get() { get() {
val stackTrace = stackTraceToString() val stackTrace = stackTraceToString()
@ -9,3 +11,9 @@ val Throwable.msg: String
else -> lMsg else -> lMsg
} }
} }
fun Throwable.rethrowAsIOException(): IOException {
val newException = IOException(this.message)
newException.initCause(this)
throw newException
}

@ -1,8 +1,9 @@
package me.ag2s.epublib.zip; package me.ag2s.epublib.zip;
import static me.ag2s.utils.ThrowableUtils.rethrowAsIOException;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.system.ErrnoException; import android.system.ErrnoException;
import android.system.OsConstants; import android.system.OsConstants;
@ -22,12 +23,18 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import me.ag2s.epublib.util.AndroidCloseGuard; import me.ag2s.utils.AndroidCloseGuard;
@SuppressWarnings("unused")
public class AndroidRandomReadableFile implements DataInput, Closeable { public class AndroidRandomReadableFile implements DataInput, Closeable {
private final ParcelFileDescriptor pfd; private final ParcelFileDescriptor pfd;
private FileInputStream fis; private FileInputStream fis;
private final Object lock = new Object();
/**
* 读取基本类型的buffer
*/
private final byte[] readBuffer = new byte[8];
private long pos = 0; private long pos = 0;
private final AndroidCloseGuard guard = AndroidCloseGuard.getInstance(); 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 { public AndroidRandomReadableFile(@NonNull Context context, @NonNull Uri treeUri) throws FileNotFoundException {
pfd = context.getContentResolver().openFileDescriptor(treeUri, "r"); pfd = context.getContentResolver().openFileDescriptor(treeUri, "r");
fis = new FileInputStream(pfd.getFileDescriptor()); fis = new FileInputStream(pfd.getFileDescriptor());
//dis = new DataInputStream(fis);
guard.open("close"); guard.open("close");
} }
@ -45,18 +51,42 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
return pfd.getFileDescriptor(); return pfd.getFileDescriptor();
} }
/**
* Returns the unique {@link java.nio.channels.FileChannel FileChannel}
* object associated with this file.
*
* <p> 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() { public final FileChannel getChannel() {
synchronized (lock) {
if (fis == null || pos != getPos()) { if (fis == null || pos != getPos()) {
fis = new FileInputStream(pfd.getFileDescriptor()); fis = new FileInputStream(pfd.getFileDescriptor());
} }
}
return fis.getChannel(); return fis.getChannel();
} }
public final FileInputStream getFileInputStream() { public final FileInputStream getFileInputStream() {
synchronized (lock) {
if (fis == null || this.pos != getPos()) { if (fis == null || this.pos != getPos()) {
this.pos = getPos(); this.pos = getPos();
fis = new FileInputStream(pfd.getFileDescriptor()); fis = new FileInputStream(pfd.getFileDescriptor());
} }
}
return fis; return fis;
} }
@ -69,8 +99,23 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
* @throws IOException if an I/O error occurs. * @throws IOException if an I/O error occurs.
*/ */
public int read() throws IOException { public int read() throws IOException {
byte[] b = new byte[1]; return (read(readBuffer, 0, 1) != -1) ? readBuffer[0] & 0xff : -1;
return (read(b, 0, 1) != -1) ? b[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. * @throws IOException if an I/O error occurs.
*/ */
public int read(byte[] b) throws IOException { 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. * @throws IOException if an I/O error occurs.
*/ */
public int read(byte[] b, int off, int len) throws IOException { public int read(byte[] b, int off, int len) throws IOException {
try { return readBytes(b, off, len);
return android.system.Os.read(pfd.getFileDescriptor(), b, off, len);
} catch (Exception e) {
throw new IOException(e);
}
} }
@ -126,8 +166,8 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
public void seek(long pos) throws IOException { public void seek(long pos) throws IOException {
try { try {
android.system.Os.lseek(pfd.getFileDescriptor(), pos, OsConstants.SEEK_SET); android.system.Os.lseek(pfd.getFileDescriptor(), pos, OsConstants.SEEK_SET);
} catch (Exception e) { } catch (ErrnoException e) {
throw new IOException(e); throw rethrowAsIOException(e);
} }
} }
@ -135,21 +175,37 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
public long length() throws IOException { public long length() throws IOException {
try { try {
return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_END); return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_END);
} catch (Exception e) { } catch (ErrnoException e) {
throw new IOException(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() { public long getPos() {
try { try {
return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_CUR); return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_CUR);
} catch (Exception e) { } catch (ErrnoException e) {
return -1; return -1;
} }
} }
/** /**
* Reads some bytes from an input * Reads some bytes from an input
* stream and stores them into the buffer * stream and stores them into the buffer
@ -192,13 +248,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
*/ */
@Override @Override
public void readFully(byte[] b) throws IOException { public void readFully(byte[] b) throws IOException {
try { readFully(b, 0, b.length);
android.system.Os.read(pfd.getFileDescriptor(), b, 0, b.length);
} catch (ErrnoException e) {
e.printStackTrace();
}
} }
/** /**
@ -246,12 +296,13 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
*/ */
@Override @Override
public void readFully(byte[] b, int off, int len) throws IOException { public void readFully(byte[] b, int off, int len) throws IOException {
try { int n = 0;
android.system.Os.read(pfd.getFileDescriptor(), b, off, len); do {
//syncInputStream(); int count = this.read(b, off + n, len - n);
} catch (ErrnoException e) { if (count < 0)
throw new IOException(e); throw new EOFException();
} n += count;
} while (n < len);
} }
/** /**
@ -277,15 +328,33 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
*/ */
@Override @Override
public int skipBytes(int n) throws IOException { public int skipBytes(int n) throws IOException {
try { long pos;
byte[] b = new byte[n]; long len;
return android.system.Os.read(pfd.getFileDescriptor(), b, 0, b.length); long newpos;
} catch (Exception e) {
return -1; 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 * Reads one input byte and returns
* {@code true} if that byte is nonzero, * {@code true} if that byte is nonzero,
@ -357,7 +426,6 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
return ch; return ch;
} }
private final byte[] readBuffer = new byte[8];
/** /**
* Reads two input bytes and returns * Reads two input bytes and returns
@ -432,7 +500,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
@Override @Override
public char readChar() throws IOException { public char readChar() throws IOException {
readFully(readBuffer, 0, 2); 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()); return Double.longBitsToDouble(readLong());
} }
private char[] lineBuffer;
/** /**
* See the general contract of the <code>readLine</code> * Reads the next line of text from this file. This method successively
* method of <code>DataInput</code>. * reads bytes from the file, starting at the current file pointer,
* <p> * until it reaches a line terminator or the end
* Bytes * of the file. Each byte is converted into a character by taking the
* for this operation are read from the contained * byte's value for the lower eight bits of the character and setting the
* input stream. * 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. * <p> 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.
*
* <p> 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. * @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&nbsp;1.1, the preferred way to read lines of text is via the
* <code>BufferedReader.readLine()</code> method. Programs that use the
* <code>DataInputStream</code> class to read lines can be converted to use
* the <code>BufferedReader</code> class by replacing code of the form:
* <blockquote><pre>
* DataInputStream d =&nbsp;new&nbsp;DataInputStream(in);
* </pre></blockquote>
* with:
* <blockquote><pre>
* BufferedReader d
* =&nbsp;new&nbsp;BufferedReader(new&nbsp;InputStreamReader(in));
* </pre></blockquote>
*/ */
@Deprecated
@Override @Override
public String readLine() throws IOException { public String readLine() throws IOException {
char[] buf = lineBuffer; StringBuilder input = new StringBuilder();
int c = -1;
if (buf == null) { boolean eol = false;
buf = lineBuffer = new char[128];
}
int room = buf.length; while (!eol) {
int offset = 0; switch (c = read()) {
int c;
loop:
while (true) {
switch (c = this.read()) {
case -1: case -1:
case '\n': case '\n':
break loop; eol = true;
break;
case '\r': case '\r':
int c2 = this.read(); eol = true;
break loop; long cur = getFilePointer();
if ((read()) != '\n') {
default: seek(cur);
if (--room < 0) {
buf = new char[offset + 128];
room = buf.length - offset - 1;
System.arraycopy(lineBuffer, 0, buf, 0, offset);
lineBuffer = buf;
} }
buf[offset++] = (char) c; break;
default:
input.append((char) c);
break; break;
} }
} }
if ((c == -1) && (offset == 0)) {
if ((c == -1) && (input.length() == 0)) {
return null; return null;
} }
return String.copyValueOf(buf, 0, offset); return input.toString();
} }
/** /**
@ -641,9 +696,6 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
guard.close(); guard.close();
if (Build.VERSION.SDK_INT >= 28) {
//Reference.reachabilityFence(this);
}
try { try {
if (fis != null) { if (fis != null) {
fis.close(); fis.close();
@ -669,6 +721,7 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
guard.warnIfOpen(); guard.warnIfOpen();
close(); close();
} finally { } finally {

@ -1,4 +1,4 @@
package me.ag2s.epublib.util; package me.ag2s.utils;
import android.os.Build; import android.os.Build;
import android.util.CloseGuard; import android.util.CloseGuard;

@ -1,4 +1,4 @@
package me.ag2s.epublib.util; package me.ag2s.utils;
import android.os.Build; import android.os.Build;
@ -8,7 +8,7 @@ import androidx.annotation.NonNull;
public interface AndroidCloseGuard { public interface AndroidCloseGuard {
public static AndroidCloseGuard getInstance() { static AndroidCloseGuard getInstance() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return new Android11CloseGuard(); return new Android11CloseGuard();
} else { } else {

@ -1,4 +1,4 @@
package me.ag2s.epublib.util; package me.ag2s.utils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;

@ -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;
}
}
Loading…
Cancel
Save