导语

  移动互联网时代,很多用户趋向于将大量的资料保存在移动设备上。但在给用户带来便利的同时引发了一个新的问题——保存在移动设备上的资料该怎样共享出去?到了思考时间,普通青年这样想:折腾什么劲啊,直接用数据线不就行了;而文艺青年可能这样想:咱们建个群吧,大家有好的东西都可以共享;二逼青年不高兴了,都特么互联网时代了,来点新意,好么?直接上网盘啊,大家互相研究研究,你懂的,嘿嘿。然而我是这样想的:都特么别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、秀代码

 import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.NetworkInfo;
import android.net.wifi.p2p.WifiP2pManager; /**
* Created by Andy on 2016/5/10.
*/ public class WifiDirectReceiver extends BroadcastReceiver {
private HandlerActivity handlerActivity;
private WifiP2pManager manager; public WifiDirectReceiver(){} public WifiDirectReceiver(HandlerActivity handlerActivity,WifiP2pManager manager) {
this.handlerActivity=handlerActivity;
this.manager=manager;
} @Override
public void onReceive(Context context, Intent intent) {
String action=intent.getAction();
if(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)){
/*判断wifi p2p是否可用*/
int state=intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,-1);
if(state==WifiP2pManager.WIFI_P2P_STATE_ENABLED){
handlerActivity.setIsWifiP2pEnabled(true);
}else {
handlerActivity.setIsWifiP2pEnabled(false);
}
}else if(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)){
/*可用设备列表发生变化,*/
manager.requestPeers(handlerActivity.getChannel(),handlerActivity);
}else if(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)){
/*连接状态发生变化*/
NetworkInfo info=intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if(info.isConnected()){
manager.requestConnectionInfo(handlerActivity.getChannel(),handlerActivity);
}else{
handlerActivity.onConnectDisabled();
}
}else if(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)){
/*当前设备发生变化*/
}
}
}
 import android.app.AlertDialog;

 import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDeviceList;
import android.net.wifi.p2p.WifiP2pInfo;
import android.net.wifi.p2p.WifiP2pManager;
import android.os.Bundle;
import android.widget.Toast; import com.mob.lee.fastair.BaseActivity;
import com.mob.lee.fastair.R;
import com.mob.lee.fastair.home.HomeFragment;
import com.mob.lee.fastair.utils.ActivityControllerUtil;
import com.mob.lee.fastair.utils.WifiP2pUtils; import java.net.InetAddress; import butterknife.ButterKnife; /**
* Created by Andy on 2016/5/10.
*/
public class HandlerActivity extends BaseActivity
implements WifiP2pManager.PeerListListener,
WifiP2pManager.ConnectionInfoListener{ private WifiP2pManager mManager;
private WifiP2pManager.Channel mChannel;
private IntentFilter mFilter;
private WifiDirectReceiver mWifiDirectReceiver; private boolean connected; private Fragment currentFragment; @Override
protected void initView() {
setContentView(R.layout.activity);
ButterKnife.inject(this);
} @Override
protected void setting(Bundle savedInstanceState) {
setCurrentFragment(new DiscoverFragment()); mFilter = new IntentFilter();
/*指示wifi p2p的状态变化*/
mFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
/*指示可用节点列表的变化*/
mFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
/*指示连接状态的变化*/
mFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
/*指示当前设备发生变化*/
mFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); /*初始化wifi p2p的控制器*/
mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
mChannel = mManager.initialize(this, getMainLooper(), null);
     /*开启设备发现*/
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
}
  
@Override
public void onFailure(int reason) {
new AlertDialog.Builder(HandlerActivity.this)
.setTitle(R.string.tips_error)
.setMessage(WifiP2pUtils.getWifiP2pFailureReson(reason))
.setPositiveButton(R.string.tips_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
}).show();
}
});      /*注册广播*/
mWifiDirectReceiver = new WifiDirectReceiver(this, mManager);
registerReceiver(mWifiDirectReceiver, mFilter);
} @Override
protected void onDestroy() {
super.onDestroy();
      /*动态注册的广播必须解注册*/
unregisterReceiver(mWifiDirectReceiver);
if(connected) {
       /*现在我不用了,但是还连着,我不高兴,移除吧,下次再连*/
mManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() { } @Override
public void onFailure(int reason) {
Toast.makeText(HandlerActivity.this, getResources().getString(R.string.toast_removeFalid) + WifiP2pUtils.getWifiP2pFailureReson(reason), Toast.LENGTH_SHORT).show();
}
});
}
} public void setIsWifiP2pEnabled(boolean enabled) {
     /*设备是否支持Wi-Fi Direct或者打开开关,通知一下*/
if (!enabled) {
new AlertDialog.Builder(this)
.setTitle(R.string.tips_error)
.setMessage(R.string.tips_disabled_wifi_p2p)
.setPositiveButton(R.string.tips_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
}).show();
}
} public WifiP2pManager.Channel getChannel() {
return mChannel;
}    /*发现周围设备了*/
@Override
public void onPeersAvailable(WifiP2pDeviceList peers) {
if(currentFragment instanceof DiscoverFragment) {
((DiscoverFragment)currentFragment).findDeviceList(peers.getDeviceList());
}
}    /*连接设备*/
public void connectDevice(WifiP2pConfig config){
mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
Toast.makeText(HandlerActivity.this, R.string.toast_connectSuccess, Toast.LENGTH_SHORT).show();
} @Override
public void onFailure(int reason) {
new AlertDialog.Builder(HandlerActivity.this)
.setTitle(R.string.tips_error)
.setMessage(WifiP2pUtils.getWifiP2pFailureReson(reason))
.setPositiveButton(R.string.tips_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();
}
});
}    /*连接完成,获取管理员的IP,跳转界面*/
@Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
InetAddress address = null;
boolean isGroupOwner = false;
if (info.groupFormed && info.isGroupOwner) {
address = info.groupOwnerAddress;
isGroupOwner = true;
} else if (info.groupFormed) {
address = info.groupOwnerAddress;
isGroupOwner = false;
}
if (null != address) {
Intent preIntent = getIntent();
preIntent.putExtra("address", address.getHostAddress());
preIntent.putExtra("isGroupOwner", isGroupOwner);
Fragment fragment=null;
setCurrentFragment(fragment);
connected=true;
}
} public void onConnectDisabled(){
connected=false;
}   public void setCurrentFragment(Fragment fragment){
currentFragment=fragment;
FragmentManager manager=getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.activity_content, fragment);
transaction.commit();
} }
 import android.app.AlertDialog;
import android.content.DialogInterface;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RadioGroup;
import android.widget.TextView; import com.mob.lee.fastair.BaseActivity;
import com.mob.lee.fastair.BaseFragment;
import com.mob.lee.fastair.R;
import com.mob.lee.scanview.ScanLayout; import java.util.ArrayList;
import java.util.Collection; import butterknife.ButterKnife;
import butterknife.InjectView; /**
* Created by Andy on 2016/5/10.
*/
public class DiscoverFragment extends BaseFragment{
@InjectView(R.id.fragment_discover_scanlayout)
ScanLayout mScanLayout; private WifiP2pDevice device;
private ArrayList<WifiP2pDevice> mDevices;
private boolean isBeginConnection; @Override
protected View initView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_discover,container,false);
ButterKnife.inject(this, view);
return view;
} @Override
protected void setListener(View view, Bundle saveInstanceState) {
/*连接某台设备*/
mScanLayout.setOnItemClickListener(new ScanLayout.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
device = mDevices.get(position);
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.PBC;
((HandlerActivity) getActivity()).connectDevice(config);
isBeginConnection=true;
}
});
} @Override
protected void setting(Bundle saveInstanceState) {
((BaseActivity)getActivity()).setToolbar(R.id.fragment_discover_toolbar, R.string.base_findDevice);
} @Override
public void onResume() {
super.onResume();
mScanLayout.startScan();
} @Override
public void onDestroyView() {
super.onDestroyView();
ButterKnife.reset(this);
} /*发现了设备,显示设备列表*/
public void findDeviceList(Collection<WifiP2pDevice> devices) {
if ((null == mDevices)) {
mDevices=new ArrayList<>();
}
mScanLayout.stopScan();
if (0 == devices.size()) {
new AlertDialog.Builder(getActivity())
.setTitle(R.string.tips_error)
.setMessage(R.string.tips_scan_failed)
.setPositiveButton(R.string.tips_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
getActivity().finish();
}
}).show();
} else if(isBeginConnection){
return;
}else{
for(WifiP2pDevice device:devices){
if(mDevices.contains(device)){
continue;
}
mDevices.add(device);
View view = LayoutInflater.from(getActivity()).inflate(R.layout.item_scan, null);
((TextView) view.findViewById(R.id.item_scan_name)).setText(device.deviceName);
mScanLayout.addView(view);
}
}
}
}

马上搞定Android平台的Wi-Fi Direct开发的更多相关文章

  1. 使用BleLib的轻松搞定Android低功耗蓝牙Ble 4.0开发具体解释

    转载请注明来源: http://blog.csdn.net/kjunchen/article/details/50909410 使用BleLib的轻松搞定Android低功耗蓝牙Ble 4.0开发具体 ...

  2. Android零基础入门第7节:搞定Android模拟器,开启甜蜜之旅

    原文:Android零基础入门第7节:搞定Android模拟器,开启甜蜜之旅 在前几期中总结分享了Android的前世今生.Android 系统架构和应用组件那些事.带你一起来聊一聊Android开发 ...

  3. 5分钟搞定android混淆(转)

    转自:https://www.jianshu.com/p/f3455ecaa56e 前言 混淆是上线前挺重要的一个环节.android使用的ProGuard,可以起到压缩,混淆,预检,优化的作用.但是 ...

  4. 五步搞定Android开发环境部署

    引言   在windows安装Android的开发环境不简单也说不上算复杂,本文写给第一次想在自己Windows上建立Android开发环境投入 Android浪潮的朋友们,为了确保大家能顺利完成开发 ...

  5. 五步搞定Android开发环境部署——非常详细的Android开发环境搭建教程

      在windows安装Android的开发环境不简单也说不上算复杂,本文写给第一次想在自己Windows上建立Android开发环境投入Android浪潮的朋友们,为了确保大家能顺利完成开发环境的搭 ...

  6. 《React Native 精解与实战》书籍连载「Android 平台与 React Native 混合开发」

    此文是我的出版书籍<React Native 精解与实战>连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理.React Native 组件布局.组件与 ...

  7. Android平台RTMP/RTSP播放器开发系列--解码和绘制

    本文主要抛砖引玉,粗略介绍下Android平台RTMP/RTSP播放器中解码和绘制相关的部分(Github). 解码 提到解码,大家都知道软硬解,甚至一些公司觉得硬解码已经足够通用,慢慢抛弃软解了,如 ...

  8. BaseHttpListActivity,几行代码搞定Android Http列表请求、加载和缓存

    Android开发中,向服务器请求一个列表并显示是非常常见的需求,但实现起来比较麻烦,代码繁杂. 随着应用的更新迭代,这种需求越来越多,我渐渐发现了实现这种需求的代码的共同点. 于是我将Activit ...

  9. 教你搞定Android自定义View

    Android App开发过程中,很多时候会遇到系统框架中提供的控件无法满足我们产品的设计需求,那么这时候我们可以选择先Google下有没有比较成熟的开源项目可以让我们用,当然现在Github上面的项 ...

随机推荐

  1. 29、activity横竖屏切换细节问题

    1 import android.app.Activity; import android.content.Intent; import android.os.Bundle; import andro ...

  2. Convert to Objective-C ARC

    今天在进行代码走查时,竟然发现了下面这段代码: Bad Code 顿时感觉吐槽无力,虽然我反复强调内存管理问题,无非就是谁申请谁释放,利用强弱引用避免 retain-cycles,但是还是会有这样那样 ...

  3. winform窗体间利用委托传值(一)

    前台:在winform窗体Form1放入pictureBox1 后台代码: namespace 点击小图变成大图 { public delegate void ClickDelegateHander( ...

  4. 【Spark学习】Spark 1.1.0 with CDH5.2 安装部署

    [时间]2014年11月18日 [平台]Centos 6.5 [工具]scp [软件]jdk-7u67-linux-x64.rpm spark-worker-1.1.0+cdh5.2.0+56-1.c ...

  5. [思维题]Bored Qishen

    给出一个整数集,其中包含1-n的所有整数,要求挑选出一个元素最多的子集,使得子集中任意两数的乘积不是完全平方数 (n<=10^6) 求这样一个最大子集的元素个数 #include <cst ...

  6. A Tour of Go Interfaces are satisfied implicitly

    A type implements an interface by implementing the methods. There is no explicit declaration of inte ...

  7. UVA 299 (13.07.30)

     Train Swapping  At an old railway station, you may still encounter one of the lastremaining ``train ...

  8. java properties 文件中书写相对路径

    工程src下的properties 文件要引用发布到D:\work\apache-tomcat-7.0.52\webapps\项目名称\certs这个地址下的文件,properties 中的文件路径应 ...

  9. [css]《CSS知多少》

    http://www.cnblogs.com/wangfupeng1988/p/4325007.html

  10. Mysql之二

    MySQL的数据类型 SQL接口 标准: ANSI SQL:SQL-86--------SQL-89--------SQL-99---------SQL-03 数据类型 字符型: CHAR(不区分字母 ...