首页 > 代码库 > Android Wi-Fi Peer-to-Peer(Android的Wi-Fi P2P对等网络)

Android Wi-Fi Peer-to-Peer(Android的Wi-Fi P2P对等网络)


Wi-Fi peer-to-peer(P2P,对等网络),它同意具备对应硬件的Android 4.0(API level 14)或者更高版本号的设备能够直接通过wifi而不须要其他中间中转节点就能直接通信(Android的Wi-Fi P2P框架符合Wi-Fi联盟的Wi-Fi Direct?直连认证标志)。使用这些API。你能够搜索并连接其他相同支持Wi-Fi P2P的设备,然后再通过一个快速的连接进行互相通信,而且这个连接的有效距离要比蓝牙连接的有效距离要长的多。

这对于须要在用户之间共享数据的应用程序很实用,比如多玩家游戏或者照片分享之类应用。


Android的Wi-Fi P2P API主要包括下面几个部分:

* 使用WifiP2pManager类定义的方法搜索、请求并连接到其他对等设备。

* 监听WiFiP2pManager的方法调用是否成功或者失败的Listener(监听器)。

当调用WiFiP2pManager的方法时,每个方法都能够接收一个指定的监听器作为參数。

* 由Wi-Fi P2P框架识别并发送的指定事件类型的Intent,比如撤销一个连接或者新发现了一个对等设备。


通常你须要一起使用这三个主要部分的API。

比如。你能够在调用discoverPeers()方法的时候提供WifiP2pManager.ActionListener监听器对象。这样你就能得到ActionListener.onSuccess()和ActionListener.onFailure()方法的通知。假设discoverPeers()方法发现对等设备列表有更新,相同也会发送WIFI_P2P_PEERS_CHANGE_ACTION类型的Intent广播。


1、API概览


WifiP2pManager类提供了与设备上的Wi-Fi硬件进行交互的方法。比如搜索和连接对等设备。下面是可用的操作:


表一:Wi-Fi P2P方法

方法描写叙述
initialize()在Wi-Fi框架上注冊应用程序。这种方法必须在调用其他Wi-Fi P2P方法之前调用。
connect()使用指定配置与一台设备開始一个P2P对等连接。

cancelConnect()取消正在进行中的P2P对等网络组的交互。
requestConnectInfo()请求一台设备连接信息。

createGroup()创建一个对等网络组,并将当前设备作为群组的拥有者。

removeGroup()移除当前的P2P对等网络组。
requestGroupInof()请求P2P对等网络组信息。
discoverPeers() 開始搜索对等网络(设备)。
requestPeers()请求当前已发现的对等网络(设备)。


WifiP2pManager的方法同意你传递一个listener监听器,这样Wi-Fi P2P框架才干通知你的activity所调用方法的状态。可使用的listener监听器接口和对应调用这些监听器的WifiP2pManager方法例如以下表:


表二:Wi-Fi P2P监听器

监听器接口可被使用的操作
WifiP2pManager.ActionListenerconnect(),cancelConnect(),createGroup(),removeGroup(),discoverPeers()
WifiP2pManager.ChannelListener initialize()
WifiP2pManager.ConnectionInfoListenerrequestConnectInfo()
WifiP2pManager.GroupInfoListenerrequestGroupInfo()
WifiP2pManager.PeerListListenerrequestPeers()


Wi-Fi P2P的API定义某些Wi-Fi P2P事件发生时要广播的Intent。比如发现了一个新的对等网络(设备)或者一台设备的Wi-Fi状态改变的事件。你能够在你的应用程序中创建并注冊一个广播接受者来处理以下这些Intent:


表三:Wi-Fi P2P Intents

Intent描写叙述
WIFI_P2P_CONNECTION_CHANGED_ACTION当设备的Wi-Fi连接状态改变时会发送这个广播。
WIFI_P2P_PEERS_CHANGED_ACTION当你调用discoverPeers()方法就会收到这个广播。假设你在你的应用程序中处理这个intent,你相同须要调用requestPeers()来获取一个最新的对等网络(设备)列表。
WIFI_P2P_STATE_CHANGED_ACTION当设备的Wi-Fi P2P启用或者禁用时会发送这个广播。当设备的Wi-Fi P2P启用或者禁用时会发送这个广播。
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION当设备的细节被改动时会发送这个广播,比如设备的名字。


2、为Wi-Fi P2P的Intent创建一个广播接收器


广播接收器(broadcase receiver)同意你接收Android系统广播出来的Intent,这样你的应用程序才干响应你所感兴趣的时间。创建一个广播接收器来处理Wi-Fi P2P intent的基本过程例如以下:


2.1 创建一个继承BroadcaseReceiver的类。

在这个类的构造方法中。你最好将WifiP2pManager、WifiP2pManager.Channel。还有注冊了这个广播接收器的activity作为參数传递进来。这样才干让广播接收器向activity发送更新的同一时候,还能在须要的时候訪问Wi-Fi硬件和通信的信道。

2.2 在广播接收器中。检查onReceive()方法中你所感兴趣的intent。基于接收到的intent完毕一些必要的操作。比如,假设广播接收器接收到一个WIFI_P2P_PEERS_CHANGE_ACTION类型的Intent,你能够调用requestPeers()方法来获取当前已发现的对等网络(设备)。


以下的代码展示怎样创建一个典型的广播接收器。

这个广播接收器携带一个WifiP2pManager对象和一个activity最为參数,并在广播接收器接到一个intent的时候。使用这两个对象来适当完毕所须要的操作:

/**
 * 一个接收Wi-Fi P2P重要事件的广播接收器
 */
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {

    private WifiP2pManager mManager;
    private Channel mChannel;
    private MyWiFiActivity mActivity;

    public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel,
            MyWifiActivity activity) {
        super();
        this.mManager = manager;
        this.mChannel = channel;
        this.mActivity = activity;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
            // 检查Wi-fi是否已经启用。并通知适当的activity
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
            // 调用WifiP2pManager.requestPeers()来获取当前的对等网络(设备)列表
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
            // 响应新的连接或者断开连接
        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            // 响应本台设备wifi状态的改变
        }
    }
}


3、创建一个Wi-Fi P2P应用程序


创建一个Wi-Fi P2P应用程序涉及到为应用程序创建并注冊一个广播接收器。搜索对等网络(设备),连接一台对等设备,还有向一台对等设备数据传输。

以下的章节描写叙述怎样完毕这些操作。


3.1 初始设置


在使用Wi-Fi P2P的API之前,你必须确认你的应用程序可以訪问相应的硬件而且设备支持Wi-Fi P2P协议。假设支持Wi-Fi P2P,你就行获取一个WifiP2pManager实例,创建并注冊你的广播接收器,然后開始使用Wi-Fi P2P的API。


3.1.1 在Android的manifest文件里请求使用设备上Wi-Fi硬件的权限。并为你的应用程序声明正确的最小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" />


3.1.2 检查Wi-Fi P2P可以支持而且已经启用。

进行这项检查的一个较好的地方是在你的广播接收器中,当广播接收器接收到WIFI_P2P_STATE_CHANGED_ACTION的时候。从而通知你的activity关于Wi-Fi P2P的状态并做出对应的应对。

@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 P2P已经启用
        } else {
            // Wi-Fi P2P没有启用
        }
    }
    ...
}


3.1.3 在你的activity的onCreate()方法中获取一个WifiP2pManager实例,并通过调用initialize()方法将你的应用程序注冊到Wi-Fi P2P框架中。这种方法会返回一个WifiP2pManager.Channel对象,这个对象被用于连接你的应用程序和Wi-Fi P2P框架。你相同须要使用这个WifiP2pManager和WifiP2pManager.Channel对象,还有你的activity引用创建一个你的广播接收器实例。这样才干同意你的广播接收器通知你的activity所感兴趣的事件并进行更新。

它相同能让你在须要的时候操作设备的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);
    ...
}


3.1.4 创建一个intent过滤器。并在你的广播接收器中加入同样的Intent以进行检查:

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);
    ...
}


3.1.5 在activity的onResume()方法中注冊你的广播接收器,并在onPause()方法中注销:

/* 使用须要匹配的intent值注冊广播接收器 */
@Override
protected void onResume() {
    super.onResume();
    registerReceiver(mReceiver, mIntentFilter);
}
/* 注销广播接收器 */
@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(mReceiver);
}


当你已经获取到WifiP2pManager.Channel对象而且设置好广播接收器,你的应用程序就能够调用Wi-Fi P2P的方法和接收Wi-Fi P2P的intent。

你如今能够实现你的应用程序而且通过调用WifiP2pManager中的方法来使用Wi-Fi P2P的功能。以下的章节将阐述怎样运行常见的操作,比方搜索和连接对等设备。


3.2 搜索对等网络(设备)


搜索同意被连接的对等设备,能够调用discoverPeers()方法来检測附近范围中同意被发现的对等网络(设备)。

这种方法的运行过程是异步的,假设你有创建并传递一个WifiP2pManager.ActionListener对象作为參数。你能够在这个对象中的onSuccess()和onFailure()方法中接收到操作运行的结果。当中onSuccess()方法仅仅是通知你搜索程序已经成功运行。可是不会提供不论什么实际搜索到的对等设备信息;

mManager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
    @Override
    public void onSuccess() {
        ...
    }

    @Override
    public void onFailure(int reasonCode) {
        ...
    }
});


假设搜索程序成功运行而且检測到对等网络和设备,系统将会发送WIFI_P2P_PEERS_CHANGED_ACTION类型的intent广播,你能够在广播接收器中监听这个广播以获取对等网络设备列表。当你的应用程序接收到WIFI_P2P_PEERS_CHANGED_ACTION类型的intent,你就能够使用requestPeers()方法来请求已被发现的对等网络设备列表。

以下的代码展示了怎样设置这些步骤:

PeerListListener myPeerListListener;
...
if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {

    // 从wifi p2p manager请求可使用的对等设备。这是一个异步调用的方法。
    // 调用的activity将会在PeerListListener.onPeersAvailable()的回调方法中得到列表结果。

if (mManager != null) { mManager.requestPeers(mChannel, myPeerListListener); } }


这个requestPeers()方法相同是异步运行的方法。当对等设备列表可用时,它会在WifiP2pManager.PeerListListener接口定义的onPeersAvailable()方法中将结果返回给你的activity。onPeersAvailable()方法会提供一个WifiP2pDeviceList对象,你能够通过这个对象找到你想要连接的对等设备。


3.3 连接对等设备


当你从获取到的可连接对等设备列表中找出你想要连接的对等设备之后。你就能够调用connect()方法链接这个设备。

这种方法的调用须要一个WifiP2pConfig对象,这个对象包括了想要连接的设备信息。

你能够通过WifiP2pManager.ActionListener得到连接成功或者失败的结果。以下的代码展示了怎样连接目标设备:

//从WifiP2pDeviceList中获取一个对等设备
WifiP2pDevice device;
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
mManager.connect(mChannel, config, new ActionListener() {

    @Override
    public void onSuccess() {
        //连接成功的逻辑处理操作
    }

    @Override
    public void onFailure(int reason) {
        //连接失败的逻辑处理操作
    }
});


3.4 数据传输


一旦连接建立成功,你就能够在设备之间通过socket数据传输。

传入数据的基本过程例如以下:


3.4.1 创建一个SeverSocket。这个socket会在一个port上等待client的连接,而且会一直堵塞直到连接请求出现。因此这种方法须要在后台线程运行。

3.4.2 创建一个client的Socket。

这个client使用服务端ServerSocket的IP地址和port来连接服务端设备。

3.4.3 从client发送数据到服务端。当client的socket成功连接上服务端的socket之后,你就能够通过字节流将数据从client发送到服务端。

3.4.4 服务端的ServerSocket等待着client的连接(通过accept()方法)。

这种方法会一直堵塞直到client连接上,因此要在其他线程中调用这种方法。当连接上之后。服务端设备就行接收到client发送过来的数据。完毕对这些数据的一些操作,比如保存为一个文件或者显示给用户。


以下的样例改动于Wi-Fi P2P Demo(能够在Android SDK中找到),它展示了怎样创建client和服务端之间的socket通信。并从通过一个service从client向服务端发送了一个JPEG图片。

完整的代码,能够直接查看Wi-Fi P2P 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 {

            /**
             * 创建一个ServerSocket并等待client的连接.这种方法会一直堵塞直到接受了一个client连接
             */
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket client = serverSocket.accept();

            /**
             * 假设代码可以运行到这里,说明已经连接上一个client而且開始数据传输
             * 将来自client的数据流保存为一个JPEG文件
             */
            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;
        }
    }

    /**
     * 启动可以处理JPEG图片的activity
     */
    @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);
        }
    }
}


在client上。通过一个client的socket连接服务端的socket并数据传输。

这个样例传输了一个client设备文件系统的上的JPEG文件。

Context context = this.getApplicationContext();
String host;
int port;
int len;
Socket socket = new Socket();
byte buf[]  = new byte[1024];
...
try {
    /**
     * 使用服务端IP和端口还有连接超时时间创建一个clientsocket
     */
    socket.bind(null);
    socket.connect((new InetSocketAddress(host, port)), 500);

    /**
     * 从一个JPEG文件创建一个字节流,而且传输到socket的输出流上。

这些数据将会在服务端设备上接收到。 */ 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 } /** * 当传输数据完毕或者发生异常时关闭已经打开的socket。

*/ finally { if (socket != null) { if (socket.isConnected()) { try { socket.close(); } catch (IOException e) { //catch logic } } } }





Android Wi-Fi Peer-to-Peer(Android的Wi-Fi P2P对等网络)