实现epub的懒加载(未经广泛测试)
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
package me.ag2s.epublib.util;
|
||||||
|
|
||||||
|
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.epublib.util;
|
||||||
|
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public interface AndroidCloseGuard {
|
||||||
|
|
||||||
|
|
||||||
|
public 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.epublib.util;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package me.ag2s.epublib.zip;
|
|||||||
|
|
||||||
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;
|
||||||
@@ -16,26 +17,30 @@ import java.io.FileDescriptor;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.UTFDataFormatException;
|
||||||
import java.nio.ByteBuffer;
|
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;
|
||||||
|
|
||||||
|
|
||||||
public class AndroidRandomReadableFile implements DataInput, Closeable {
|
public class AndroidRandomReadableFile implements DataInput, Closeable {
|
||||||
private ParcelFileDescriptor pfd;
|
private final ParcelFileDescriptor pfd;
|
||||||
private FileInputStream fis;
|
private FileInputStream fis;
|
||||||
private DataInputStream dis;
|
private long pos = 0;
|
||||||
public long pos = 0;
|
|
||||||
|
private final AndroidCloseGuard guard = AndroidCloseGuard.getInstance();
|
||||||
|
|
||||||
public AndroidRandomReadableFile(@NonNull Context context, @NonNull Uri treeUri) throws FileNotFoundException {
|
public AndroidRandomReadableFile(@NonNull Context context, @NonNull Uri treeUri) throws FileNotFoundException {
|
||||||
try {
|
|
||||||
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);
|
//dis = new DataInputStream(fis);
|
||||||
} catch (FileNotFoundException e) {
|
guard.open("close");
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public final FileDescriptor getFD() {
|
public final FileDescriptor getFD() {
|
||||||
return pfd.getFileDescriptor();
|
return pfd.getFileDescriptor();
|
||||||
}
|
}
|
||||||
@@ -48,38 +53,73 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final FileInputStream getFileInputStream() {
|
public final FileInputStream getFileInputStream() {
|
||||||
if (fis == null || pos != getPos()) {
|
if (fis == null || this.pos != getPos()) {
|
||||||
|
this.pos = getPos();
|
||||||
fis = new FileInputStream(pfd.getFileDescriptor());
|
fis = new FileInputStream(pfd.getFileDescriptor());
|
||||||
}
|
}
|
||||||
return fis;
|
return fis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
public int read() throws IOException {
|
||||||
byte[] b = new byte[1];
|
byte[] b = new byte[1];
|
||||||
return (read(b, 0, 1) != -1) ? b[0] & 0xff : -1;
|
return (read(b, 0, 1) != -1) ? b[0] & 0xff : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(byte[] b) {
|
|
||||||
try {
|
/**
|
||||||
|
* 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 read(b, 0, b.length);
|
return read(b, 0, b.length);
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
try {
|
try {
|
||||||
return android.system.Os.read(pfd.getFileDescriptor(), b, off, len);
|
return android.system.Os.read(pfd.getFileDescriptor(), b, off, len);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return -1;
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void syncInputStream() {
|
private void syncInputStream() {
|
||||||
|
this.pos = getPos();
|
||||||
fis = new FileInputStream(pfd.getFileDescriptor());
|
fis = new FileInputStream(pfd.getFileDescriptor());
|
||||||
dis = new DataInputStream(fis);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -110,6 +150,46 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
@Override
|
||||||
public void readFully(byte[] b) throws IOException {
|
public void readFully(byte[] b) throws IOException {
|
||||||
try {
|
try {
|
||||||
@@ -121,6 +201,49 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
@Override
|
||||||
public void readFully(byte[] b, int off, int len) throws IOException {
|
public void readFully(byte[] b, int off, int len) throws IOException {
|
||||||
try {
|
try {
|
||||||
@@ -131,6 +254,27 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
@Override
|
||||||
public int skipBytes(int n) throws IOException {
|
public int skipBytes(int n) throws IOException {
|
||||||
try {
|
try {
|
||||||
@@ -142,6 +286,19 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
@Override
|
||||||
public boolean readBoolean() throws IOException {
|
public boolean readBoolean() throws IOException {
|
||||||
int ch = this.read();
|
int ch = this.read();
|
||||||
@@ -151,6 +308,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
|
@Override
|
||||||
public byte readByte() throws IOException {
|
public byte readByte() throws IOException {
|
||||||
int ch = this.read();
|
int ch = this.read();
|
||||||
@@ -160,6 +331,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
|
@Override
|
||||||
public int readUnsignedByte() throws IOException {
|
public int readUnsignedByte() throws IOException {
|
||||||
int ch = this.read();
|
int ch = this.read();
|
||||||
@@ -170,30 +359,131 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|||||||
|
|
||||||
private final byte[] readBuffer = new byte[8];
|
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
|
@Override
|
||||||
public short readShort() throws IOException {
|
public short readShort() throws IOException {
|
||||||
readFully(readBuffer, 0, 2);
|
readFully(readBuffer, 0, 2);
|
||||||
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get();
|
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
|
@Override
|
||||||
public int readUnsignedShort() throws IOException {
|
public int readUnsignedShort() throws IOException {
|
||||||
readFully(readBuffer, 0, 2);
|
readFully(readBuffer, 0, 2);
|
||||||
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get() & 0xffff;
|
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
|
@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 (char) ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asShortBuffer().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
|
@Override
|
||||||
public int readInt() throws IOException {
|
public int readInt() throws IOException {
|
||||||
readFully(readBuffer, 0, 4);
|
readFully(readBuffer, 0, 4);
|
||||||
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asIntBuffer().get();
|
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
|
@Override
|
||||||
public long readLong() throws IOException {
|
public long readLong() throws IOException {
|
||||||
readFully(readBuffer, 0, 8);
|
readFully(readBuffer, 0, 8);
|
||||||
@@ -207,11 +497,47 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|||||||
((readBuffer[7] & 255) << 0));
|
((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
|
@Override
|
||||||
public float readFloat() throws IOException {
|
public float readFloat() throws IOException {
|
||||||
return Float.intBitsToFloat(readInt());
|
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
|
@Override
|
||||||
public double readDouble() throws IOException {
|
public double readDouble() throws IOException {
|
||||||
return Double.longBitsToDouble(readLong());
|
return Double.longBitsToDouble(readLong());
|
||||||
@@ -219,6 +545,32 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|||||||
|
|
||||||
private char[] lineBuffer;
|
private char[] lineBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the general contract of the <code>readLine</code>
|
||||||
|
* method of <code>DataInput</code>.
|
||||||
|
* <p>
|
||||||
|
* Bytes
|
||||||
|
* for this operation are read from the contained
|
||||||
|
* input stream.
|
||||||
|
*
|
||||||
|
* @return the next line of text from this input stream.
|
||||||
|
* @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
|
||||||
|
* <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 = new DataInputStream(in);
|
||||||
|
* </pre></blockquote>
|
||||||
|
* with:
|
||||||
|
* <blockquote><pre>
|
||||||
|
* BufferedReader d
|
||||||
|
* = new BufferedReader(new InputStreamReader(in));
|
||||||
|
* </pre></blockquote>
|
||||||
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public String readLine() throws IOException {
|
public String readLine() throws IOException {
|
||||||
@@ -260,109 +612,67 @@ public class AndroidRandomReadableFile implements DataInput, Closeable {
|
|||||||
return String.copyValueOf(buf, 0, offset);
|
return String.copyValueOf(buf, 0, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
@Override
|
||||||
public String readUTF() throws IOException {
|
public String readUTF() throws IOException {
|
||||||
if (pos != getPos()) {
|
return DataInputStream.readUTF(this);
|
||||||
syncInputStream();
|
|
||||||
}
|
|
||||||
return dis.readUTF();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
try {
|
|
||||||
dis.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
guard.close();
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 28) {
|
||||||
|
//Reference.reachabilityFence(this);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
|
if (fis != null) {
|
||||||
fis.close();
|
fis.close();
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
if (pfd != null) {
|
||||||
pfd.close();
|
pfd.close();
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
try {
|
||||||
|
// Note that guard could be null if the constructor threw.
|
||||||
|
|
||||||
|
guard.warnIfOpen();
|
||||||
|
|
||||||
|
close();
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user