原文地址:http://android.xsoftlab.net/training/connect-devices-wirelessly/wifi-direct.html#permissions

Wi-Fi peer-to-peer (P2P) APIs可以使程序与附近的设备进行直接通讯,Android的Wi-Fi P2P框架由Wi-Fi Direct™提供技术支持。WI-FI P2P技术可以使程序快速的检索附近的设备并与之建立连接。其覆盖范围超过蓝牙的覆盖范围。

这节课会学习如何通过WI-FI P2P技术搜索附近的设备并与之建立连接。

设置应用权限

如果要使用WI-FI P2P技术,需要在程序的清单文件中添加CHANGE\_WIFI\_STATE, ACCESS\_WIFI\_STATE, INTERNET三项权限。Wi-Fi P2P并不需要互联网连接,但是它需要使用标准的Java Socket通讯技术,所以需要使用INTERNET权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.nsdchat" ... <uses-permission android:required="true" android:name="android.permission.ACCESS_WIFI_STATE"/>    <uses-permission  android:required="true" android:name="android.permission.CHANGE_WIFI_STATE"/>    <uses-permission  android:required="true" android:name="android.permission.INTERNET"/>    ...

设置广播接收器及P2P管理员

使用WI-FI P2P技术,需要监听广播意图,广播意图会通知程序某些事件的发生。所以在程序中需要添加IntentFilter,并设置其监听以下行为:

WIFI\_P2P\_STATE\_CHANGED\_ACTION

监听Wi-Fi P2P是否可用

WIFI\_P2P\_PEERS\_CHANGED\_ACTION

监听WI-FI P2P列表的变化

WIFI\_P2P\_CONNECTION\_CHANGED\_ACTION

监听Wi-Fi P2P的连接状态

WIFI\_P2P\_THIS\_DEVICE\_CHANGED\_ACTION

监听设备的配置变化
private final IntentFilter intentFilter = new IntentFilter();...@Overridepublic void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.main);    // Indicates a change in the Wi-Fi P2P status.    intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);    // Indicates a change in the list of available peers.    intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);    // Indicates the state of Wi-Fi P2P connectivity has changed.    intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);    // Indicates this device's details have changed.    intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);    ...}

在onCreate()方法的末尾,需要获取WifiP2pManager的实例,然后调用它的initialize()方法。这个方法会返回一个WifiP2pManager.Channel的对象,它用于使程序应用层与Wi-Fi P2P框架建立连接。

@OverrideChannel mChannel;public void onCreate(Bundle savedInstanceState) {    ....    mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);    mChannel = mManager.initialize(this, getMainLooper(), null);}

接下来创建一个新的BroadcastReceiver类,它用于监听系统的Wi-Fi P2P的状态变化,在onReceive()方法中,需要添加一些基本的判断条件来处理每种P2P的状态并处理:

    @Override    public void onReceive(Context context, Intent intent) {        String action = intent.getAction();        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {            // Determine if Wifi P2P mode is enabled or not, alert            // the Activity.            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);            if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {                activity.setIsWifiP2pEnabled(true);            } else {                activity.setIsWifiP2pEnabled(false);            }        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {            // The peer list has changed! We should probably do something about            // that.        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {            // Connection state changed! We should probably do something about            // that.        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {            DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager()                    .findFragmentById(R.id.frag_list);            fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(                    WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));        }    }

最后,将广播接收器与意图过滤器添加到上下文中,并需要在Activity暂停的时候注销这个广播接收器。放置这些代码的最佳位置就是onResume()方法与onPause()方法。

    /** register the BroadcastReceiver with the intent values to be matched */    @Override    public void onResume() {        super.onResume();        receiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);        registerReceiver(receiver, intentFilter);    }    @Override    public void onPause() {        super.onPause();        unregisterReceiver(receiver);    }

初始化端点搜索

如果开始要使用Wi-Fi P2P来搜索附近的设备,需要调用discoverPeers()方法。这个方法要求传入以下参数:

  • 在初始化P2P管理员时获得的WifiP2pManager.Channel对象。
  • WifiP2pManager.ActionListener的实现,它用于监听搜索的成功与否。
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {        @Override        public void onSuccess() {            // Code for when the discovery initiation is successful goes here.            // No services have actually been discovered yet, so this method            // can often be left blank. Code for peer discovery goes in the            // onReceive method, detailed below.        }        @Override        public void onFailure(int reasonCode) {            // Code for when the discovery initiation fails goes here.            // Alert the user that something went wrong.        }});

要记住,这里只是初始化了端点搜索。discoverPeers()方法启动搜索进程后会立即返回。如果端点搜索进程成功初始化,那么系统会自动调用初始化时设置的回调方法。另外,端点搜索功能会一直保持在活动状态,直到连接初始化完成或者P2P组建立连接。

获取端点列表

接下来需要获得并处理端点列表。首先需要实现WifiP2pManager.PeerListListener接口,它提供了WI-FI P2P所搜索到的端点信息。下面的代码演示了这个过程:

    private List peers = new ArrayList();    ...    private PeerListListener peerListListener = new PeerListListener() {        @Override        public void onPeersAvailable(WifiP2pDeviceList peerList) {            // Out with the old, in with the new.            peers.clear();            peers.addAll(peerList.getDeviceList());            // If an AdapterView is backed by this data, notify it            // of the change. For instance, if you have a ListView of available            // peers, trigger an update.            ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();            if (peers.size() == 0) {                Log.d(WiFiDirectActivity.TAG, "No devices found");                return;            }        }    }

现在需要修改广播接收器的onReceive()方法,在收到WIFI\_P2P\_STATE\_CHANGED\_ACTION行为时调用requestPeers()方法。在这之前需要将监听器的实例传入到广播接收器中,常规的方式是在广播接收器的构造方法中将这个监听器传进来。、

public void onReceive(Context context, Intent intent) {    ...    else 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, peerListListener);        }        Log.d(WiFiDirectActivity.TAG, "P2P peers changed");    }...}

现在,WIFI\_P2P\_STATE\_CHANGED\_ACTION的行为将会触发端点列表的更新。

连接到端点

为了可以连接到端点,需要创建一个新的WifiP2pConfig对象,然后将WifiP2pDevice中的数据拷贝进这个对象中。WifiP2pDevice代表的将要连接的设备。然后调用connect()方法。

    @Override    public void connect() {        // Picking the first device found on the network.        WifiP2pDevice device = peers.get(0);        WifiP2pConfig config = new WifiP2pConfig();        config.deviceAddress = device.deviceAddress;        config.wps.setup = WpsInfo.PBC;        mManager.connect(mChannel, config, new ActionListener() {            @Override            public void onSuccess() {                // WiFiDirectBroadcastReceiver will notify us. Ignore for now.            }            @Override            public void onFailure(int reason) {                Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",                        Toast.LENGTH_SHORT).show();            }        });    }

上述代码中的WifiP2pManager.ActionListener接口只有在初始化成功或者失败的情况下才会调用。如果要监听连接状态的变化,需要实现WifiP2pManager.ConnectionInfoListener接口,它的方法onConnectionInfoAvailable()会在连接状态发生变化的时候回调。在多台设备连接一台设备的情况下(比如多人互动的游戏或者聊天类的APP),其中一台设备会被指定为”group owner”。

    @Override    public void onConnectionInfoAvailable(final WifiP2pInfo info) {        // InetAddress from WifiP2pInfo struct.        InetAddress groupOwnerAddress = info.groupOwnerAddress.getHostAddress());        // After the group negotiation, we can determine the group owner.        if (info.groupFormed && info.isGroupOwner) {            // Do whatever tasks are specific to the group owner.            // One common case is creating a server thread and accepting            // incoming connections.        } else if (info.groupFormed) {            // The other device acts as the client. In this case,            // you'll want to create a client thread that connects to the group            // owner.        }    }

现在回到广播接收器的onReceive()方法,修改监听WIFI\_P2P\_CONNECTION\_CHANGED\_ACTION的部分,当这个意图接收到时,调用requestConnectionInfo()方法。这是一个异步方法,所以结果会通过参数:连接信息监听器回调回来。

        ...        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {            if (mManager == null) {                return;            }            NetworkInfo networkInfo = (NetworkInfo) intent                    .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);            if (networkInfo.isConnected()) {                // We are connected with the other device, request connection                // info to find group owner IP                mManager.requestConnectionInfo(mChannel, connectionListener);            }            ...

更多相关文章

  1. Android编译本地C++程序方法
  2. Android开发者网站打不开的解决方法
  3. android sdk manager 无法更新解决方法
  4. Android下app生成coredump方法
  5. Android使用webview调用js方法传参,参数无法传入的问题
  6. android6.0源码分析之Runtime的初始化
  7. Android中bindService基本使用方法概述
  8. Unity 编辑器环境下不能正确加载Android Assetbundle 中的 Shade

随机推荐

  1. Android中实现长按照片弹出右键菜单
  2. Android(安卓)计算方法运行时间
  3. 在AndroidStudio中出现android no debugg
  4. Android-ListView中嵌套(ListView)控件时
  5. Uni-app Android(安卓)离线打包集成 uni-
  6. Android(安卓)Studio 2.2 预览版 - 全新
  7. ViewPager + Fragment + RecyclerView 切
  8. "Kernel version" 中编译时间的前世今生
  9. Unity之调用AndroidWebView
  10. Android中post请求传递json数据给服务端