Wifi Direct:功能测试

1.WiFi 直连中收到 WIFI_P2P_STATE_CHANGED_ACTION 广播时,更改直连状态,根据状态不同执行相应操作。如果 WiFi 未开启,要跳到设置界面去设置,本来想如果设置返回还要判断执行,所以放到 onResum 方法里执行,但这样有问题,这个界面还可能跳到其它界面,状态可能没改变,而且 onResume 比广播接收者要调用的方法执行的更早,所以 onResume 里用到的 isWifiP2pEnabled 并不是最新的。所以后来放在更改状态之后执行,这样只要更改状态就执行。

        广播接收者收到广播调用Activity中的方法,设置状态后,把自己想要紧接着做的封装到一个方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void setIsWifiP2pEnabled(boolean isWifiP2pEnabled) {
    this.isWifiP2pEnabled = isWifiP2pEnabled;
    // 状态变化了,调用这个方法
    testAndDiscover();
}
// 看WiFi功能是否开启,若开启,去发现设备
private void testAndDiscover() {
 
    if(!isWifiP2pEnabled) {
        AlertDialog.Builder builder = new Builder(this);
        builder.setTitle("提示")
        .setMessage("请先确认您的设备支持WiFi直连功能。如果支持,请先在设置中开启WiFi")
        .setPositiveButton("去设置"new DialogInterface.OnClickListener() {
 
            @Override
            public void onClick(DialogInterface dialog, int which) {
                startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
            }
        })
        .setNegativeButton("不弄了"new DialogInterface.OnClickListener() {
 
            @Override
            public void onClick(DialogInterface dialog, int which) {
 
            }
        }).show();
    else // 如果wifi直连功能支持并已开启
        WifiDirectUtil.discoverPeers(DeviceListActivity.this, manager, channel);
    }
}

2.广播接收者里收到的 WIFI_P2P_THIS_DEVICE_CHANGED_ACTION 这个广播是更新自己的状态

1
2
3
4
5
if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
    // 更新自己的设备状态
    activity.updateThisDevice((WifiP2pDevice) intent
            .getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
}
        收到广播的意图intent中保存了自己这个设备的信息,所以通过(WifiP2pDevice) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE 来得到自己这台设备 WifiP2pDevice 对象。
        更新自己的设备状态
1
2
3
4
5
6
public void updateThisDevice(WifiP2pDevice device) {
    TextView myName = (TextView) findViewById(R.id.my_name);
    TextView myStatus  = (TextView) findViewById(R.id.my_status);
    myName.setText(device.deviceName);
    myStatus.setText(Util.getDeviceStatus(device.status));
}

其中 device.status 是int型值,所以要转变为对应文字,这样看着有意义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static String getDeviceStatus(int deviceStatus) {
    switch (deviceStatus) {
    case WifiP2pDevice.AVAILABLE:
        return "Available";
    case WifiP2pDevice.INVITED:
        return "Invited";
    case WifiP2pDevice.CONNECTED:
        return "Connected";
    case WifiP2pDevice.FAILED:
        return "Failed";
    case WifiP2pDevice.UNAVAILABLE:
        return "Unavailable";
    default:
        return "Unknown";
    }
}

3.原来 discoverPeers,requestPeers 之类的方法都是放在Activity里,但如果作为一个库供其它程序调用的话要把这些方法提取出来放到一个工具类里。比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
   * 去发现设备
   * @param context
   * @param manager
   * @param channel
   */
public static void discoverPeers(final Context context, WifiP2pManager manager, Channel channel) {
    Log.i(TAG, "discoverPeers");
    manager.discoverPeers(channel, new ActionListener() {
 
        @Override
        public void onSuccess() {
            // 成功后,会收到对应广播
        }
 
        @Override
        public void onFailure(int reason) {
            Toast.makeText(context, "搜索设备失败,错误代码:" + reason,
                    Toast.LENGTH_SHORT).show();
        }
    });
}
 
/**
   * 更新自己这台设备的状态
   * @param activity
   * @param device
   */
public static void updateThisDevice(Activity activity, WifiP2pDevice device) {
    Log.i(TAG, "updateThisDevice");
    TextView myName = (TextView) activity.findViewById(R.id.my_name);
    TextView myStatus  = (TextView) activity.findViewById(R.id.my_status);
    myName.setText(device.deviceName);
    myStatus.setText(getDeviceStatus(device.status));
}
        这样在 Activity 和 BroadcastReceiver 中只要调用这些方法,并传入对应参数即可。
        在广播接收者里收到 WIFI_P2P_PEERS_CHANGED_ACTION 这个广播时,要调用 WifiP2pManager 的requestPeers 方法,第二个参数是 PeerListListener 对象,会自动回调里面的 onPeersAvailable 方法。原来是让设备列表的适配器直接实现 PeerListListener,但这样就写死了,所以考虑将这个 requestPeers 方法也提取到工具类里。
        在 onPeersAvailable 方法里得到更新的设备列表,应该去更新适配器的数据并刷新。原来是这样的
1
2
3
4
5
6
7
8
9
10
11
@Override
public void onPeersAvailable(WifiP2pDeviceList peerList) {
    // peers 就是适配器中用到的那个设备列表集合    
    peers.clear();
    peers.addAll(peerList.getDeviceList());
 
    notifyDataSetChanged(); // 刷新适配器
    if (peers.size() == 0) {
        return;
    }
}

但现在将 PeerListListener 和适配器分离,所以考虑得到最新的列表后去通过方法调用去更改适配器中内容,然后再刷新,所以要在适配器类中添加一个方法,去设置它自己的那个 List 集合,但用户开始不知道一定要有这方法,所以写一个接口

1
2
3
4
5
6
7
8
public interface BaseDeviceAdapter {
    /**
       * 更新适配器中的设备列表那个List集合。并刷新适配器内容
       * @param peers
       */
    public void updateDeviceList(List<WifiP2pDevice> peers);
}

这样自己写的 DeviceAdapter 实现接口,重写这个方法

1
2
3
4
5
6
// 更新peers并刷新适配器
@Override
public void updateDeviceList(List<WifiP2pDevice> peers) {
    this.peers = peers;
    notifyDataSetChanged();
}

在工具类里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
   * 查找设备
   * @param manager
   * @param channel
   * @param adapter 要更新内容的适配器,类型是自己定义的那个接口
   */
public static void requestPeers(WifiP2pManager manager,  Channel channel, final BaseDeviceAdapter adapter) {
    Log.i(TAG, "requestPeers");
    manager.requestPeers(channel, new PeerListListener() {
 
        @Override
        public void onPeersAvailable(WifiP2pDeviceList peerList) {
            List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>();
            peers.clear();
            peers.addAll(peerList.getDeviceList());
 
            // 更新适配器中内容。利用参数传递的那个接口调用
            adapter.updateDeviceList(peers);
            if (peers.size() == 0) {
                return;
            }
        }
    });
}

4.连接一台设备,知道的是WifiP2pDevice,WifiP2pManager 真正去 connect 需要的 WifiP2pConfig 信息通过 WifiP2pDevice 来设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void connect(final Context context, WifiP2pManager manager,
    Channel channel, WifiP2pDevice device) {
    WifiP2pConfig config = new WifiP2pConfig();
    config.deviceAddress = device.deviceAddress;
    config.wps.setup = WpsInfo.PBC;
 
    manager.connect(channel, config, new ActionListener() {
        @Override
        public void onSuccess() {
            // WiFiDirectBroadcastReceiver will notify us. Ignore for now.
        }
 
        @Override
        public void onFailure(int reason) {
            Toast.makeText(context, "连接失败,请重试", Toast.LENGTH_SHORT).show();
        }
    });
}

5.广播接收者收到这个广播 WIFI_P2P_CONNECTION_CHANGED_ACTION 表示连接状态改变了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if (manager == null) {
    return;
}
 
NetworkInfo networkInfo = (NetworkInfo) intent
                    .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
 
if (networkInfo.isConnected()) { // 成功连接上
    Log.i(TAG, "收到广播:连接成功");
    WifiDirectUtil.requestConnectionInfo(manager, channel);
else {
    // 不管哪一方主动断开连接,这里都会收到广播,在这里去重置数据
    // 比如清空一些数据,或者重新 discoverPeers 显示到界面上
    activity.resetData();
}
        
// 在Activity中,如果直连断了,重新查找设备
public void resetData() {
    WifiDirectUtil.discoverPeers(DeviceListActivity.this, manager, channel);
}
        而在 Activity 中的 onCreate 方法中每次进入都调用 adapter.clearPeers() 先清空适配器中数据,不然上一次的数据不清空会影响下一次的界面显示内容。
        在适配器中
1
2
3
4
public void clearPeers() {
    peers.clear();
    notifyDataSetChanged();
}

在 onDestory 中断开连接,如果原来是连接的,断开成功,如果原来就是断开的,断开失败,反正没影响。

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s