Android WifiDirect学习(一)
2015-07-07 10:58
441 查看
WiFiDirect基本介绍
Wi-FiDirect标准允许无线网络中的设备无需通过无线路由器即可相互连接。与蓝牙技术类似,这种标准允许无线设备以点对点形式互连,不过在传输速度与传输距离方面则比蓝牙有大幅提升。Wi-FiDirect可以支持一对一直连,也可以实现多台设备同时连接
WiFiDirect一对一搜索/连接/传输基本流程
第一步:初始化WifiDirect模块,一般情况下,只要打开Wifi,WifiDirect就会处于激活状态
第二步:WifiDirect进行搜索状态,只有处于Wifidirect搜索状态的手机才能被其他手机搜索到。
第三步:连接搜索到的手机,建立连接。连接一旦建立,就会停止搜索。
第四步:在建立好连接的网络基础上进行网络传输(socket通信)
第五步:传输完毕后,可以根据需要继续进行传输或者断开连接
AndroidWiFiDirect
Android4.0(APIlevel14)之后版本支持AndroidWiFiDirect基本使用方法
AndroidWiFiDirectAPI包含以下主要部分:允许用户发现,请求然后连接对等设备的各种方法,定义在
允许用户定义收到调用
通知用户被Wi-Fi直连技术框架检测到的特定事件的Intent,比如一个已丢掉的连接或者一个新的Peer的发现等。
AndroidWiFiDirectAPI概述
表格1.Wi-Fi直连技术方法
方法名 | 详细描述 |
initialize() | 通过Wi-Fi框架对应用来进行注册。这个方法必须在任何其他Wi-Fi直连方法使用之前调用。 |
connect()] | 开始一个拥有特定设置的设备的点对点连接。 |
cancelConnect() | 取消任何一个正在进行的点对点组的连接。 |
requestConnectInfo() | 获取一个设备的连接信息。 |
createGroup() | 以当前设备为组拥有者来创建一个点对点连接组。 |
removeGroup() | 移除当前的点对点连接组。 |
requestGroupInfo() | 获取点对点连接组的信息。 |
discoverPeers() | 初始化对等设备的发现。 |
requestPeers() | 获取当前发现的对等设备列表。 |
可以被使用的监听器接口和使用监听器的相应的
表格2.Wi-Fi直连监听器方法
监听器接口 | 相关联的方法 |
WifiP2pManager.ActionListener | connect(),cancelConnect(),createGroup(),removeGroup(),anddiscoverPeers() |
WifiP2pManager.ChannelListener | initialize() |
WifiP2pManager.ConnectionInfoListener | requestConnectInfo() |
WifiP2pManager.GroupInfoListener | requestGroupInfo() |
WifiP2pManager.PeerListListener | requestPeers() |
Table3.Wi-Fi直连意图
意图名称 | 详细描述 |
WIFI_P2P_CONNECTION_CHANGED_ACTION | 当设备的Wi-Fi连接信息状态改变时候进行广播。 |
WIFI_P2P_PEERS_CHANGED_ACTION | 当调用discoverPeers()方法的时候进行广播。在你的应用里处理此意图时,你通常会调用requestPeers()去获得对等设备列表的更新。 |
WIFI_P2P_STATE_CHANGED_ACTION | 当设备的Wi-Fi直连功能打开或关闭时进行广播。 |
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION | 当设备的详细信息改变的时候进行广播,比如设备的名称 |
创建一个Wi-Fi直连的应用
创建一个Wi-Fi直连的应用包括创建和注册一个BroadcastReceiver,发现其他设备,连接其他设备,然后传输数据等步骤。接下来的几个部分描述了怎么去做这些工作。初始化设置
在使用Wi-Fi直连的API之前,你必须确保你的应用可以访问设备的硬件并且你的设备要支持Wi-Fi直连的通讯协议。如果Wi-Fi直连技术是支持的,你可以获得一个WifiP2pManager的实例对象,然后创建并注册你的BroadcastReceiver,然后开始使用WiFiDirect的API方法。
1.为设备的Wi-Fi硬件获取权限并在Android的清单文件中声明你的应用正确使用的最低SDK版本:
<uses-sdkandroid:minSdkVersion="14"/> <uses-permissionandroid:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permissionandroid:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permissionandroid:name="android.permission.CHANGE_NETWORK_STATE"/> <uses-permissionandroid:name="android.permission.INTERNET"/> <uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/>
2.检查设备是否支持Wi-Fi直连技术。一种好的解决办法是当你的BrocastReceiver接收到一个WIFI_P2P_STATE_CHANGED_ACTIONIntent。通知你的ActivityWi-Fi直连的状态和相应的反应。
@Override
publicvoidonReceive(Contextcontext,Intentintent){
...
Stringaction=intent.getAction();
if(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)){
intstate=intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,-1);
if(state==WifiP2pManager.WIFI_P2P_STATE_ENABLED){
//WifiDirectisenabled
}else{
//Wi-FiDirectisnotenabled
}
}
...
}
3.在你的窗体的onCreate()方法里,获得一个WifiP2pManager的实例并调用initialize()方法通过WiFiDirect框架去注册你的应用。这个方法返回一个WifiP2pManager.Channel对象,是被用来连接你的应用和WiFiDirect框架的。你应该再创建一个以WifiP2pManager和WifiP2pManager.Channel为参数且关联你的Activity的BroadcastRecevier的实例。这样你的BroadcastRecevier就可以接收到你感兴趣的事件去通知你的Activity并更新它。它还可以让你在需要的时候操纵设备的Wi-Fi状态。
WifiP2pManagermManager;
ChannelmChannel;
BroadcastReceivermReceiver;
...
@Override
protectedvoidonCreate(BundlesavedInstanceState){
...
mManager=(WifiP2pManager)getSystemService(Context.WIFI_P2P_SERVICE);
mChannel=mManager.initialize(this,getMainLooper(),null);
mReceiver=newWiFiDirectBroadcastReceiver(manager,channel,this);
...
}
4.创建一个IntentFilter并把它添加在你的BroadcastReceiver需要处理的Intent上。
IntentFiltermIntentFilter;
...
@Override
protectedvoidonCreate(BundlesavedInstanceState){
...
mIntentFilter=newIntentFilter();
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.注册你的BroadcastReceiver在Acitivity的onResume()方法,解除注册在onPause()方法中。(BroadcastReceiver的register和unregister根据需要可放在不同的位置)
/*registerthebroadcastreceiverwiththeintentvaluestobematched*/
@Override
protectedvoidonResume(){
super.onResume();
registerReceiver(mReceiver,mIntentFilter);
}
/*unregisterthebroadcastreceiver*/
@Override
protectedvoidonPause(){
super.onPause();
unregisterReceiver(mReceiver);
}
当你获取到一个WifiP2pManager.Channel对象并且设置好你的BroadcastReceiver时,你的应用就可以调用Wi-Fi直连的方法并且可以接收Wi-Fi直连的Intent。
你可以现在就通过调用WifiP2pManager中的方法取实现你的应用体验Wi-Fi直连技术的特性了。
下面的章节描述了怎样去实现一些常用的操作,比如说发现其他设备(搜索)和连接它们。
发现对等设备
要发现可以使用并连接的对等设备,调用discoverPeers()方法去检测在范围内的可使用设备。这个方法的调用是异步的同时如果你创建了一个WifiP2pManager.ActionListener监听器的话你会通过onSuccess()或者onFailure()方法收到发现成功或失败的消息。onSuccess()方法只能通知你发现的过程是否成功而不能提供任何关于发现设备的信息:
manager.discoverPeers(channel,newWifiP2pManager.ActionListener(){
@Override
publicvoidonSuccess(){
...
}
@Override
publicvoidonFailure(intreasonCode){
...
}
});
如果发现过程成功且检测到了对等设备,系统将会广播出一个WIFI_P2P_PEERS_CHANGED_ACTIONIntent,这样你就可以利用BroadcastReceiver监听并获得发现设备的列表。当你的应用接收到WIFI_P2P_PEERS_CHANGED_ACTIONIntent时,你就可以调用requestPeers()方法来获取发现设备的列表,代码如下:
PeerListListenermyPeerListListener;
...
if(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)){
//requestavailablepeersfromthewifip2pmanager.Thisisan
//asynchronouscallandthecallingactivityisnotifiedwitha
//callbackonPeerListListener.onPeersAvailable()
if(manager!=null){
manager.requestPeers(channel,myPeerListListener);
}
}
连接到设备
当你已经找到你要连接的设备在获得发现设备列表之后,调用connect()方法去连接指定设备。这个方法的调用需要一个包含待连接设备信息的WifiP2pConfig对象。你可以通过WifiP2pManager.ActionListener接收到连接是否成功的通知。
下面的代码展示了怎样去连接一个想得到的连接:
//obtainapeerfromtheWifiP2pDeviceList
WifiP2pDevicedevice;
WifiP2pConfigconfig=newWifiP2pConfig();
config.deviceAddress=device.deviceAddress;
manager.connect(channel,config,newActionListener(){
@Override
publicvoidonSuccess(){
//successlogic
}
@Override
publicvoidonFailure(intreason){
//failurelogic
}
});
数据传输
WiFiDirect连接成功后,可以收到系统广播WIFI_P2P_CONNECTION_CHANGED_ACTION,此时调用requestConnectionInfo,会异步回调方法onConnectionInfoAvailable(finalWifiP2pInfoinfo)。在WifiP2pInfo中,我们可以得知isGroupOwner(是不是GO),groupOwnerAddress(GO的IP地址是多少)。
此处注意:GC可以知道GO的地址,而GO是不知道GC的地址的。因此,一般的Socket编程思路是,GO做Server端,GC做Client端。
一旦连接已经建立,你可以通过Socket来进行数据的传输。基本的数据传输步骤如下(google官方例子,也可以根据需要使用适用Socket通讯的其他方法协议,如HTTP,FTP协议):
1.创建一个ServerSocket对象。这个服务端Socket对象等待一个来自指定地址和端口的客户端的连接且阻塞线程直到连接发生,所以把它建立在一个后台线程里。
2.创建一个客户端Socket.这个客户端Socket对象使用指定ip地址和端口去连接服务端设备。(IP地址从上面的groupOwnerAddress获得,端口号由server和client两端通讯之前确定)
3.从客户端给服务端发送数据。当客户端成功连接服务端设备后,你可以通过字节流从客户端给服务端发送数据。
4.服务端等待客户端的连接(使用accept()方法)。这个调用阻塞服务端线程直到客户端连接上,所以叫这个过程一个新的线程。当连接建立时,服务端可以接受来自客户端的数据。执行关于数据的任何动作,比如保存数据或者展示给用户。
下来的例子,展示了怎样去创建服务端和客户端的连接和通信,并且使用一个客户端到服务端的服务来传输了一张JPEG图像。如。
publicstaticclassFileServerAsyncTaskextendsAsyncTask{
privateContextcontext;
privateTextViewstatusText;
publicFileServerAsyncTask(Contextcontext,ViewstatusText){
this.context=context;
this.statusText=(TextView)statusText;
}
@Override
protectedStringdoInBackground(Void...params){
try{
/**
*Createaserversocketandwaitforclientconnections.This
*callblocksuntilaconnectionisacceptedfromaclient
*/
ServerSocketserverSocket=newServerSocket(8888);
Socketclient=serverSocket.accept();
/**
*Ifthiscodeisreached,aclienthasconnectedandtransferreddata
*SavetheinputstreamfromtheclientasaJPEGfile
*/
finalFilef=newFile(Environment.getExternalStorageDirectory()+"/"
+context.getPackageName()+"/wifip2pshared-"+System.currentTimeMillis()
+".jpg");
Filedirs=newFile(f.getParent());
if(!dirs.exists())
dirs.mkdirs();
f.createNewFile();
InputStreaminputstream=client.getInputStream();
copyFile(inputstream,newFileOutputStream(f));
serverSocket.close();
returnf.getAbsolutePath();
}catch(IOExceptione){
Log.e(WiFiDirectActivity.TAG,e.getMessage());
returnnull;
}
}
/**
*StartactivitythatcanhandletheJPEGimage
*/
@Override
protectedvoidonPostExecute(Stringresult){
if(result!=null){
statusText.setText("Filecopied-"+result);
Intentintent=newIntent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://"+result),"image/*");
context.startActivity(intent);
}
}
}
在客户端,使用客户端套接字连接服务端套接字并传输数据。这个例子从客户端的文件系统里传输了一张JPEG的图像到服务端。
Contextcontext=this.getApplicationContext();
Stringhost;
intport;
intlen;
Socketsocket=newSocket();
bytebuf[]=newbyte[1024];
...
try{
/**
*Createaclientsocketwiththehost,
*port,andtimeoutinformation.
*/
socket.bind(null);
socket.connect((newInetSocketAddress(host,port)),500);
/**
*CreateabytestreamfromaJPEGfileandpipeittotheoutputstream
*ofthesocket.Thisdatawillberetrievedbytheserverdevice.
*/
OutputStreamoutputStream=socket.getOutputStream();
ContentResolvercr=context.getContentResolver();
InputStreaminputStream=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(FileNotFoundExceptione){
//catchlogic
}catch(IOExceptione){
//catchlogic
}
/**
*Cleanupanyopensocketswhendone
*transferringorifanexceptionoccurred.
*/
finally{
if(socket!=null){
if(socket.isConnected()){
try{
socket.close();
}catch(IOExceptione){
//catchlogic
}
}
}
}
参考文章
相关文章推荐
- android 下自定义view, android.view.InflateException
- Java中的Timer和TimerTask在Android中的用法
- Android adt-bundle下载链接
- 开源:矿Android新闻client,快、小、支持离线阅读、操作简单、内容丰富,形式多样展示、的信息量、全功能 等待(离开码邮箱)
- android带圆角textview和圆角的图片
- 自定义android进度条
- android studio 代码混淆如何忽略第三方jar包
- Android源码分析-Context
- Android跨语言篇
- android 开发 知识点
- Android学习之LayoutInflater类和inflate()方法的使用
- Android获取mac地址
- Android 检查设备是否存在 导航栏 NavigationBar
- Android Studio快捷键
- Android Studio导入第三方类库的方法
- [转]Android重力感应开发
- Android图片淡化 透明度 设置Alpha值
- android 存储5种方式
- android api 说明
- android studio上的百度定位的初始化