Android uses Okio to simplify I/O operations
The Okio library is developed by square and complements java. io and java. nio to facilitate and quickly access, store, and process your data. The underlying layer of OkHttp also uses this library as support. In development, using this library can greatly help you.
Currently, the latest version of Okio is 1.6.0, and the reference of gradle is as follows:
compile 'com.squareup.okio:okio:1.6.0'
Okio has two key interfaces,SinkAndSourceBoth interfaces inherit the Closeable interface, while Sink can be viewed as OutputStream, and Source can be viewed as InputStream. Both interfaces support read/write timeout settings. The structure is as follows:
Each of them has a sub-class interface that supports buffer, BufferedSink and BufferedSource, while BufferedSink has an implementation class RealBufferedSink, and BufferedSource has an implementation class RealBufferedSource. In addition, the Sink and Source respectively have an implementation class GzipSink and GzipSource that supports gzip compression, an abstract class ForwardingSink and ForwardingSource with delegation functions, and an implementation class is InflaterSource and DeflaterSink, these two classes are mainly used for compression and serve GzipSink and GzipSource. The overall structure is as follows:
BufferedSink defines a series of writing methods to the cache, such as writing byte arrays, writeUtf8 writing strings, and writeByte, writeString, writeShort, writeInt, writeLong, writeDecimalLong, and so on;
The methods defined by BufferedSource are very similar to those defined by BufferedSink, except that they write and read, which are basically exactly the same, such as readUtf8, readByte, readString, readShort, and readInt. If you are interested in the methods in these two interfaces, you can check the source code.
The implementation classes RealBufferedSink and RealBufferedSource of the two interfaces supporting the Buffer are implemented by packaging a Sink + Buffer or Source + Buffer. As shown in
Taking RealBufferedSink as an example, the actual calling of a series of write methods is a direct operation on the member Variable buffer. After the buffer is successfully written, at last, we will call a method to write the content in the buffer to the sink. Let's take a look at it.
public BufferedSink writeLong(long v) throws IOException { if(this.closed) { throw new IllegalStateException("closed"); } else { this.buffer.writeLong(v); return this.emitCompleteSegments(); } }
We can see that the closed member variable is marked as closed. If it is already closed, an exception is thrown. Otherwise, the content is written to the buffer. After the write is complete, an emitCompleteSegments method is called, what has been done in this method? Yes, it is to write the content in the buffer to the sink member variable and then return itself.
public BufferedSink emitCompleteSegments() throws IOException { if(this.closed) { throw new IllegalStateException("closed"); } else { long byteCount = this.buffer.completeSegmentByteCount(); if(byteCount > 0L) { this.sink.write(this.buffer, byteCount); } return this; } }
All the internal methods of these two implementation classes are similar. We will not expand them one by one here.
All these are backed by a Buffer class that supports Buffer. Buffer is the implementation class of BufferedSink and BufferedSource. Therefore, it can be used to read or write data, inside it, a Segment and SegmentPool are used to maintain a linked list. The recycling mechanism is the same as the Message exploitation mechanism in Android.
final class SegmentPool { static final long MAX_SIZE = 65536L; static Segment next; static long byteCount; private SegmentPool() { } static Segment take() { Class var0 = SegmentPool.class; synchronized(SegmentPool.class) { if(next != null) { Segment result = next; next = result.next; result.next = null; byteCount -= 2048L; return result; } } return new Segment(); } static void recycle(Segment segment) { if(segment.next == null && segment.prev == null) { if(!segment.shared) { Class var1 = SegmentPool.class; synchronized(SegmentPool.class) { if(byteCount + 2048L <= 65536L) { byteCount += 2048L; segment.next = next; segment.pos = segment.limit = 0; next = segment; } } } } else { throw new IllegalArgumentException(); } }}
An internal member variable next points to the next element of the linked list. The take method first checks whether the pool is available. If it exists, it returns. If it does not exist, a new one is returned, the recycle is to re-Throw the unused Segment to the pool. To achieve the role of a Segment pool.
Okio is exposed to the external class. Okio has a large number of internal static methods, including obtaining BufferedSource through a Source and obtaining a BufferedSink through a Sink. This process is very simple. We can call the buffer method of Okio to return what we need, as shown below:
Okio.buffer(sink)Okio.buffer(source)
But the above two methods need to pass a Sink or Source, so how can this Sink and Source be obtained. In fact, the method is also in the Okio class. We can call the sink method to obtain a Sink, call the source method to obtain a Source, and the data source or purpose can be a File, an input or output stream, a Socket link, and so on. As follows:
Okio.sink(new File("***"));Okio.sink(new FileOutputStream(new File("***")));Okio.sink(new Socket("***",8888));Okio.source(new File("***"));Okio.source(new FileInputStream(new File("***")));Okio.source(new Socket("****",8888));
In this way, you may not be addicted, so let's connect and apply it. Now we can read a local file and then write content to another file.
public static void main(String[] args) { Source source = null; BufferedSource bufferedSource = null; try { File file = new File("resources/test.txt"); source = Okio.source(file); bufferedSource = Okio.buffer(source); String content = bufferedSource.readUtf8(); System.out.println(content); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { closeQuietly(bufferedSource); } Sink sink = null; BufferedSink bufferedSink = null; try { File dest = new File("resources/dest.txt"); sink = Okio.sink(dest); bufferedSink = Okio.buffer(sink); bufferedSink.writeUtf8("11111111111"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { closeQuietly(bufferedSink); }}public static void closeQuietly(Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (RuntimeException rethrown) { throw rethrown; } catch (Exception ignored) { } }}
Sometimes we need to use the Gzip function in network requests, so we can simply use the gzip function.
public static void main(String[] args) { Sink sink = null; BufferedSink bufferedSink = null; GzipSink gzipSink=null; try { File dest = new File("resources/gzip.txt"); sink = Okio.sink(dest); gzipSink=new GzipSink(sink); bufferedSink = Okio.buffer(gzipSink); bufferedSink.writeUtf8("android vs ios"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { closeQuietly(bufferedSink); } Source source = null; BufferedSource bufferedSource = null; GzipSource gzipSource=null; try { File file = new File("resources/gzip.txt"); source = Okio.source(file); gzipSource=new GzipSource(source); bufferedSource = Okio.buffer(gzipSource); String content = bufferedSource.readUtf8(); System.out.println(content); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { closeQuietly(bufferedSource); }}public static void closeQuietly(Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (RuntimeException rethrown) { throw rethrown; } catch (Exception ignored) { } }}
The correct method is to check whether the written file is garbled and whether the read is the original string.
By comparing the original gzip compression and decompression methods, you will find that it is much simpler.
Public class GzipUtil {/*** GZIP compression ** @ param data * @ return */public static byte [] gzip (byte [] data) throws Exception {if (data = null | data. length = 0) {return null;} ByteArrayOutputStream out = new ByteArrayOutputStream (); GZIPOutputStream zos; BufferedInputStream bis = new BufferedInputStream (new ByteArrayInputStream (data )); byte [] buf = new byte [512]; int len; try {zos = new GZIPOut PutStream (out); while (len = bis. read (buf ))! =-1) {zos. write (buf, 0, len); zos. flush ();} bis. close (); zos. close (); return out. toByteArray ();} finally {if (out! = Null) {try {out. close ();} catch (Exception e2) {}}}/*** Gzip decompression * @ param B * @ return */public static byte [] unGzip (byte [] B) {if (B = null | B. length = 0) {return null;} ByteArrayOutputStream out = new ByteArrayOutputStream (); bytes in = new ByteArrayInputStream (B); try {GZIPInputStream gunzip = new GZIPInputStream (in ); byte [] buffer = new byte [256]; int n; while (N = gunzip. read (buffer)> = 0) {out. write (buffer, 0, n);} return out. toByteArray ();} catch (IOException e) {Log. e (WDCore. getInstance (). getConfiguration (). getLogTag (), "uncompress error", e) ;}finally {try {if (out! = Null) {out. close () ;}if (in! = Null) {in. close () ;}} catch (Exception e2) {}} return null ;}}
In addition, there is a ByteString class, which can be used for various changes. It transfers byte to a String, and this String can be a utf8 value or a base64 value, it can also be the md5 value or the sha256 value. In short, it is a variety of changes and finally gets the value you want.
In short, using the Okio library in a proper place will certainly bring you a lot of convenience for development. Why not!