Android WiFi P2P

本文介绍了Android 4.0及以上版本的WiFi Peer-to-Peer(P2P)技术,允许设备之间直接通信,无需中间访问点。内容涵盖API概览、Broadcast Receiver创建、APP初始化、发现和连接Peer,以及数据交换的流程。利用WiFi P2P,开发者可以构建多用户游戏或照片分享等应用,实现更远距离、高速的数据共享。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述:

Wi-Fi peer-to-peer(P2P)让Android4.0及之后版本的设备可以使用合适的硬件来通过WiFi直接连接对方而不用中间访问点. 使用这些API, 我们可以发现和连接其它支持WiFi P2P的设备, 并可以跨越比蓝牙远的多的距离高速通信. 这对那些需要在用户间共享数据的APP很有用, 比如一个多用户游戏或者照片分享APP. WiFi P2P包含这些主要的部分:

l  可以让我们发现, 请求和连接的定义在WifiP2pManager类中的方法.

l  可以提醒我们WifiP2pManager方法调用成功或者失败的监听器. 当调用WifiP2pManager中的方法, 每个方法都传递收到一个指定的listener作为参数.

l  当WiFi P2Pframework检测到指定事件的时候通过intent提醒我们. 比如丢失连接或者发现了新的目标.

我们会经常一起使用这三个组件. 栗如, 可以提供一个WifiP2pManager.ActionListener给discoverPeers(), 这样就可以得到ActionListener.onSuccess()和ActionListener.onFailure()的提醒. 当discoverPeers()发现peer列表发生变化的时候会发出一个WIFI_P2P_PEERS_CHANGED_ACTION的intent广播.

API总览:

WifiP2pManager类提供了很多方法允许我们使用WiFi硬件来做比如发现和连接到其它设备的操作. 我们可以做这些事儿:

方法

描述

initialize()

注册APP到WiFi framework. 必须先于任何WiFi方法执行.

connect()

启动一个与指定配置的设备的P2P连接.

cancelConnect()

取消任何正在进行的P2P连接.

requestConnectInfo()

请求设备的连接信息.

createGroup()

通过当前设备作为一个组的拥有者创建一个P2P组.

removeGroup()

移除当前的P2P组.

requestGroupInfo()

请求P2P组信息.

discoverPeers()

初始化peer发现.

requestPeers()

请求当前发现的peer的列表.

WifiP2pManager中的方法可以接收监听器, 这样WiFi P2Pframework可以提醒我们的activity状态变化. 可用的监听器接口和相应的WifiP2pManager方法可以参考下表:

监听器接口

关联方法

WifiP2pManager.ActionListener

Connect(), cancelConnect(), createGroup(), removeGroup(), discoverPeers()

WifiP2pManager.ChannelListener

Initialize()

WifiP2pManager.ConnectionInfoListener

requestConnectInfo()

WifiP2pManager.GroupInfoListener

requestGroupInfo()

WifiP2pManager.PeerListListener

requestPeers()

WiFi P2P API还定义了一些广播的intent, 在WiFi P2P事件发生的时候会发送广播,比如当一个新的peer被发现或者设备的WiFi状态发生变化的时候. 我们可以注册接收这些intent来处理这些事件:

Intent

Description

WIFI_P2P_CONNECTION_CHANGED_ACTION

当设备WiFi连接状态改变的时候广播.

WIFI_P2P_PEERS_CHANGED_ACTION

当我们调用discoverPeers()时候广播. 如果我们处理了这个intent, 那么可能会经常调用requestPeers()来获得peer的列表更新peer的list.

WIFI_P2P_STATE_CHANGED_ACTION

WiFi P2P启动或者关闭的时候广播.

WIFI_P2P_ THIS_DEVICE_CHANGED_ACTION

当一个设备的详细信息发生变化的时候会广播, 比如设备的名字.

为WiFi P2P Intent创建一个Broadcast Receiver:

Broadcast receiver让我们可以接收Androidframework发出的广播, 这样我们的APP就可以响应我们感兴趣的事件了. 创建它的基础步骤是这样的:

1.      创建一个类, 继承自BroadcastReceiver类. 我们最常为其构造方法指定的参数是WifiP2pManager和WifiP2pManager.Channel. 这让broadcast receiver可以给activity发送更新, 就好像访问了WiFi硬件和通信通道一样.

2.      在broadcast receiver的onReceive()方法中检查感兴趣的intent. 比如如果broadcast receiver收到了一个WIFI_P2P_PEERS_CHANGED_ACTION intent, 我们就可以调用requestPeers()方法来得到当前发现的peer的列表.

下面的代码展示了如何创建一个典型的broadcast receiver. 它接收一个WifiP2pManager对象和一个activity作为参数, 并使用这俩类来处理感兴趣的intent:

/**
 * A BroadcastReceiver that notifies of important Wi-Fi p2p events.
 */

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)) {
            // Check to see if Wi-Fi is enabled andnotify appropriate activity
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
            // Call WifiP2pManager.requestPeers() toget a list of current peers
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
            // Respond to new connection ordisconnections
        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            // Respond to this device's wifi statechanging
        }
    }
}

创建一个WiFi P2P的APP:

创建这样一个APP涉及创建和注册一个broadcast receiver, 发现peer, 连接到一个peer, 还有交换数据. 下面我们挨个讨论它们.

初始化设置:

在使用WiFi P2P的API之前, 我们必须确保APP可以访问硬件并且设备支持WiFi P2P协议. 如果WiFi P2P可以支持, 我们可以获取一个WifiP2pManager的实例, 创建和注册我们的broadcast receiver, 并开始使用WiFi P2P API.

1.      请求使用WiFi硬件的使用权限, 声明APP的最小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" />

2.      检查WiFi P2P是否支持并开启. 一个检查该功能的好地方是在broadcast receiver收到WIFI_P2P_STATE_CHANGED_ACTION intent的时候. 提醒activityWiFiP2P的状态并作出相应的反应:

@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 is enabled
        } else {
            // Wi-Fi P2P is not enabled
        }
    }
    ...
}

3.      在activity的onCreate()方法中, 获取一个WifiP2pManager的实例并通过initialize()方法注册WiFi P2P framework. 该方法返回一个WifiP2pManager.Channel, 它用来连接我们的APP与WiFi P2Pframework. 我们还应该用WifiP2pManager和WifiP2pManager.Channel创建一个broadcast receiver的实例. 这样就可以收到感兴趣的事件了, 还可以操作WiFi的状态(如果需要的话):

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

4.      创建一个intent filter并增加感兴趣的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);
    ...
}

5.      在onResume()方法中注册broadcast receiver, 并在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并设置了broadcast receiver, APP就可以调用WiFi P2P的方法和接收WiFiP2P的intent了. 现在就可以实现APP并使用WiFi P2P的功能了.

发现peer:

想要发现可以连接的peer, 调用discoverPeers()方法来检查范围内可用的peer. 该方法是异步的, 如果我们创建了WifiP2pManager.ActionListener的话它的结果将会通过onSuccess()和onFailure()方法返回. onSuccess()方法只会提醒我们发现操作处理成功, 而不提供任何关于发现的设备的信息(如果有的话):

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

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

如果发现操作成功了并检查到了peer, 系统将会广播一个WIFI_P2P_PEERS_CHANGED_ACTION, 我们可以通过监听它来得到一个peer的列表. 当APP收到这个intent的时候, 我们可以用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 (mManager != null) {
        mManager.requestPeers(mChannel, myPeerListListener);
    }
}

RequestPeers()方法也是异步的, 如果它发现了列表可用, 将会在onPeersAvailable()方法中提醒我们, 想要实现这个功能需要实现WifiP2pManager.PeerListListener接口. onPeersAvailable()方法提供给我们一个WifiP2pDeviceList列表, 通过这个列表我们可以找到想要连接的peer.

连接Peer:

当我们决定了想要连接的设备之后, 调用connect()方法来连接该设备. 这个方法的调用需要一个WifiP2pConfig对象作为参数, 它包含了要连接的设备的信息. 我们可以通过WifiP2pManager.ActionListener来接收连接的结果是成功还是失败. 栗子:

//obtain a peer from the WifiP2pDeviceList
WifiP2pDevice device;
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
mManager.connect(mChannel, config, new ActionListener() {

    @Override
    public void onSuccess() {
        //success logic
    }

    @Override
    public void onFailure(int reason) {
        //failure logic
    }
});

数据交换:

一旦连接建立了, 我们就可以在设备间用socket进行数据交换了. 这些是交换数据的基本步骤:

1.      创建一个ServerSocket. 该socket会在指定端口等待一个客户端的连接并在连接之前阻塞着, 所以要在后台线程中实现这个功能.

2.      创建一个客户端Socket. 客户端使用服务端的IP地址和端口来连接到服务端设备.

3.      从客户端发送数据给服务端. 当客户端socket成功的连接到服务端socket, 我们就可以通过数据流从客户端发送数据给服务端了.

4.      服务端socket等待客户端连接(通过accept()方法). 该方法会在客户端连接之前阻塞, 所以应该在一个新的线程中调用该方法. 当有客户端接入的时候, 服务端设备可以从客户端收到设备. 然后就可以保存数据或者展示给用户了.

下面的代码从WiFi P2P Demo中修改得来, 展示了如何创建一个这样的CS socket连接并通过一个服务从客户端发送JPEG图片数据给服务端:

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

在客户端中, 连接到服务端socket并交换数据. 栗子:

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
            }
        }
    }
}

 

参考: https://developer.android.com/guide/topics/connectivity/wifip2p.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值