zoukankan      html  css  js  c++  java
  • 马上搞定Android平台的Wi-Fi Direct开发

    导语

      移动互联网时代,很多用户趋向于将大量的资料保存在移动设备上。但在给用户带来便利的同时引发了一个新的问题——保存在移动设备上的资料该怎样共享出去?到了思考时间,普通青年这样想:折腾什么劲啊,直接用数据线不就行了;而文艺青年可能这样想:咱们建个群吧,大家有好的东西都可以共享;二逼青年不高兴了,都特么互联网时代了,来点新意,好么?直接上网盘啊,大家互相研究研究,你懂的,嘿嘿。然而我是这样想的:都特么别BB,技术要以时俱进,来个新潮点的不行么?直接上Wi-Fi Direct。好用简单不解释。那么我们就迫不及待地开始吧。

    认识Wi-Fi Direct

      Wi-Fi Direct,也叫Wi-Fi P2p,还叫Wi-Fi直连,这是它的名字。但是它和WI-Fi是什么关系呢,有一腿?我不知道。但是我们可以用我们熟悉的Wi-Fi来理解不熟悉的Wi-Fi Direct。想象这样的情景,当用Wi-F前我们必须要干什么啊,那当然是接热点啊。但是要是没有热点怎么办?没热点玩个毛啊。但是Wi-Fi Direct就可以玩,而且可以玩得很愉快。所以Wi-Fi Direct不用连接热点,也不需要。没网也能玩得high,就是这么任性。那它怎么玩啊?兄弟,知道蓝牙是怎么玩的么?不知道,唉,算了。我们还是看名字吧。p2p(peer to peer),也就是点对点,就是我的手机是一个点,你的手机是一个点,duang~~~,一条网线(数据线)怼上了,唉,网络好了,可以玩耍了,就是怎么简单。但是为什么要用它呢?我跟它又不是太熟,跟蓝牙玩耍不好么?大声告诉你,不好。普通手机自带蓝牙传输距离是多远,不知道吧,10~13m左右,Wi-Fi Direct呢,长一倍,自己算。传输速率呢,你忍受得了蓝牙的速度么?反正我是受不了0.2M/s的速度,但是Wi-Fi Direct 7,8M/s的速度我就很喜欢了。所以,骚年,用Wi-Fi Direct吧,靠谱!

    开发Wi-Fi Direct的Android应用

    1、Android开发第一步——Manifest权限注册

      涉及到Wi-Fi,当然理所应当的要用到INTERNET了。哦,不,等等。你之前不是说过,它不用网络么,怎么现在反悔了。是,我是说过它不用网络也可以玩,但是,你两台怼在一起的设备间访问,你总要有种访问方式吧,Wi-Fi Direct使用Socket,而Socket就需要INTERNET了。另外,它还需要用到CHANGE_WIFI_STATE。因为两台设备间怼上了只是一种理解,实际上,它是无线访问,而上面我没告诉你的是,Wi-Fi Direct兼容Wi-Fi。而且大部分的手机厂商都直接使用Wi-Fi模块实现的Wi-Fi Direct标准,所以它们通常是绑定在一起的。怼上了就说明连上了,连上了是不是就改变了你的Wi-Fi状态。最后,还需要ACCESS_WIFI_STATE,这个怎么理解呢,额,我不知道。自己想去,总之需要就对了。

    2、创建并注册一个广播接收器

      Android系统将Wi-Fi Direct技术封装了,当需要使用到该技术时,只需拦截相应的系统广播即可,系统会把Wi-Fi Direct的硬件状态通过广播告诉我们的,在广播中就可以获取到所需要的信息,如周围的设备列表,管理员的IP地址等信息,有了这些信息后就可以建立Socket连接,进行数据操作了。所以广播接收器可以说是这一大步骤的关键。创建广播接收器很简单,只需创建一个类继承自BroadcastReceiver,重写OnReceive()方法即可。可关键在于,在接收到广播消息后,应该怎样处理?这就得先看看会接收到些什么消息了。为此,我整理了个说明表格,如表一:

    表格一 Wi-Fi Direct广播接收器侦听事件说明

    WIFI_P2P_STATE_CHANGED_ACTION

    指示Wi-Fi P2P是否开启或设备是否支持Wi-Fi Direct

    WIFI_P2P_PEERS_CHANGED_ACTION

    代表对等节点(peer)列表发生了变化

    WIFI_P2P_CONNECTION_CHANGED_ACTION

    表明Wi-Fi P2P的连接状态发生了改变

    WIFI_P2P_THIS_DEVICE_CHANGED_ACTION

    指示设备的详细配置发生了变化

    现在已经明确了,广播中会接收到的消息,所以,接下来的任务就是怎样处理这些消息。不过在开始之前,需要对一个关键类进行说明——WifiP2pManager。

      这个类负责管理Wi-Fi对等设备。它让应用可以发现可用的对等设备,设置对等设备的连接以及查询对等设备的列表。需要注意的是,WifiP2pManager对应用请求都是采用的异步处理,所以需要使用到比较多的回调.它的大致工作过程是,首先通过Context的getSystemService方法获取WifiP2pManager对象,紧接着需要用WifiP2pManager对象的initialize方法对它进行初始化,初始化完成会返回另一个关键参数— WifiP2pManager.Channel,之后的发现,连接操作都需要它的参与才能完成。初始化完成后,就可以调用discoverPeers方法进行设备搜寻了。当搜寻到设备后会以广播的形式通知应用,应用获取到通知后,需要调用WifiP2pManager对象的requestPeers方法获取可用的设备列表。当获取到可用列表后,会以回调的方式通知监听在WifiP2pManager对象上的onPeersAvailable方法,这里就可以获取到相应的设备列表信息,用户就可以选择其中一台设备,继续调用WifiP2pManager对象的connect方法连接设备了,连接成功后,依旧会通知监听在WifiP2pManager对象上的回调,调用onConnectionInfoAvailable方法,这里就可以获取到管理员的IP信息,管理员是谁等信息,之后就是Socket的事了。

      明确了WifiP2pManager的角色,接下来就可以处理广播消息了。首先,WIFI_P2P_STATE_CHANGED_ACTION这个动作很好处理,因为它携调用带的消息比较单一,就是给出当前设备是否支持Wi-Fi Direct标准,或者Wi-Fi Direct功能是否开启。所以当接收到这个消息后,可以针对相应的消息,通知Activity显示相应的对话框,通知用户即可,因为这些信息不是软件能够处理的。其次,当收到WIFI_P2P_PEERS_CHANGED_ACTION消息时,说明系统已经扫描到相应的设备了,需要把获取这些信息,通知Activity进行相应处理即可。获取的操作就是上面提到过的WifiP2pManager,调用WifiP2pManager.requestPeers方法来请求列表。接着处理WIFI_P2P_CONNECTION_CHANGED_ACTION,这个消息说明了Activity已经调用了connect方法了,所以这个消息封装的都是连接状态的一些信息,所以这里需要做的就是判断连接是否是可用的,如果可用,就将可用信息获取出来,传递给Activity处理。最后来处理WIFI_P2P_THIS_DEVICE_CHANGED_ACTION消息,这个消息其实通常情况下是可以不用处理的,如果想处理也只需在Activity中更新本机设备信息就行了。至此,广播接收器中的处理都已经结束了,有了它,Activity中的处理就很容易了。

      Activity的操作流程也很清晰,其实就是将前面提到的WifiP2pManager的流程拆分出来了,Activity的作用就是注册广播,接收广播传过来的消息并处理,在必要时解注册广播即可。注册广播需要创建IntentFilter对象,并添加表一中的所有动作,调用Context.registerReceiver。然后就是WifiP2pManager的上场时间了,具体的流程前面已经描述了,所以这里只是着重描述一下三个关键点。首先是discoverPeers,启动设备发现,系统会自动处理相关的操作,扫描结束后会以广播的形式通知应用,这一步很关键,没有这一步,后面的步骤都不会进行。之后需要Activity实现相应的监听器回调——WifiP2pManager.PeerListListener,当周围设备信息可用时,并且执行了requestPeers后,会回调onPeersAvailable方法,该方法会传进来WifiP2pDeviceList对象,调用WifiP2pDeviceList.getDeviceList()就可以获取到扫描到的设备列表,接下来就可以选取其中一个设备进行connect()操作了。这一步还是需要设置监听器 WifiP2pManager.ConnectionInfoListener,当连接状态可用时会触发 onConnectionInfoAvailable()方法,在这里通过参数WifiP2pInfo就可以拿到IP地址,谁是组管理员等信息,之后就可以用这些信息建立Socket连接,从而进行数据操作了。假如是管理员就应该创建一个服务线程等待组成员连接进来,假如是普通的用户则创建Socket连接到管理员的设备,连接建立成功后,可以用IO操作数据了。

    3、数据传送

      其实上面已经说过了,当 onConnectionInfoAvailable()方法调用时,这时候两台设备已经怼上了。但是怼上了,总有台设备是服务器吧,不然怎么玩。服务器的地址可以用传进来的参数WifiP2pInfo对象的groupOwnerAddress查到,自己是不是服务器也可以通过WifiP2pInfo.isGroupOwner确定。至此,就是玩Socket的事了。别告诉我你不会,假如真是不会的话,建议你先看看怎么玩Socket。因为这个高级玩法目前你还驾驭不了。ok,国际惯例,到了上源码的时间了。

    4、秀代码

     1 import android.content.BroadcastReceiver;
     2 import android.content.Context;
     3 import android.content.Intent;
     4 import android.net.NetworkInfo;
     5 import android.net.wifi.p2p.WifiP2pManager;
     6 
     7 /**
     8  * Created by Andy on 2016/5/10.
     9  */
    10 
    11 public class WifiDirectReceiver extends BroadcastReceiver {
    12     private HandlerActivity handlerActivity;
    13     private WifiP2pManager manager;
    14 
    15     public WifiDirectReceiver(){}
    16 
    17     public WifiDirectReceiver(HandlerActivity handlerActivity,WifiP2pManager manager) {
    18         this.handlerActivity=handlerActivity;
    19         this.manager=manager;
    20     }
    21 
    22     @Override
    23     public void onReceive(Context context, Intent intent) {
    24         String action=intent.getAction();
    25         if(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)){
    26             /*判断wifi p2p是否可用*/
    27             int state=intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,-1);
    28             if(state==WifiP2pManager.WIFI_P2P_STATE_ENABLED){
    29                 handlerActivity.setIsWifiP2pEnabled(true);
    30             }else {
    31                 handlerActivity.setIsWifiP2pEnabled(false);
    32             }
    33         }else if(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)){
    34             /*可用设备列表发生变化,*/
    35             manager.requestPeers(handlerActivity.getChannel(),handlerActivity);
    36         }else if(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)){
    37             /*连接状态发生变化*/
    38             NetworkInfo info=intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
    39             if(info.isConnected()){
    40                 manager.requestConnectionInfo(handlerActivity.getChannel(),handlerActivity);
    41             }else{
    42                 handlerActivity.onConnectDisabled();
    43             }
    44         }else if(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)){
    45             /*当前设备发生变化*/
    46         }
    47     }
    48 }

      1 import android.app.AlertDialog;
      2 
      3 import android.app.Fragment;
      4 import android.app.FragmentManager;
      5 import android.app.FragmentTransaction;
      6 import android.content.Context;
      7 import android.content.DialogInterface;
      8 import android.content.Intent;
      9 import android.content.IntentFilter;
     10 import android.net.wifi.p2p.WifiP2pConfig;
     11 import android.net.wifi.p2p.WifiP2pDeviceList;
     12 import android.net.wifi.p2p.WifiP2pInfo;
     13 import android.net.wifi.p2p.WifiP2pManager;
     14 import android.os.Bundle;
     15 import android.widget.Toast;
     16 
     17 import com.mob.lee.fastair.BaseActivity;
     18 import com.mob.lee.fastair.R;
     19 import com.mob.lee.fastair.home.HomeFragment;
     20 import com.mob.lee.fastair.utils.ActivityControllerUtil;
     21 import com.mob.lee.fastair.utils.WifiP2pUtils;
     22 
     23 import java.net.InetAddress;
     24 
     25 import butterknife.ButterKnife;
     26 
     27 /**
     28  * Created by Andy on 2016/5/10.
     29  */
     30 public class HandlerActivity extends BaseActivity
     31         implements WifiP2pManager.PeerListListener,
     32         WifiP2pManager.ConnectionInfoListener{
     33 
     34     private WifiP2pManager mManager;
     35     private WifiP2pManager.Channel mChannel;
     36     private IntentFilter mFilter;
     37     private WifiDirectReceiver mWifiDirectReceiver;
     38 
     39     private boolean connected;
     40 
     41     private Fragment currentFragment;
     42 
     43     @Override
     44     protected void initView() {
     45         setContentView(R.layout.activity);
     46         ButterKnife.inject(this);
     47     }
     48 
     49     @Override
     50     protected void setting(Bundle savedInstanceState) {
     51         setCurrentFragment(new DiscoverFragment());
     52 
     53         mFilter = new IntentFilter();
     54         /*指示wifi p2p的状态变化*/
     55         mFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
     56         /*指示可用节点列表的变化*/
     57         mFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
     58         /*指示连接状态的变化*/
     59         mFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
     60         /*指示当前设备发生变化*/
     61         mFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
     62 
     63         /*初始化wifi p2p的控制器*/
     64         mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
     65         mChannel = mManager.initialize(this, getMainLooper(), null);
     66      /*开启设备发现*/
     67         mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
     68             @Override
     69             public void onSuccess() {
     70             }
     71   
     72             @Override
     73             public void onFailure(int reason) {
     74                 new AlertDialog.Builder(HandlerActivity.this)
     75                         .setTitle(R.string.tips_error)
     76                         .setMessage(WifiP2pUtils.getWifiP2pFailureReson(reason))
     77                         .setPositiveButton(R.string.tips_ok, new DialogInterface.OnClickListener() {
     78                             @Override
     79                             public void onClick(DialogInterface dialog, int which) {
     80                                 finish();
     81                             }
     82                         }).show();
     83             }
     84         });
     85 
     86      /*注册广播*/
     87         mWifiDirectReceiver = new WifiDirectReceiver(this, mManager);
     88         registerReceiver(mWifiDirectReceiver, mFilter);
     89     }
     90 
     91     @Override
     92     protected void onDestroy() {
     93         super.onDestroy();
     94       /*动态注册的广播必须解注册*/
     95         unregisterReceiver(mWifiDirectReceiver);
     96         if(connected) {
     97        /*现在我不用了,但是还连着,我不高兴,移除吧,下次再连*/
     98             mManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
     99                 @Override
    100                 public void onSuccess() {
    101 
    102                 }
    103 
    104                 @Override
    105                 public void onFailure(int reason) {
    106                     Toast.makeText(HandlerActivity.this, getResources().getString(R.string.toast_removeFalid) + WifiP2pUtils.getWifiP2pFailureReson(reason), Toast.LENGTH_SHORT).show();
    107                 }
    108             });
    109         }
    110     }
    111 
    112 
    113     public void setIsWifiP2pEnabled(boolean enabled) {
    114      /*设备是否支持Wi-Fi Direct或者打开开关,通知一下*/
    115         if (!enabled) {
    116             new AlertDialog.Builder(this)
    117                     .setTitle(R.string.tips_error)
    118                     .setMessage(R.string.tips_disabled_wifi_p2p)
    119                     .setPositiveButton(R.string.tips_ok, new DialogInterface.OnClickListener() {
    120                         @Override
    121                         public void onClick(DialogInterface dialog, int which) {
    122                             finish();
    123                         }
    124                     }).show();
    125         }
    126     }
    127 
    128     public WifiP2pManager.Channel getChannel() {
    129         return mChannel;
    130     }
    131 
    132    /*发现周围设备了*/
    133     @Override
    134     public void onPeersAvailable(WifiP2pDeviceList peers) {
    135         if(currentFragment instanceof DiscoverFragment) {
    136             ((DiscoverFragment)currentFragment).findDeviceList(peers.getDeviceList());
    137         }
    138     }
    139 
    140    /*连接设备*/
    141     public void connectDevice(WifiP2pConfig config){
    142         mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
    143             @Override
    144             public void onSuccess() {
    145                 Toast.makeText(HandlerActivity.this, R.string.toast_connectSuccess, Toast.LENGTH_SHORT).show();
    146             }
    147 
    148             @Override
    149             public void onFailure(int reason) {
    150                 new AlertDialog.Builder(HandlerActivity.this)
    151                         .setTitle(R.string.tips_error)
    152                         .setMessage(WifiP2pUtils.getWifiP2pFailureReson(reason))
    153                         .setPositiveButton(R.string.tips_ok, new DialogInterface.OnClickListener() {
    154                             @Override
    155                             public void onClick(DialogInterface dialog, int which) {
    156                                 dialog.dismiss();
    157                             }
    158                         }).show();
    159             }
    160         });
    161     }
    162 
    163    /*连接完成,获取管理员的IP,跳转界面*/
    164     @Override
    165     public void onConnectionInfoAvailable(WifiP2pInfo info) {
    166         InetAddress address = null;
    167         boolean isGroupOwner = false;
    168         if (info.groupFormed && info.isGroupOwner) {
    169             address = info.groupOwnerAddress;
    170             isGroupOwner = true;
    171         } else if (info.groupFormed) {
    172             address = info.groupOwnerAddress;
    173             isGroupOwner = false;
    174         }
    175         if (null != address) {
    176             Intent preIntent = getIntent();
    177             preIntent.putExtra("address", address.getHostAddress());
    178             preIntent.putExtra("isGroupOwner", isGroupOwner);
    179             Fragment fragment=null;
    180             setCurrentFragment(fragment);
    181             connected=true;
    182         }
    183     }
    184 
    185     public void onConnectDisabled(){
    186         connected=false;
    187     }
    188 
    189   public void setCurrentFragment(Fragment fragment){
    190         currentFragment=fragment;
    191         FragmentManager manager=getFragmentManager();
    192         FragmentTransaction transaction = manager.beginTransaction();
    193         transaction.replace(R.id.activity_content, fragment);
    194         transaction.commit();
    195     }
    196 
    197 }
      1 import android.app.AlertDialog;
      2 import android.content.DialogInterface;
      3 import android.net.wifi.WpsInfo;
      4 import android.net.wifi.p2p.WifiP2pConfig;
      5 import android.net.wifi.p2p.WifiP2pDevice;
      6 import android.os.Bundle;
      7 import android.view.LayoutInflater;
      8 import android.view.View;
      9 import android.view.ViewGroup;
     10 import android.widget.ImageView;
     11 import android.widget.RadioGroup;
     12 import android.widget.TextView;
     13 
     14 import com.mob.lee.fastair.BaseActivity;
     15 import com.mob.lee.fastair.BaseFragment;
     16 import com.mob.lee.fastair.R;
     17 import com.mob.lee.scanview.ScanLayout;
     18 
     19 import java.util.ArrayList;
     20 import java.util.Collection;
     21 
     22 import butterknife.ButterKnife;
     23 import butterknife.InjectView;
     24 
     25 /**
     26  * Created by Andy on 2016/5/10.
     27  */
     28 public class DiscoverFragment extends BaseFragment{
     29     @InjectView(R.id.fragment_discover_scanlayout)
     30     ScanLayout mScanLayout;
     31 
     32     private WifiP2pDevice device;
     33     private ArrayList<WifiP2pDevice> mDevices;
     34     private boolean isBeginConnection;
     35 
     36     @Override
     37     protected View initView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     38         View view=inflater.inflate(R.layout.fragment_discover,container,false);
     39         ButterKnife.inject(this, view);
     40         return view;
     41     }
     42 
     43     @Override
     44     protected void setListener(View view, Bundle saveInstanceState) {
     45         /*连接某台设备*/
     46         mScanLayout.setOnItemClickListener(new ScanLayout.OnItemClickListener() {
     47             @Override
     48             public void onItemClick(View view, int position) {
     49                 device = mDevices.get(position);
     50                 WifiP2pConfig config = new WifiP2pConfig();
     51                 config.deviceAddress = device.deviceAddress;
     52                 config.wps.setup = WpsInfo.PBC;
     53                 ((HandlerActivity) getActivity()).connectDevice(config);
     54                 isBeginConnection=true;
     55             }
     56         });
     57     }
     58 
     59     @Override
     60     protected void setting(Bundle saveInstanceState) {
     61         ((BaseActivity)getActivity()).setToolbar(R.id.fragment_discover_toolbar, R.string.base_findDevice);
     62     }
     63 
     64     @Override
     65     public void onResume() {
     66         super.onResume();
     67         mScanLayout.startScan();
     68     }
     69 
     70     @Override
     71     public void onDestroyView() {
     72         super.onDestroyView();
     73         ButterKnife.reset(this);
     74     }
     75 
     76     /*发现了设备,显示设备列表*/
     77     public void findDeviceList(Collection<WifiP2pDevice> devices) {
     78         if ((null == mDevices)) {
     79             mDevices=new ArrayList<>();
     80         }
     81         mScanLayout.stopScan();
     82         if (0 == devices.size()) {
     83             new AlertDialog.Builder(getActivity())
     84                     .setTitle(R.string.tips_error)
     85                     .setMessage(R.string.tips_scan_failed)
     86                     .setPositiveButton(R.string.tips_ok, new DialogInterface.OnClickListener() {
     87                         @Override
     88                         public void onClick(DialogInterface dialog, int which) {
     89                             getActivity().finish();
     90                         }
     91                     }).show();
     92         } else if(isBeginConnection){
     93             return;
     94         }else{
     95             for(WifiP2pDevice device:devices){
     96                 if(mDevices.contains(device)){
     97                     continue;
     98                 }
     99                 mDevices.add(device);
    100                 View view = LayoutInflater.from(getActivity()).inflate(R.layout.item_scan, null);
    101                 ((TextView) view.findViewById(R.id.item_scan_name)).setText(device.deviceName);
    102                 mScanLayout.addView(view);
    103             }
    104         }
    105     }
    106 }
  • 相关阅读:
    启动时间知多少?8款音视频类应用测评报告分析
    优化信息流很麻烦?三招教你轻松搞定
    vmstat
    iostat
    dstat
    strace
    Mysql(一)
    rsync
    Kubernetes-深入分析集群安全机制
    Kubernetes-apiserver
  • 原文地址:https://www.cnblogs.com/andy-tab/p/5479565.html
Copyright © 2011-2022 走看看