Android NIO(Noblocking I/O非阻塞I/O)小結

來源:互聯網
上載者:User

標籤:

參考:http://www.cnblogs.com/cpcpc/archive/2011/06/27/2123009.html

 

對於Android的網路通訊效能的提高,我們可以使用Java上高效能的NIO (New I/O) 技術進行處理,NIO是從JDK 1.4開始引入的,NIO的N我們可以理解為Noblocking即非阻塞的意思,相對應傳統的I/O,比如Socket的accpet()、read()這些方法而言都是阻塞的。

  NIO主要使用了Channel和Selector來實現,Java的Selector類似Winsock的Select模式,是一種基於事件驅動的,整個處理方法使用了輪訓的狀態機器,如果你過去開發過Symbian應用的話這種方式有點像使用中的物件,好處就是單線程更節省系統開銷,NIO的好處可以很好的處理並發,對於Android網遊開發來說比較關鍵,對於多點Socket串連而言使用NIO可以大大減少線程使用,降低了線程死結的機率,畢竟手機遊戲有UI線程,音樂線程,網路線程,管理的難度可想而知,同時I/O這種低速裝置將影響遊戲的體驗。

  NIO作為一種中高負載的I/O模型,相對於傳統的BIO (Blocking I/O)來說有了很大的提高,處理並發不用太多的線程,省去了建立銷毀的時間,如果線程過多調度是問題,同時很多線程可能處於空閑狀態,大大浪費了CPU時間,同時過多的線程可能是效能大幅下降,一般的解決方案中可能使用線程池來管理調度但這種方法治標不治本。使用NIO可以使並發的效率大大提高。當然NIO和JDK 7中的AIO還存在一些區別,AIO作為一種更新的當然這是對於Java而言,如果你開發過Winsock伺服器,那麼IOCP這樣的I/O完成連接埠可以解決更進階的負載,當然了今天主要給大家講解下為什麼使用NIO在Android中有哪些用處。

   NIO我們分為幾個類型分別描述,作為Java的特性之一,我們需要瞭解一些新的概念,比如ByteBuffer類,Channel,SocketChannel,ServerSocketChannel,Selector和SelectionKey。有關具體的使用,可以在Android SDK文檔中看下java.nio和java.nio.channels兩個包瞭解。

 

Android NIO主要分為三大類,ByteBuffer、FileChannel和SocketChannel。NIO和傳統的I/O比較大的區別在於傳輸方式非阻塞,一種基於事件驅動的模式,將會使方法執行完後立即返回,傳統I/O主要使用了流Stream的方式,而在New I/O中,使用了位元組緩衝ByteBuffer來承載資料。

   ByteBuffer位於java.nio包中,目前提供了Java基本類型中除Boolean外其他類型的緩衝類型,比如ByteBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer和ShortBuffer  。同時還提供了一種更特殊的映射位元組緩衝類型MappedByteBuffer。在傳統IO的輸入輸出資料流中,InputStream中只提供了位元組型或位元組數組的訪問對應NIO就是ByteBuffer,但是處理傳統的DataInputStream的int等類型,就是IntBuffer,但是緩衝類型並沒有提供UTF這樣的類型處理,所以我們仍然需要使用ByteBuffer處理字串,但是NIO提供了一個封裝的類在java.nio.charset包中,通過字元的編碼CharsetEncoder和解碼CharsetDecoder類來處理字串,同時這些類可以方便轉換編碼比如GBK或UTF等等。

  一、ByteBuffer類

  1) 執行個體化

  直接使用ByteBuffer類的靜態方法static ByteBuffer allocate(int capacity) 或 static ByteBuffer allocateDirect(int capacity)  這兩個方法來分配記憶體空間,兩種方法的區別主要是後者更適用於繁複分配的位元組數組。而 put(ByteBuffer src) 可以從另一個ByteBuffer中構造,也可以通過wrap方法從byte[]中構造,具體參考下面的類型轉化內容。

  2) 類型轉化

   ByteBuffer可以很好的和位元組數組byte[]轉換類型,通過執行ByteBuffer類的final byte[]  array() 方法就可以將ByteBuffer轉為byte[]。從byte[]來構造ByteBuffer可以使用wrap方法,目前Android或者說Java提供了兩種重寫方法,比如為static ByteBuffer  wrap(byte[] array)  和 static ByteBuffer  wrap(byte[] array, int start, int len)  ,第二個重載方法中第二個參數為從array這個位元組數組的起初位置,第三個參數為array這個位元組數組的長度。

  3) 往ByteBuffer中添加元素

  目前ByteBuffer提供了多種put重寫類型來添加,比如put(byte b) 、putChar(char value) 、putFloat(float value) 等等,需要注意的是,按照Java的類型長度,一個byte佔1位元組,一個char類型是2位元組,一個float或int是4位元組,一個long則為8位元組,和傳統的C++有些區別。所以內部的相關位置也會發生變化,同時每種方法還提供了定位的方法比如ByteBuffer  put(int index, byte b) 

  4) 從ByteBuffer中擷取元素

  同上面的添加想法,各種put被換成了get,比如byte  get()  、float  getFloat()  ,當然了還提供了一種定位的方式,比如double  getDouble(int index) 

  5) ByteBuffer中位元組順序

  對於Java來說預設使用了BIG_ENDIAN方式儲存,和C正好相反的,通過

  final ByteOrder  order() 返回當前的位元組順序。

  final ByteBuffer  order(ByteOrder byteOrder)  設定位元組順序,ByteOrder類的值有兩個定義,比如LITTLE_ENDIAN、BIG_ENDIAN,如果使用當前平台則為ByteOrder.nativeOrder()在Android中則為 BIG_ENDIAN,當然如果設定為order(null) 則使用LITTLE_ENDIAN。

  二、FileChannel類

   在NIO中除了Socket外,還提供了File裝置的通道類,FileChannel位於java.nio.channels.FileChannel包中,在Android SDK文檔中我們可以方便的找到,對於檔案複製我們可以使用ByteBuffer方式作為緩衝,比如

  String infile = "/sdcard/cwj.dat";
  String outfile = "/sdcard/android123-test.dat";

    FileInputStream fin = new FileInputStream( infile );
    FileOutputStream fout = new FileOutputStream( outfile );

    FileChannel fcin = fin.getChannel();
    FileChannel fcout = fout.getChannel();

    ByteBuffer buffer = ByteBuffer.allocate( 1024 ); //分配1KB作為緩衝區

    while (true) {
    buffer.clear(); //每次使用必須置空緩衝區

      int r = fcin.read( buffer );

      if (r==-1) {
        break;
      }

   buffer.flip(); //寫入前使用flip這個方法

      fcout.write( buffer );
    }

   flip和clear這兩個方法是java.nio.Buffer包中,ByteBuffer的父類是從Buffer類繼承而來的,提醒大家看Android SDK文檔時注意Inherited Methods,而JDK的文檔就比較直接了,同時複製檔案使用FileChannel的transferTo(long position, long count, WritableByteChannel target) 這個方法可以快速的複製檔案,無需自己管理ByteBuffer緩衝區。

 

http://www.jb51.net/article/64733.htm

 

一起來看看Android NIO有關Socket操作提供的類吧:

 

  一、ServerSocketChannel 伺服器通訊端通道在Android SDK中尋找package名為  java.nio.channels.ServerSocketChannel

 

   在Java的NIO中,ServerSocketChannel對應的是傳統IO中的ServerSocket,通過ServerSocketChannel類的socket() 方法可以獲得一個傳統的ServerSocket對象,同時從ServerSocket對象的getChannel() 方法,可以獲得一個ServerSocketChannel()對象,這點說明NIO的ServerSocketChannel和傳統IO的ServerSocket是有關聯的,執行個體化ServerSocketChannel 只需要直接調用ServerSocketChannel 類的靜態方法open()即可。

 

  二、 SocketChannel 通訊端通道 java.nio.channels.SocketChannel   

 

  在Java的New I/O中,處理Socket類對應的東西,我們可以看做是SocketChannel,通訊端通道關聯了一個Socket類,這一點使用SocketChannel類的socket() 方法可以返回一個傳統IO的Socket類。SocketChannel()對象在Server中一般通過Socket類的getChannel()方法獲得。

 

 三、SelectionKey 選擇鍵 java.nio.channels.SelectionKey

 

  在NIO中SelectionKey和Selector是最關鍵的地方,SelectionKey類中描述了NIO中比較重要的事件,比如OP_ACCEPT(用於伺服器端)、OP_CONNECT(用於用戶端)、OP_READ和OP_WRITE。

 

 四、Selector 選取器 java.nio.channels.Selector

 

  在NIO中註冊各種事件的方法主要使用Selector來實現的,構造一個Selector對象,使用Selector類的靜態方法open()來執行個體化。

 

  對於Android平台上我們實現一個非阻塞的伺服器,過程如下:

 

   1. 通過Selector類的open()靜態方法執行個體化一個Selector對象。

 

   2. 通過ServerSocketChannel類的open()靜態方法執行個體化一個ServerSocketChannel對象。

 

   3. 顯示的調用ServerSocketChannel對象的configureBlocking(false);方法,設定為非阻塞模式,Android123提示網友這一步十分重要。

 

   4. 使用ServerSocketChannel對象的socket()方法返回一個ServerSocket對象,使用ServerSocket對象的bind()方法綁定一個IP地址和連接埠號碼

 

   5. 調用ServerSocketChannel對象的register方法註冊感興趣的網路事件,很多開發人員可能發現Android SDK文檔中沒有看到register方法,這裡Android開發網給大家一個ServerSocketChannel類的繼承關係  

 

java.lang.Object

   ? java.nio.channels.spi.AbstractInterruptibleChannel

     ? java.nio.channels.SelectableChannel

       ? java.nio.channels.spi.AbstractSelectableChannel

         ? java.nio.channels.ServerSocketChannel

 

 

   這裡我們使用的register方法其實來自ServerSocketChannel的父類java.nio.channels.SelectableChannel,該方法原型為 final SelectionKey  register(Selector selector, int operations)  ,參數為我們執行第1步時的selector對象,參數二為需要註冊的事件,作為伺服器,我們當然是接受用戶端發來的請求,所以這裡使用SelectionKey.OP_ACCEPT了。

 

  6. 通過Selector對象的select() 方法判斷是否有我們感興趣的事件發生,這裡就是OP_ACCEPT事件了。我們通過一個死迴圈擷取Selector對象執行select()方法的值,SDK中的原始描述為the number of channels that are ready for operation.,就是到底有多少個通道返回。

 

  7. 如果 Selector對象的select()方法返回的結果數大於0,則通過selector對象的selectedKeys()方法擷取一個SelectionKey類型的Set集合,我們使用Java的迭代器Iterator類來遍曆這個Set集合,注意判斷SelectionKey對象,

 

  8. 為了表示我們處理了SelectionKey對象,需要先移除這個SelectionKey對象從Set集合中。這句很關鍵Android 123提醒網友注意這個地方。

 

  9. 接下來判斷SelectionKey對象的事件,因為我們註冊的感興趣的是SelectionKey.OP_ACCEPT事件,我們使用SelectionKey對象的isAcceptable()方法判斷,如果是我們建立一個臨時SocketChannel對象類似上面的方法繼續處理,不過這時這個SocketChannel對象主要處理讀寫操作,我們註冊SelectionKey.OP_READ和SelectionKey.OP_WRITE分配ByteBuffer緩衝區,進行網路資料轉送。

 ========對於開發安卓應用,還沒有具體情境用NIO。===========================================

但是,在官方給出的OpenGL教程中卻用到了,所以,記錄一下:

/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.edaixi.opengl;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.FloatBuffer;import java.nio.ShortBuffer;import javax.microedition.khronos.opengles.GL10;/** * A two-dimensional square for use as a drawn object in OpenGL ES 1.0/1.1. */public class Square {    private final FloatBuffer vertexBuffer;    private final ShortBuffer drawListBuffer;    // number of coordinates per vertex in this array    static final int COORDS_PER_VERTEX = 3;    static float squareCoords[] = {            -0.5f,  0.5f, 0.0f,   // top left            -0.5f, -0.5f, 0.0f,   // bottom left             0.5f, -0.5f, 0.0f,   // bottom right             0.5f,  0.5f, 0.0f }; // top right    private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices    float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f };    /**     * Sets up the drawing object data for use in an OpenGL ES context.     */    public Square() {        // initialize vertex byte buffer for shape coordinates        ByteBuffer bb = ByteBuffer.allocateDirect(        // (# of coordinate values * 4 bytes per float)                squareCoords.length * 4);        bb.order(ByteOrder.nativeOrder());        vertexBuffer = bb.asFloatBuffer();        vertexBuffer.put(squareCoords);        vertexBuffer.position(0);        // initialize byte buffer for the draw list        ByteBuffer dlb = ByteBuffer.allocateDirect(                // (# of coordinate values * 2 bytes per short)                drawOrder.length * 2);        dlb.order(ByteOrder.nativeOrder());        drawListBuffer = dlb.asShortBuffer();        drawListBuffer.put(drawOrder);        drawListBuffer.position(0);    }    /**     * Encapsulates the OpenGL ES instructions for drawing this shape.     *     * @param gl - The OpenGL ES context in which to draw this shape.     */    public void draw(GL10 gl) {        // Since this shape uses vertex arrays, enable them        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);        // draw the shape        gl.glColor4f(       // set color                color[0], color[1],                color[2], color[3]);        gl.glVertexPointer( // point to vertex data:                COORDS_PER_VERTEX,                GL10.GL_FLOAT, 0, vertexBuffer);        gl.glDrawElements(  // draw shape:                GL10.GL_TRIANGLES,                drawOrder.length, GL10.GL_UNSIGNED_SHORT,                drawListBuffer);        // Disable vertex array drawing to avoid        // conflicts with shapes that don‘t use it        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);    }}

  

 

 

Android NIO(Noblocking I/O非阻塞I/O)小結

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.