Merge pull request #1759 from ag2s20150909/epublazy

优化
pull/1771/head
kunfei 3 years ago committed by GitHub
commit d7607a453e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      app/src/main/java/io/legado/app/help/http/cronet/AbsCallBack.kt
  2. 3
      app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt
  3. 8
      app/src/main/java/io/legado/app/utils/ThrowableExtensions.kt
  4. 707
      epublib/src/main/java/me/ag2s/epublib/zip/AndroidRandomReadableFile.java
  5. 33
      epublib/src/main/java/me/ag2s/utils/Android11CloseGuard.java
  6. 38
      epublib/src/main/java/me/ag2s/utils/AndroidCloseGuard.java
  7. 83
      epublib/src/main/java/me/ag2s/utils/AndroidRefCloseGuard.java
  8. 16
      epublib/src/main/java/me/ag2s/utils/ThrowableUtils.java

@ -2,6 +2,7 @@ package io.legado.app.help.http.cronet
import io.legado.app.help.http.okHttpClient
import io.legado.app.utils.DebugLog
import io.legado.app.utils.rethrowAsIOException
import okhttp3.*
import okhttp3.EventListener
import okhttp3.MediaType.Companion.toMediaTypeOrNull
@ -123,7 +124,7 @@ abstract class AbsCallBack(
//UrlResponseInfo可能为null
override fun onFailed(request: UrlRequest, info: UrlResponseInfo?, error: CronetException) {
DebugLog.i(javaClass.name, error.message.toString())
mException = IOException(error.message, error)
mException = error.rethrowAsIOException()
this.eventListener?.callFailed(mCall, error)
responseCallback?.onFailure(mCall, error)
}

@ -55,9 +55,12 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
* 判断Cronet是否安装完成
*/
fun install(): Boolean {
synchronized(this) {
if (cacheInstall) {
return true
}
}
if (AppConfig.isGooglePlay) {
return false
}

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

@ -1,5 +1,7 @@
package me.ag2s.epublib.zip;
import static me.ag2s.utils.ThrowableUtils.rethrowAsIOException;
import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
@ -16,78 +18,156 @@ import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UTFDataFormatException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import me.ag2s.utils.AndroidCloseGuard;
@SuppressWarnings("unused")
public class AndroidRandomReadableFile implements DataInput, Closeable {
private ParcelFileDescriptor pfd;
private final ParcelFileDescriptor pfd;
private FileInputStream fis;
private DataInputStream dis;
public long pos = 0;
private final Object lock = new Object();
/**
* 读取基本类型的buffer
*/
private final byte[] readBuffer = new byte[8];
private long pos = 0;
private final AndroidCloseGuard guard = AndroidCloseGuard.getInstance();
public AndroidRandomReadableFile(@NonNull Context context, @NonNull Uri treeUri) throws FileNotFoundException {
try {
pfd = context.getContentResolver().openFileDescriptor(treeUri, "r");
fis = new FileInputStream(pfd.getFileDescriptor());
dis = new DataInputStream(fis);
} catch (FileNotFoundException e) {
throw e;
}
guard.open("close");
}
public final FileDescriptor getFD() {
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() {
synchronized (lock) {
if (fis == null || pos != getPos()) {
fis = new FileInputStream(pfd.getFileDescriptor());
}
}
return fis.getChannel();
}
public final FileInputStream getFileInputStream() {
if (fis == null || pos != getPos()) {
synchronized (lock) {
if (fis == null || this.pos != getPos()) {
this.pos = getPos();
fis = new FileInputStream(pfd.getFileDescriptor());
}
return fis;
}
public int read() throws IOException {
byte[] b = new byte[1];
return (read(b, 0, 1) != -1) ? b[0] & 0xff : -1;
}
public int read(byte[] b) {
try {
return read(b, 0, b.length);
} catch (IOException e) {
e.printStackTrace();
return -1;
}
return fis;
}
public int read(byte[] b, int off, int len) throws IOException {
/**
* Reads a byte of data from this input stream. This method blocks
* if no input is yet available.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* file is reached.
* @throws IOException if an I/O error occurs.
*/
public int read() throws IOException {
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 (Exception e) {
return -1;
}
} catch (ErrnoException e) {
throw rethrowAsIOException(e);
}
}
/**
* Reads up to <code>b.length</code> bytes of data from this input
* stream into an array of bytes. This method blocks until some input
* is available.
*
* @param b the buffer into which the data is read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the file has been reached.
* @throws IOException if an I/O error occurs.
*/
public int read(byte[] b) throws IOException {
return readBytes(b, 0, b.length);
}
/**
* Reads up to <code>len</code> bytes of data from this input stream
* into an array of bytes. If <code>len</code> is not zero, the method
* blocks until some input is available; otherwise, no
* bytes are read and <code>0</code> is returned.
*
* @param b the buffer into which the data is read.
* @param off the start offset in the destination array <code>b</code>
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the file has been reached.
* @throws NullPointerException If <code>b</code> is <code>null</code>.
* @throws IndexOutOfBoundsException If <code>off</code> is negative,
* <code>len</code> is negative, or <code>len</code> is greater than
* <code>b.length - off</code>
* @throws IOException if an I/O error occurs.
*/
public int read(byte[] b, int off, int len) throws IOException {
return readBytes(b, off, len);
}
private void syncInputStream() {
this.pos = getPos();
fis = new FileInputStream(pfd.getFileDescriptor());
dis = new DataInputStream(fis);
}
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);
}
}
@ -95,53 +175,199 @@ 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);
}
}
public long getPos() {
/**
* 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 (Exception e) {
return -1;
} catch (ErrnoException e) {
throw rethrowAsIOException(e);
}
}
@Override
public void readFully(byte[] b) throws IOException {
public long getPos() {
try {
android.system.Os.read(pfd.getFileDescriptor(), b, 0, b.length);
return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_CUR);
} catch (ErrnoException e) {
e.printStackTrace();
return -1;
}
}
/**
* Reads some bytes from an input
* stream and stores them into the buffer
* array {@code b}. The number of bytes
* read is equal
* to the length of {@code b}.
* <p>
* This method blocks until one of the
* following conditions occurs:
* <ul>
* <li>{@code b.length}
* bytes of input data are available, in which
* case a normal return is made.
*
* <li>End of
* file is detected, in which case an {@code EOFException}
* is thrown.
*
* <li>An I/O error occurs, in
* which case an {@code IOException} other
* than {@code EOFException} is thrown.
* </ul>
* <p>
* If {@code b} is {@code null},
* a {@code NullPointerException} is thrown.
* If {@code b.length} is zero, then
* no bytes are read. Otherwise, the first
* byte read is stored into element {@code b[0]},
* the next one into {@code b[1]}, and
* so on.
* If an exception is thrown from
* this method, then it may be that some but
* not all bytes of {@code b} have been
* updated with data from the input stream.
*
* @param b the buffer into which the data is read.
* @throws EOFException if this stream reaches the end before reading
* all the bytes.
* @throws IOException if an I/O error occurs.
*/
@Override
public void readFully(byte[] b) throws IOException {
readFully(b, 0, b.length);
}
/**
* Reads {@code len}
* bytes from
* an input stream.
* <p>
* This method
* blocks until one of the following conditions
* occurs:
* <ul>
* <li>{@code len} bytes
* of input data are available, in which case
* a normal return is made.
*
* <li>End of file
* is detected, in which case an {@code EOFException}
* is thrown.
*
* <li>An I/O error occurs, in
* which case an {@code IOException} other
* than {@code EOFException} is thrown.
* </ul>
* <p>
* If {@code b} is {@code null},
* a {@code NullPointerException} is thrown.
* If {@code off} is negative, or {@code len}
* is negative, or {@code off+len} is
* greater than the length of the array {@code b},
* then an {@code IndexOutOfBoundsException}
* is thrown.
* If {@code len} is zero,
* then no bytes are read. Otherwise, the first
* byte read is stored into element {@code b[off]},
* the next one into {@code b[off+1]},
* and so on. The number of bytes read is,
* at most, equal to {@code len}.
*
* @param b the buffer into which the data is read.
* @param off an int specifying the offset into the data.
* @param len an int specifying the number of bytes to read.
* @throws EOFException if this stream reaches the end before reading
* all the bytes.
* @throws IOException if an I/O error occurs.
*/
@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);
}
/**
* Makes an attempt to skip over
* {@code n} bytes
* of data from the input
* stream, discarding the skipped bytes. However,
* it may skip
* over some smaller number of
* bytes, possibly zero. This may result from
* any of a
* number of conditions; reaching
* end of file before {@code n} bytes
* have been skipped is
* only one possibility.
* This method never throws an {@code EOFException}.
* The actual
* number of bytes skipped is returned.
*
* @param n the number of bytes to be skipped.
* @return the number of bytes actually skipped.
* @throws IOException if an I/O error occurs.
*/
@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,
* {@code false} if that byte is zero.
* This method is suitable for reading
* the byte written by the {@code writeBoolean}
* method of interface {@code DataOutput}.
*
* @return the {@code boolean} value read.
* @throws EOFException if this stream reaches the end before reading
* all the bytes.
* @throws IOException if an I/O error occurs.
*/
@Override
public boolean readBoolean() throws IOException {
int ch = this.read();
@ -151,6 +377,20 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
}
/**
* Reads and returns one input byte.
* The byte is treated as a signed value in
* the range {@code -128} through {@code 127},
* inclusive.
* This method is suitable for
* reading the byte written by the {@code writeByte}
* method of interface {@code DataOutput}.
*
* @return the 8-bit value read.
* @throws EOFException if this stream reaches the end before reading
* all the bytes.
* @throws IOException if an I/O error occurs.
*/
@Override
public byte readByte() throws IOException {
int ch = this.read();
@ -160,6 +400,24 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
}
/**
* Reads one input byte, zero-extends
* it to type {@code int}, and returns
* the result, which is therefore in the range
* {@code 0}
* through {@code 255}.
* This method is suitable for reading
* the byte written by the {@code writeByte}
* method of interface {@code DataOutput}
* if the argument to {@code writeByte}
* was intended to be a value in the range
* {@code 0} through {@code 255}.
*
* @return the unsigned 8-bit value read.
* @throws EOFException if this stream reaches the end before reading
* all the bytes.
* @throws IOException if an I/O error occurs.
*/
@Override
public int readUnsignedByte() throws IOException {
int ch = this.read();
@ -168,32 +426,132 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
return ch;
}
private final byte[] readBuffer = new byte[8];
/**
* Reads two input bytes and returns
* a {@code short} value. Let {@code a}
* be the first byte read and {@code b}
* be the second byte. The value
* returned
* is:
* <pre>{@code (short)((a << 8) | (b & 0xff))
* }</pre>
* This method
* is suitable for reading the bytes written
* by the {@code writeShort} method of
* interface {@code DataOutput}.
*
* @return the 16-bit value read.
* @throws EOFException if this stream reaches the end before reading
* all the bytes.
* @throws IOException if an I/O error occurs.
*/
@Override
public short readShort() throws IOException {
readFully(readBuffer, 0, 2);
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get();
}
/**
* Reads two input bytes and returns
* an {@code int} value in the range {@code 0}
* through {@code 65535}. Let {@code a}
* be the first byte read and
* {@code b}
* be the second byte. The value returned is:
* <pre>{@code (((a & 0xff) << 8) | (b & 0xff))
* }</pre>
* This method is suitable for reading the bytes
* written by the {@code writeShort} method
* of interface {@code DataOutput} if
* the argument to {@code writeShort}
* was intended to be a value in the range
* {@code 0} through {@code 65535}.
*
* @return the unsigned 16-bit value read.
* @throws EOFException if this stream reaches the end before reading
* all the bytes.
* @throws IOException if an I/O error occurs.
*/
@Override
public int readUnsignedShort() throws IOException {
readFully(readBuffer, 0, 2);
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get() & 0xffff;
}
/**
* Reads two input bytes and returns a {@code char} value.
* Let {@code a}
* be the first byte read and {@code b}
* be the second byte. The value
* returned is:
* <pre>{@code (char)((a << 8) | (b & 0xff))
* }</pre>
* This method
* is suitable for reading bytes written by
* the {@code writeChar} method of interface
* {@code DataOutput}.
*
* @return the {@code char} value read.
* @throws EOFException if this stream reaches the end before reading
* all the bytes.
* @throws IOException if an I/O error occurs.
*/
@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();
}
/**
* Reads four input bytes and returns an
* {@code int} value. Let {@code a-d}
* be the first through fourth bytes read. The value returned is:
* <pre>{@code
* (((a & 0xff) << 24) | ((b & 0xff) << 16) |
* ((c & 0xff) << 8) | (d & 0xff))
* }</pre>
* This method is suitable
* for reading bytes written by the {@code writeInt}
* method of interface {@code DataOutput}.
*
* @return the {@code int} value read.
* @throws EOFException if this stream reaches the end before reading
* all the bytes.
* @throws IOException if an I/O error occurs.
*/
@Override
public int readInt() throws IOException {
readFully(readBuffer, 0, 4);
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asIntBuffer().get();
}
/**
* Reads eight input bytes and returns
* a {@code long} value. Let {@code a-h}
* be the first through eighth bytes read.
* The value returned is:
* <pre>{@code
* (((long)(a & 0xff) << 56) |
* ((long)(b & 0xff) << 48) |
* ((long)(c & 0xff) << 40) |
* ((long)(d & 0xff) << 32) |
* ((long)(e & 0xff) << 24) |
* ((long)(f & 0xff) << 16) |
* ((long)(g & 0xff) << 8) |
* ((long)(h & 0xff)))
* }</pre>
* <p>
* This method is suitable
* for reading bytes written by the {@code writeLong}
* method of interface {@code DataOutput}.
*
* @return the {@code long} value read.
* @throws EOFException if this stream reaches the end before reading
* all the bytes.
* @throws IOException if an I/O error occurs.
*/
@Override
public long readLong() throws IOException {
readFully(readBuffer, 0, 8);
@ -207,162 +565,167 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
((readBuffer[7] & 255) << 0));
}
/**
* Reads four input bytes and returns
* a {@code float} value. It does this
* by first constructing an {@code int}
* value in exactly the manner
* of the {@code readInt}
* method, then converting this {@code int}
* value to a {@code float} in
* exactly the manner of the method {@code Float.intBitsToFloat}.
* This method is suitable for reading
* bytes written by the {@code writeFloat}
* method of interface {@code DataOutput}.
*
* @return the {@code float} value read.
* @throws EOFException if this stream reaches the end before reading
* all the bytes.
* @throws IOException if an I/O error occurs.
*/
@Override
public float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
/**
* Reads eight input bytes and returns
* a {@code double} value. It does this
* by first constructing a {@code long}
* value in exactly the manner
* of the {@code readLong}
* method, then converting this {@code long}
* value to a {@code double} in exactly
* the manner of the method {@code Double.longBitsToDouble}.
* This method is suitable for reading
* bytes written by the {@code writeDouble}
* method of interface {@code DataOutput}.
*
* @return the {@code double} value read.
* @throws EOFException if this stream reaches the end before reading
* all the bytes.
* @throws IOException if an I/O error occurs.
*/
@Override
public double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
private char[] lineBuffer;
@Deprecated
/**
* 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.
*
* <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.
*/
@Override
public String readLine() throws IOException {
char[] buf = lineBuffer;
if (buf == null) {
buf = lineBuffer = new char[128];
}
int room = buf.length;
int offset = 0;
int c;
StringBuilder input = new StringBuilder();
int c = -1;
boolean eol = false;
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();
}
/**
* See the general contract of the <code>readUTF</code>
* method of <code>DataInput</code>.
* <p>
* Bytes
* for this operation are read from the contained
* input stream.
*
* @return a Unicode string.
* @throws EOFException if this input stream reaches the end before
* reading all the bytes.
* @throws IOException the stream has been closed and the contained
* input stream does not support reading after close, or
* another I/O error occurs.
* @throws UTFDataFormatException if the bytes do not represent a valid
* modified UTF-8 encoding of a string.
* @see java.io.DataInputStream#readUTF(java.io.DataInput)
*/
@Override
public String readUTF() throws IOException {
if (pos != getPos()) {
syncInputStream();
}
return dis.readUTF();
return DataInputStream.readUTF(this);
}
// public final static String readUTF(DataInput in) throws IOException {
// int utflen = in.readUnsignedShort();
// byte[] bytearr = null;
// char[] chararr = null;
// if (in instanceof DataInputStream) {
// DataInputStream dis = (DataInputStream)in;
// if (dis.bytearr.length < utflen){
// dis.bytearr = new byte[utflen*2];
// dis.chararr = new char[utflen*2];
// }
// chararr = dis.chararr;
// bytearr = dis.bytearr;
// } else {
// bytearr = new byte[utflen];
// chararr = new char[utflen];
// }
//
// int c, char2, char3;
// int count = 0;
// int chararr_count=0;
//
// in.readFully(bytearr, 0, utflen);
//
// while (count < utflen) {
// c = (int) bytearr[count] & 0xff;
// if (c > 127) break;
// count++;
// chararr[chararr_count++]=(char)c;
// }
//
// while (count < utflen) {
// c = (int) bytearr[count] & 0xff;
// switch (c >> 4) {
// case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// /* 0xxxxxxx*/
// count++;
// chararr[chararr_count++]=(char)c;
// break;
// case 12: case 13:
// /* 110x xxxx 10xx xxxx*/
// count += 2;
// if (count > utflen)
// throw new UTFDataFormatException(
// "malformed input: partial character at end");
// char2 = (int) bytearr[count-1];
// if ((char2 & 0xC0) != 0x80)
// throw new UTFDataFormatException(
// "malformed input around byte " + count);
// chararr[chararr_count++]=(char)(((c & 0x1F) << 6) |
// (char2 & 0x3F));
// break;
// case 14:
// /* 1110 xxxx 10xx xxxx 10xx xxxx */
// count += 3;
// if (count > utflen)
// throw new UTFDataFormatException(
// "malformed input: partial character at end");
// char2 = (int) bytearr[count-2];
// char3 = (int) bytearr[count-1];
// if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
// throw new UTFDataFormatException(
// "malformed input around byte " + (count-1));
// chararr[chararr_count++]=(char)(((c & 0x0F) << 12) |
// ((char2 & 0x3F) << 6) |
// ((char3 & 0x3F) << 0));
// break;
// default:
// /* 10xx xxxx, 1111 xxxx */
// throw new UTFDataFormatException(
// "malformed input around byte " + count);
// }
// }
// // The number of chars produced may be less than utflen
// return new String(chararr, 0, chararr_count);
// }
@Override
public void close() throws IOException {
try {
dis.close();
} catch (Exception e) {
e.printStackTrace();
}
guard.close();
try {
if (fis != null) {
fis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (pfd != null) {
pfd.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
protected void finalize() throws Throwable {
try {
// Note that guard could be null if the constructor threw.
guard.warnIfOpen();
close();
} finally {
super.finalize();
}
}
}

@ -0,0 +1,33 @@
package me.ag2s.utils;
import android.os.Build;
import android.util.CloseGuard;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
@RequiresApi(api = Build.VERSION_CODES.R)
final class Android11CloseGuard implements AndroidCloseGuard {
@NonNull
private final CloseGuard mImpl;
public Android11CloseGuard() {
mImpl = new CloseGuard();
}
@Override
public void open(@NonNull String closeMethodName) {
mImpl.open(closeMethodName);
}
@Override
public void close() {
mImpl.close();
}
@Override
public void warnIfOpen() {
mImpl.warnIfOpen();
}
}

@ -0,0 +1,38 @@
package me.ag2s.utils;
import android.os.Build;
import androidx.annotation.NonNull;
public interface AndroidCloseGuard {
static AndroidCloseGuard getInstance() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return new Android11CloseGuard();
} else {
return new AndroidRefCloseGuard();
}
}
/**
* Initializes the instance with a warning that the caller should have explicitly called the
* {@code closeMethodName} method instead of relying on finalization.
*
* @param closeMethodName non-null name of explicit termination method. Printed by warnIfOpen.
* @throws NullPointerException if closeMethodName is null.
*/
void open(@NonNull String closeMethodName);
/**
* Marks this CloseGuard instance as closed to avoid warnings on finalization.
*/
void close();
/**
* Logs a warning if the caller did not properly cleanup by calling an explicit close method
* before finalization.
*/
void warnIfOpen();
}

@ -0,0 +1,83 @@
package me.ag2s.utils;
import androidx.annotation.NonNull;
import java.lang.reflect.Method;
final class AndroidRefCloseGuard implements AndroidCloseGuard {
private static Object closeGuardInstance;
private static Method getMethod;
private static Method closeMethod;
private static Method openMethod;
private static Method warnIfOpenMethod;
public AndroidRefCloseGuard() {
if (getMethod == null || closeMethod == null || openMethod == null || warnIfOpenMethod == null) {
try {
Class<?> closeGuardClass = Class.forName("dalvik.system.CloseGuard");
getMethod = closeGuardClass.getMethod("get");
closeMethod = closeGuardClass.getMethod("close");
openMethod = closeGuardClass.getMethod("open", String.class);
warnIfOpenMethod = closeGuardClass.getMethod("warnIfOpen");
} catch (Exception ignored) {
getMethod = null;
openMethod = null;
warnIfOpenMethod = null;
}
}
}
Object createAndOpen(String closer) {
if (getMethod != null) {
try {
if (closeGuardInstance == null) {
closeGuardInstance = getMethod.invoke(null);
}
openMethod.invoke(closeGuardInstance, closer);
return closeGuardInstance;
} catch (Exception ignored) {
}
}
return null;
}
boolean warnIfOpen(Object closeGuardInstance) {
boolean reported = false;
if (closeGuardInstance != null) {
try {
warnIfOpenMethod.invoke(closeGuardInstance);
reported = true;
} catch (Exception ignored) {
}
}
return reported;
}
@Override
public void open(@NonNull String closeMethodName) {
closeGuardInstance = createAndOpen(closeMethodName);
}
@Override
public void close() {
if (closeGuardInstance != null) {
try {
closeMethod.invoke(closeMethod);
} catch (Exception ignored) {
}
}
}
@Override
public void warnIfOpen() {
warnIfOpen(closeGuardInstance);
}
}

@ -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