Android網絡通訊之WiFi Direct

使用Wi-Fi Direct技術可讓具有硬件支持的設備在沒有中間接入點的狀況下進行直接互聯。Android 4.0(API版本14)及之後的系統都提供了對Wi-Fi Direct的API支持。經過對這些API的使用,開發者能夠實現支持Wi-Fi Direct的設備間進行相互探測和鏈接,從而得到較之藍牙更遠距離的高速數據通訊效果。對於諸如多人遊戲、圖片共享等須要在用戶之間傳輸數據的應用而言,這一技術無疑是十分有價值的。html

關於Wi-Fi Direct的API函數的使用須要注意一下幾個要點:
·用於探測(discover)對等設備(peers)、向對等設備發起請求(request)以及創建鏈接(connect)的方法定義在類WifiP2pManager中。
·經過設置監聽器(Listener)能夠獲知WifiP2pManager中方法調用的成功與否。監聽器以參數的形式傳遞給被調用的方法。
·當發現新對等設備或連接丟失的時候,Wi-Fi Direct系統(framework)以意向(Intent)的方式根據檢測到的不一樣事件作出相應的通知。android

開發中,以上三點的配合使用至關廣泛。簡單舉個例子,定義一個監聽器WifiP2pManager.ActionListener並調用函數discoverPeers(),當相應事件發生的時候就會在ActionListener.onSuccess()和ActionListener.onFailure()兩個方法中獲得通知。當discoverPeers()方法檢測到了對等設備列表變化的時候,能夠收到由系統廣播(broadcast)發出一個WIFI_P2P_PEERS_CHANGED_ACTION意向。服務器

建立廣播接收器以處理Wi-Fi Direct意向的基本步驟以下:
一、建立一個繼承BroadcastReceiver類的新類。構造函數的參數分別傳遞WifiP2pManager,WifiP2pManager.Channel,以及在這個廣播接收器中須要註冊的活動(activity)。這是一種最多見的參數設置模式,它讓廣播接收器可以引發活動做出更新,同時又能在必要時使用Wi-Fi硬件和通訊信道。
二、在廣播接收器的onReceive()函數中,針對感興趣的特定意向能夠執行相應的動做(actions)。例如,當廣播接收器收到了意向WIFI_P2P_PEERS_CHANGED_ACTION,就能夠調用requestPeers()方法來列舉出當前探測到的對等設備。異步

建立Wi-Fi Direct應用:
完整的Wi-Fi Direct應用包含建立並註冊廣播接收器、檢測對等設備、鏈接對等設備以及在對等設備間傳輸數據幾個方面的功能。下面將詳細介紹如何實現。
準備工做:在使用Wi-Fi Direct API以前,首先要確保應用程序可以訪問硬件,而且設備支持Wi-Fi Direct協議。若是這些條件都知足,就能夠獲取一個WifiP2pManager實例,建立並註冊廣播接收器,最後就是使用Wi-Fi Direct API了。
在Android manifest文件中加入如下內容,容許使用Wi-Fi設備上的硬件並聲明應用程序正確支持了調用API所需的最低SDK版本。
<uses-sdk android:minSdkVersion="14" />  
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />  
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />  
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />  
<uses-permission android:name="android.permission.INTERNET" />  
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> socket

檢查Wi-Fi Direct支持並已開啓。推薦在廣播接收器收到WIFI_P2P_STATE_CHANGED_ACTION意向的時候進行檢測。檢測結果須要通告相應的活動並作出處理:
@Override  
public void onReceive(Context context, Intent intent) {  
    ... …
    String action = intent.getAction();  
    if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {  
        int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);  
        if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {  
            // Wifi Direct is enabled  
        } else {  
            // Wi-Fi Direct is not enabled  
        }  
    }  
    ...… 
async

在活動的onCreate()方法中獲取WifiP2pManager對象的一個實例,經過該對象的initialize()方法向Wi-Fi Direct系統註冊當前的應用程序。註冊成功後,會返回一個WifiP2pManager.Channel,經過它,應用程序就能和Wi-Fi Direct系統交互。WifiP2pManager和WifiP2pManager.Channel對象以及一個活動的引用最後都被做爲參數傳遞給自定義的廣播接收器。這樣,該活動就可以響應廣播接收器的通知並做出相應的更新。固然,這樣作也使程序具有了操縱設備Wi-Fi狀態的能力:
WifiP2pManager mManager;  
Channel mChannel;  
BroadcastReceiver mReceiver;  
......  
@Override  
protected void onCreate(Bundle savedInstanceState){  
    .…..  
    mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);  
    mChannel = mManager.initialize(this, getMainLooper(), null);  
    mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);  
    ... …. 
ide

建立一個意向過濾器(intent filter),其中添加的意向種類和廣播接收器中的保持一致:
IntentFilter mIntentFilter;  
......  
@Override  
protected void onCreate(Bundle savedInstanceState){  
    ...... 
    mIntentFilter = new IntentFilter();  
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);  
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);  
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);  
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);  
    ......  
}函數

在活動的onResume()方法中註冊廣播接收器,並在活動的onPause()方法中註銷它:
/* register the broadcast receiver with the intent values to be matched */  
@Override  
protected void onResume() {  
    super.onResume();  
    registerReceiver(mReceiver, mIntentFilter);  
}  
/* unregister the broadcast receiver */  
@Override  
protected void onPause() {  
    super.onPause();  
    unregisterReceiver(mReceiver);  

一旦成功獲取WifiP2pManager.Channel並建立了廣播接收器,應用程序就已經具有了使用Wi-Fi Direct相關函數和接收Wi-Fi Direct意向的能力。儘管放手使用WifiP2pManager爲你提供的方法,讓程序也擁有Wi-Fi Direct的特殊能力吧!oop

探測對等設備(Discovering peers)
調用discoverPeers()函數能夠探測到有效距離內的對等設備。它是一個異步函數,調用成功與否會在程序所建立WifiP2pManager.ActionListener監聽器的onSuccess()和onFailure()中給出通知。值得注意的是,onSuccess()方法只會對成功探測到對等設備這一事件作出通知,而並不會提供任何關於已發現的對等設備的具體信息:
manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {  
    @Override  
    public void onSuccess() {  
        .…..  
    }  
    @Override  
    public void onFailure(int reasonCode) {  
        .…..  
    }  
}); this

當成功檢測到對等設備存在的時候,系統會廣播WIFI_P2P_PEERS_CHANGED_ACTION意向。程序接收到該意向後,經過調用requestPeers()方法,就能得到已經探測到對等設備的清單。下面代碼將展現如何實現這一過程:
PeerListListener myPeerListListener;  
......  
if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {  
    /* request available peers from the wifi p2p manager. This is an asynchronous call and the calling activity is notified with a callback on PeerListListener.onPeersAvailable()  */
    if (manager != null) {  
        manager.requestPeers(channel, myPeerListListener);  
    }  
}  
requestPeers()方法一樣是一個異步函數,當它準備好一份對等設備列表的時候,就會通知監聽器WifiP2pManager.PeerListListener中定義的onPeersAvailable()方法。而onPeersAvailable()方法中所能獲取到的對等設備列表以WifiP2pDeviceList形式存儲,經過遍歷這個列表能夠選擇出但願鏈接的設備。

鏈接對等設備(Connecting to peers)
肯定了要鏈接的設備,還需調用connect()方法創建鏈接。該方法的其中一個參數是WifiP2pConfig對象,它提供了要鏈接設備的相關信息。鏈接的成功與否須要經過監聽器WifiP2pManager.ActionListener獲取通知。下面的代碼將示範如何創建設備鏈接:
//obtain a peer from the WifiP2pDeviceList  
WifiP2pDevice device;  
WifiP2pConfig config = new WifiP2pConfig();  
config.deviceAddress = device.deviceAddress;  
manager.connect(channel, config, new ActionListener() {  
    @Override  
    public void onSuccess() {  
        //success logic  
    }  
    @Override  
    public void onFailure(int reason) {  
        //failure logic  
    }  
}); 

傳輸數據(Transferring data)
鏈接一旦創建成功,數據傳輸也就是瓜熟蒂落的事情。如下是經過socket發送數據的基本步驟:
一、建立ServerSocket。它將被用於監聽特定端口,等待客戶端發起的鏈接請求。該操做須要在後臺線程中實現。
二、建立客戶端Socket。客戶端經過ServerSocket對應的IP和端口鏈接到服務設備。
三、客戶端向服務器發生數據。客戶socket成功鏈接到服務socket後,就能以字節流的形式向服務器發生數據了。
四、服務器socket經過accept()方法等待客戶端數據鏈接的到來。該方法在收到客戶端數據以前一直處於阻塞狀態。所以,須要在單獨的線程中調用它。數據鏈接一旦創建,服務設備就能接收到客戶端的數據。這時要作的就是施以相應的動做,例如將數據保存到文件,或者是直接顯示到用戶界面上,等等。

如下代碼修改自SDK自帶的示例Wi-Fi Direct Demo。它演示瞭如何創建一對客戶端-服務器鏈接,並由客戶端向服務器發送JPEG圖片。若需完整的演示工程,只需編譯並運行SDK示例Wi-Fi Direct Demo便可。
public static class FileServerAsyncTask extends AsyncTask {  
    private Context context;  
    private TextView statusText; 

    public FileServerAsyncTask(Context context, View statusText) {  
        this.context = context;  
        this.statusText = (TextView) statusText;  
    }  
    @Override  
    protected String doInBackground(Void... params) {  
        try {   
            /* Create a server socket and wait for client connections. This call blocks until a connection is accepted from a client */  
            ServerSocket serverSocket = new ServerSocket(8888);  
            Socket client = serverSocket.accept(); 
            /*If this code is reached, a client has connected and transferred data Save the input stream from the client as a JPEG file */  
            final File f = new File(Environment.getExternalStorageDirectory() + "/"  
        + context.getPackageName() + "/wifip2pshared-"
        + System.currentTimeMillis()  + ".jpg");  
            File dirs = new File(f.getParent());  
            if (!dirs.exists())  
                dirs.mkdirs();  
            f.createNewFile();  
            InputStream inputstream = client.getInputStream();  
            copyFile(inputstream, new FileOutputStream(f));  
            serverSocket.close();  
            return f.getAbsolutePath();  
        } catch (IOException e) {  
            Log.e(WiFiDirectActivity.TAG, e.getMessage());  
            return null;  
        }  
    }  
    // Start activity that can handle the JPEG image   
    @Override  
    protected void onPostExecute(String result) {  
        if (result != null) {  
            statusText.setText("File copied - " + result);  
            Intent intent = new Intent();  
            intent.setAction(android.content.Intent.ACTION_VIEW);  
            intent.setDataAndType(Uri.parse("file://" + result), "image/*");  
            context.startActivity(intent);  
        }  
    }  

On the client, connect to the server socket with a client socket and transfer data. This example transfers a JPEG file on the client device's file system:

Context context = this.getApplicationContext();   
String host;  
int port;  
int len;  
Socket socket = new Socket();  
byte buf[]  = new byte[1024];  
...  
try {  
    /* Create a client socket with the host,  port, and timeout information.*/  
    socket.bind(null);  
    socket.connect((new InetSocketAddress(host, port)), 500);  
    /* Create a byte stream from a JPEG file and pipe it to the output stream  of the socket. This data will be retrieved by the server device. */  
    OutputStream outputStream = socket.getOutputStream();  
    ContentResolver cr = context.getContentResolver();  
    InputStream inputStream = null;  
    inputStream = cr.openInputStream(Uri.parse("path/to/picture.jpg"));  
    while ((len = inputStream.read(buf)) != -1) {  
        outputStream.write(buf, 0, len);  
    }  
    outputStream.close();  
    inputStream.close();  
} catch (FileNotFoundException e) {  
    //catch logic  
} catch (IOException e) {  
    //catch logic   

/* Clean up any open sockets when done transferring or if an exception occurred. */  
finally {  
    if (socket != null) {  
        if (socket.isConnected()) {  
            try {  
                socket.close();  
            } catch (IOException e) {  
                //catch logic  
            }  
        }  
    }  

 

官方參考連接:http://developer.android.com/reference/android/net/wifi/p2p/WifiP2pManager.html

參考連接:http://blog.csdn.net/yichigo/article/details/8472570

代碼下載連接:http://download.csdn.net/detail/klcf0220/5894381
http://www.apkbus.com/android-137269-1-1.html

 

轉載:http://klcf0220.cnblogs.com/

相關文章
相關標籤/搜索