Wifi-Direct
参考链接:http://developer.android.com/guide/topics/connectivity/wifip2p.html
国内镜像开发文档:http://wear.techbrood.com/guide/topics/connectivity/wifip2p.html
API:
WifiP2pManager类提供了很多方法允许用户通过设备的Wi-Fi模块来进行交互,比如做一些如发现,连接其他对等设备的事情。下列的方法都是可以使用的: 表格1.Wi-Fi直连技术方法
方法名 | 详细描述 |
---|---|
initialize() |
通过Wi-Fi框架对应用来进行注册。这个方法必须在任何其他Wi-Fi直连方法使用之前调用。 |
connect()] |
开始一个拥有特定设置的设备的点对点连接。 |
cancelConnect() |
取消任何一个正在进行的点对点组的连接。 |
requestConnectInfo() |
获取一个设备的连接信息。 |
createGroup() |
以当前设备为组拥有者来创建一个点对点连接组。 |
removeGroup() |
移除当前的点对点连接组。 |
requestGroupInfo() |
获取点对点连接组的信息。 |
discoverPeers() |
初始化对等设备的发现。 |
requestPeers() |
获取当前发现的对等设备列表。 |
WifiP2pManager的方法可以让你在一个监听器里传递参数,这样Wi-fi直连框架就可以通知给你的窗体这个方法调用的状态。可以被使用的监听器接口和使用监听器的相应的WifiP2pManager的方法的调用都将在下面这张表中有所描述:
表格 2. Wi-Fi直连监听器方法
监听器接口 | 相关联的方法 |
---|---|
WifiP2pManager.ActionListener |
connect(), cancelConnect(), createGroup(), removeGroup(), and discoverPeers() |
WifiP2pManager.ChannelListener |
initialize() |
WifiP2pManager.ConnectionInfoListener |
requestConnectInfo() |
WifiP2pManager.GroupInfoListener |
requestGroupInfo() |
WifiP2pManager.PeerListListener |
requestPeers() |
Wi-Fi直连技术的API定义了一些当特定的Wi-Fi直连事件发生时作为广播的意图,比如说当一个新的对等设备被发现,或者一个设备的Wi-Fi状态的改变。你可以在你的应用里通过创建一个处理这些意图的广播接收器来注册去接收这些意图。
Table 3. 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 |
当设备的详细信息改变的时候进行广播,比如设备的名称 |
创建一个广播接收器
一个广播接收器允许你接收由android系统发布的意图广播,这样你的应用就可以对那些你感兴趣的事件作出响应。创建一个基本的Wi-Fi直连意图使用的广播接收器的步骤如下:
1.创建一个继承自BroadcastReceiver的类。对于类的构造,一般最常用的就是以WifiP2pManager, WifiP2pManager.Channel作为参数,同时这个广播接收器对应的窗体也将被注册进来。这个广播接收器可以像窗体发送更新或者在需要的时候可以访问Wi-Fi硬件或通信通道。
2.在广播接收器里,处理onReceive()方法里你感兴趣的意图。执行接收到的意图的任何需要的动作。比如,广播接收器接收到一个WIFI_P2P_PEERS_CHANGED_ACTION的意图,你就要调用requestPeers()方法去获得当前发现的对等设备列表。
下面的代码展示了怎样去创建一个典型的广播接收器。广播接收器接收一个WifiP2pManager对象和一个窗体对象作为参数然后利用这两个类去处理接收到的意图的特定的动作需求。
1 @TargetApi(Build.VERSION_CODES.HONEYCOMB) 2 @SuppressLint("NewApi") 3 public class WiFiDirectBroadcastReceiver extends BroadcastReceiver { 4 5 private WifiP2pManager manager; 6 private Channel channel; 7 private WiFiDirectActivity activity; 8 9 10 public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel, 11 WiFiDirectActivity activity) { 12 super(); 13 this.manager = manager; 14 this.channel = channel; 15 this.activity = activity; 16 } 17 18 19 @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 20 @SuppressLint("NewApi") 21 @Override 22 public void onReceive(Context context, Intent intent) { 23 String action = intent.getAction(); 24 if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { 25 int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1); 26 if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) { 27 } else { 28 Log.d(WiFiDirectActivity.TAG, "无法使用Wifi-Direct"); 29 activity.setIsWifiP2pEnabled(false); 30 activity.resetData(); 31 32 } 33 Log.d(WiFiDirectActivity.TAG, "P2P state changed - " + state); 34 } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { 35 if (manager != null) { 36 manager.requestPeers(channel, (PeerListListener) activity.getFragmentManager() 37 .findFragmentById(R.id.frag_list)); 38 } 39 Log.d(WiFiDirectActivity.TAG, "P2P peers changed"); 40 } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { 41 if (manager == null) { 42 return; 43 } 44 45 NetworkInfo networkInfo = (NetworkInfo) intent 46 .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO); 47 48 if (networkInfo.isConnected()) { 49 50 // we are connected with the other device, request connection 51 // info to find group owner IP 52 53 DeviceDetailFragment fragment = (DeviceDetailFragment) activity 54 .getFragmentManager().findFragmentById(R.id.frag_detail); 55 manager.requestConnectionInfo(channel, fragment); 56 } else { 57 // It's a disconnect 58 activity.resetData(); 59 } 60 } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) { 61 DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager() 62 .findFragmentById(R.id.frag_list); 63 fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra( 64 WifiP2pManager.EXTRA_WIFI_P2P_DEVICE)); 65 66 } 67 } 68 }
发现对等设备
要发现可以使用并连接的对等设备,调用discoverPeers()方法去检测在范围内的可使用设备。这个方法的调用是异步的同时如果你创建了一个WifiP2pManager.ActionListener监听器的话你会通过onSuccess()或者onFailure()方法收到发现成功或失败的消息。
onSuccess()方法只能通知你发现的过程是否成功而不能提供任何关于发现设备的信息:
1 manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
2 @Override
3 public void onSuccess() {
4 ...
5 }
6
7 @Override
8 public void onFailure(int reasonCode) {
9 ...
10 }
11 });
连接到设备
当你已经找到你要连接的设备在获得发现设备列表之后,调用connect()方法去连接指定设备。这个方法的调用需要一个包含待连接设备信息的WifiP2pConfig对象。你可以通过WifiP2pManager.ActionListener接收到连接是否成功的通知。下面的代码展示了怎样去连接一个想得到的连接:
1 WifiP2pDevice device;
2 WifiP2pConfig config = new WifiP2pConfig();
3 config.deviceAddress = device.deviceAddress;
4 manager.connect(channel, config, new ActionListener() {
5
6 @Override
7 public void onSuccess() {
8 //success logic
9 }
10
11 @Override
12 public void onFailure(int reason) {
13 //failure logic
14 }
15 });
创建一个Wi-Fi直连的应用
创建一个Wi-Fi直连的应用包括创建和注册一个广播接收器,发现其他设备,连接其他设备,然后传输数据等步骤。接下来的几个部分描述了怎么去做这些工作。
初始化设置
在使用Wi-Fi直连的API之前,你必须确保你的应用可以访问设备的硬件并且你的设备要支持Wi-Fi直连的通讯协议。如果Wi-Fi直连技术是支持的,你可以获得一个WifiP2pManager的实例对象,然后创建并注册你的广播接收器,然后开始使用Wi-Fi直连的API方法。
1.为设备的Wi-Fi硬件获取权限并在Android的清单文件中声明你的应用正确使用的最低SDK版本:
1 <uses-sdk android:minSdkVersion="14" /> 2 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> 3 <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> 4 <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> 5 <uses-permission android:name="android.permission.INTERNET" /> 6 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
2.在你的窗体的onCreate()方法里,获得一个WifiP2pManager的实例并调用initialize()方法通过Wi-Fi直连框架去注册你的应用。这个方法返回一个WifiP2pManager.Channel对象,是被用来连接你的应用和Wi-Fi直连框架的。你应该再创建一个以WifiP2pManager和WifiP2pManager.Channel为参数且关联你的窗体的广播接收器的实例。这样你的广播接收器就可以接收到你感兴趣的事件去通知你的窗体并更新它。它还可以让你在需要的时候操纵设备的Wi-Fi状态。
1 WifiP2pManager mManager; 2 Channel mChannel; 3 BroadcastReceiver mReceiver; 4 ... 5 @Override 6 protected void onCreate(Bundle savedInstanceState){ 7 ... 8 mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); 9 mChannel = mManager.initialize(this, getMainLooper(), null); 10 mReceiver = new WiFiDirectBroadcastReceiver(manager, channel, this); 11 ... 12 }
3.创建一个意图过滤器并把它添加在你的广播接收器需要处理的意图上。
1 IntentFilter mIntentFilter; 2 ... 3 @Override 4 protected void onCreate(Bundle savedInstanceState){ 5 ... 6 mIntentFilter = new IntentFilter(); 7 mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); 8 mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); 9 mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); 10 mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); 11 ... 12 }
4.注册你的广播接收器在窗体的onResume()方法,解除注册在onPause()方法中。
1 @Override 2 protected void onResume() { 3 super.onResume(); 4 registerReceiver(mReceiver, mIntentFilter); 5 } 6 /* unregister the broadcast receiver */ 7 @Override 8 protected void onPause() { 9 super.onPause(); 10 unregisterReceiver(mReceiver); 11 }
当你获取到一个WifiP2pManager.Channel对象并且设置好你的广播接收器时,你的应用就可以调用Wi-Fi直连的方法并且可以接收Wi-Fi直连的意图。
你可以现在就通过调用WifiP2pManager中的方法取实现你的应用体验Wi-Fi直连技术的特性了。
下面是窗体Activity的完整代码:
1 @SuppressLint("NewApi") 2 public class WiFiDirectActivity extends Activity implements ChannelListener, DeviceActionListener { 3 4 public static final String TAG = "wifidirectdemo"; 5 private WifiP2pManager manager; 6 private boolean isWifiP2pEnabled = false; 7 private boolean retryChannel = false; 8 9 private final IntentFilter intentFilter = new IntentFilter(); 10 private Channel channel; 11 private BroadcastReceiver receiver = null; 12 13 public void setIsWifiP2pEnabled(boolean isWifiP2pEnabled) { 14 this.isWifiP2pEnabled = isWifiP2pEnabled; 15 } 16 17 @Override 18 public void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 setContentView(R.layout.main); 21 22 // add necessary intent values to be matched. 23 24 intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); 25 intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); 26 intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); 27 intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); 28 29 manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); 30 channel = manager.initialize(this, getMainLooper(), null); 31 } 32 33 /** register the BroadcastReceiver with the intent values to be matched */ 34 @Override 35 public void onResume() { 36 super.onResume(); 37 receiver = new WiFiDirectBroadcastReceiver(manager, channel, this); 38 registerReceiver(receiver, intentFilter); 39 } 40 41 @Override 42 public void onPause() { 43 super.onPause(); 44 unregisterReceiver(receiver); 45 } 46 47 /** 48 * Remove all peers and clear all fields. This is called on 49 * BroadcastReceiver receiving a state change event. 50 */ 51 public void resetData() { 52 DeviceListFragment fragmentList = (DeviceListFragment) getFragmentManager() 53 .findFragmentById(R.id.frag_list); 54 DeviceDetailFragment fragmentDetails = (DeviceDetailFragment) getFragmentManager() 55 .findFragmentById(R.id.frag_detail); 56 if (fragmentList != null) { 57 fragmentList.clearPeers(); 58 } 59 if (fragmentDetails != null) { 60 fragmentDetails.resetViews(); 61 } 62 } 63 64 @Override 65 public boolean onCreateOptionsMenu(Menu menu) { 66 MenuInflater inflater = getMenuInflater(); 67 inflater.inflate(R.menu.wi_fi_direct, menu); 68 return true; 69 } 70 71 @Override 72 public boolean onOptionsItemSelected(MenuItem item) { 73 switch (item.getItemId()) { 74 case R.id.atn_direct_enable: 75 if (manager != null && channel != null) { 76 77 // Since this is the system wireless settings activity, it's 78 // not going to send us a result. We will be notified by 79 // WiFiDeviceBroadcastReceiver instead. 80 81 startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS)); 82 } else { 83 Log.e(TAG, "channel or manager is null"); 84 } 85 return true; 86 87 case R.id.atn_direct_discover: 88 if (!isWifiP2pEnabled) { 89 Toast.makeText(WiFiDirectActivity.this, R.string.p2p_off_warning, 90 Toast.LENGTH_SHORT).show(); 91 return true; 92 } 93 final DeviceListFragment fragment = (DeviceListFragment) getFragmentManager() 94 .findFragmentById(R.id.frag_list); 95 fragment.onInitiateDiscovery(); 96 manager.discoverPeers(channel, new WifiP2pManager.ActionListener() { 97 98 @Override 99 public void onSuccess() { 100 Toast.makeText(WiFiDirectActivity.this, "Discovery Initiated", 101 Toast.LENGTH_SHORT).show(); 102 } 103 104 @Override 105 public void onFailure(int reasonCode) { 106 Toast.makeText(WiFiDirectActivity.this, "Discovery Failed : " + reasonCode, 107 Toast.LENGTH_SHORT).show(); 108 } 109 }); 110 return true; 111 default: 112 return super.onOptionsItemSelected(item); 113 } 114 } 115 116 @Override 117 public void showDetails(WifiP2pDevice device) { 118 DeviceDetailFragment fragment = (DeviceDetailFragment) getFragmentManager() 119 .findFragmentById(R.id.frag_detail); 120 fragment.showDetails(device); 121 122 } 123 124 @Override 125 public void connect(WifiP2pConfig config) { 126 manager.connect(channel, config, new ActionListener() { 127 128 @Override 129 public void onSuccess() { 130 // WiFiDirectBroadcastReceiver will notify us. Ignore for now. 131 Log.e(TAG, "Success!"); 132 } 133 134 @Override 135 public void onFailure(int reason) { 136 Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.", 137 Toast.LENGTH_SHORT).show(); 138 } 139 }); 140 } 141 142 @Override 143 public void disconnect() { 144 final DeviceDetailFragment fragment = (DeviceDetailFragment) getFragmentManager() 145 .findFragmentById(R.id.frag_detail); 146 fragment.resetViews(); 147 manager.removeGroup(channel, new ActionListener() { 148 149 @Override 150 public void onFailure(int reasonCode) { 151 Log.d(TAG, "Disconnect failed. Reason :" + reasonCode); 152 153 } 154 155 @Override 156 public void onSuccess() { 157 fragment.getView().setVisibility(View.GONE); 158 } 159 160 }); 161 } 162 163 @Override 164 public void onChannelDisconnected() { 165 // we will try once more 166 if (manager != null && !retryChannel) { 167 Toast.makeText(this, "Channel lost. Trying again", Toast.LENGTH_LONG).show(); 168 resetData(); 169 retryChannel = true; 170 manager.initialize(this, getMainLooper(), this); 171 } else { 172 Toast.makeText(this, 173 "Severe! Channel is probably lost premanently. Try Disable/Re-Enable P2P.", 174 Toast.LENGTH_LONG).show(); 175 } 176 } 177 178 @Override 179 public void cancelDisconnect() { 180 181 if (manager != null) { 182 final DeviceListFragment fragment = (DeviceListFragment) getFragmentManager() 183 .findFragmentById(R.id.frag_list); 184 if (fragment.getDevice() == null 185 || fragment.getDevice().status == WifiP2pDevice.CONNECTED) { 186 disconnect(); 187 } else if (fragment.getDevice().status == WifiP2pDevice.AVAILABLE 188 || fragment.getDevice().status == WifiP2pDevice.INVITED) { 189 190 manager.cancelConnect(channel, new ActionListener() { 191 192 @Override 193 public void onSuccess() { 194 Toast.makeText(WiFiDirectActivity.this, "Aborting connection", 195 Toast.LENGTH_SHORT).show(); 196 } 197 198 @Override 199 public void onFailure(int reasonCode) { 200 Toast.makeText(WiFiDirectActivity.this, 201 "Connect abort request failed. Reason Code: " + reasonCode, 202 Toast.LENGTH_SHORT).show(); 203 } 204 }); 205 } 206 } 207 208 } 209 }
显示设备连接的详细信息代码
1 @TargetApi(Build.VERSION_CODES.HONEYCOMB) 2 @SuppressLint("NewApi") 3 public class DeviceListFragment extends ListFragment implements PeerListListener { 4 5 private List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>(); 6 ProgressDialog progressDialog = null; 7 View mContentView = null; 8 private WifiP2pDevice device; 9 10 @Override 11 public void onActivityCreated(Bundle savedInstanceState) { 12 super.onActivityCreated(savedInstanceState); 13 this.setListAdapter(new WiFiPeerListAdapter(getActivity(), R.layout.row_devices, peers)); 14 15 } 16 17 @Override 18 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 19 mContentView = inflater.inflate(R.layout.device_list, null); 20 return mContentView; 21 } 22 23 public WifiP2pDevice getDevice() { 24 return device; 25 } 26 27 private static String getDeviceStatus(int deviceStatus) { 28 Log.d(WiFiDirectActivity.TAG, "Peer status :" + deviceStatus); 29 switch (deviceStatus) { 30 case WifiP2pDevice.AVAILABLE: 31 return "available"; 32 case WifiP2pDevice.INVITED: 33 return "invited"; 34 case WifiP2pDevice.CONNECTED: 35 return "connected"; 36 case WifiP2pDevice.FAILED: 37 return "failed"; 38 case WifiP2pDevice.UNAVAILABLE: 39 return "unavailable"; 40 default: 41 return "unknow"; 42 43 } 44 } 45 46 /** 47 * Initiate a connection with the peer. 48 */ 49 @Override 50 public void onListItemClick(ListView l, View v, int position, long id) { 51 WifiP2pDevice device = (WifiP2pDevice) getListAdapter().getItem(position); 52 ((DeviceActionListener) getActivity()).showDetails(device); 53 } 54 55 /** 56 * Array adapter for ListFragment that maintains WifiP2pDevice list. 57 */ 58 private class WiFiPeerListAdapter extends ArrayAdapter<WifiP2pDevice> { 59 60 private List<WifiP2pDevice> items; 61 62 public WiFiPeerListAdapter(Context context, int textViewResourceId, 63 List<WifiP2pDevice> objects) { 64 super(context, textViewResourceId, objects); 65 items = objects; 66 67 } 68 69 @Override 70 public View getView(int position, View convertView, ViewGroup parent) { 71 View v = convertView; 72 if (v == null) { 73 LayoutInflater vi = (LayoutInflater) getActivity().getSystemService( 74 Context.LAYOUT_INFLATER_SERVICE); 75 v = vi.inflate(R.layout.row_devices, null); 76 } 77 WifiP2pDevice device = items.get(position); 78 if (device != null) { 79 TextView top = (TextView) v.findViewById(R.id.device_name); 80 TextView bottom = (TextView) v.findViewById(R.id.device_details); 81 if (top != null) { 82 top.setText(device.deviceName); 83 } 84 if (bottom != null) { 85 bottom.setText(getDeviceStatus(device.status)); 86 } 87 } 88 89 return v; 90 91 } 92 } 93 94 public void updateThisDevice(WifiP2pDevice device) { 95 this.device = device; 96 TextView view = (TextView) mContentView.findViewById(R.id.my_name); 97 view.setText(device.deviceName); 98 view = (TextView) mContentView.findViewById(R.id.my_status); 99 view.setText(getDeviceStatus(device.status)); 100 } 101 102 @Override 103 public void onPeersAvailable(WifiP2pDeviceList peerList) { 104 if (progressDialog != null && progressDialog.isShowing()) { 105 progressDialog.dismiss(); 106 } 107 peers.clear(); 108 peers.addAll(peerList.getDeviceList()); 109 110 if (peers.size() == 0) { 111 Log.d(WiFiDirectActivity.TAG, "size==0"); 112 return; 113 } 114 115 } 116 117 public void clearPeers() { 118 peers.clear(); 119 ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged(); 120 } 121 122 public void onInitiateDiscovery() { 123 if (progressDialog != null && progressDialog.isShowing()) { 124 progressDialog.dismiss(); 125 } 126 progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel", "finding peers", true, 127 true, new DialogInterface.OnCancelListener() { 128 129 @Override 130 public void onCancel(DialogInterface dialog) { 131 132 } 133 }); 134 } 135 136 public interface DeviceActionListener { 137 138 void showDetails(WifiP2pDevice device); 139 140 void cancelDisconnect(); 141 142 void connect(WifiP2pConfig config); 143 144 void disconnect(); 145 } 146 147 }
数据传输
一旦连接已经建立,你可以通过套接字来进行数据的传输。基本的数据传输步骤如下:
1.创建一个ServerSocket对象。这个服务端套接字对象等待一个来自指定地址和端口的客户端的连接且阻塞线程直到连接发生,所以把它建立在一个后台线程里。
2.创建一个客户端Socket.这个客户端套接字对象使用指定ip地址和端口去连接服务端设备。
3.服务端等待客户端的连接(使用accept()方法)。这个调用阻塞服务端线程直到客户端连接上,所以叫这个过程一个新的线程。当连接建立时,服务端可以接受来自客户端的数据。执行关于数据的任何动作,比如保存数据或者展示给用户。
1 @Override 2 protected String doInBackground(Void... params) { 3 try { 4 ServerSocket serverSocket = new ServerSocket(8888); 5 //服务器端口号 6 Log.d(WiFiDirectActivity.TAG, "Server: Socket opened"); 7 Socket client = serverSocket.accept(); 8 //客户端绑定服务器端口 9 //!!!!!!!!!!使用accept方法等待客户机发送数据 10 Log.d(WiFiDirectActivity.TAG, "Server: connection done"); 11 12 13 final File f = new File(Environment.getExternalStorageDirectory() + "/" 14 + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis() 15 + ".jpg"); 16 17 File dirs = new File(f.getParent()); 18 if (!dirs.exists()) 19 dirs.mkdirs(); 20 f.createNewFile(); 21 22 Log.d(WiFiDirectActivity.TAG, "server: copying files " + f.toString()); 23 InputStream inputstream = client.getInputStream(); 24 copyFile(inputstream, new FileOutputStream(f)); 25 serverSocket.close(); 26 return f.getAbsolutePath(); 27 } catch (IOException e) { 28 Log.e(WiFiDirectActivity.TAG, e.getMessage()); 29 return null; 30 } 31 }
4.服务端与客户端,官方demo只给出了客户端向服务端发送数据的方式。
1 @Override 2 public void onConnectionInfoAvailable(final WifiP2pInfo info) { 3 if (progressDialog != null && progressDialog.isShowing()) { 4 progressDialog.dismiss(); 5 } 6 this.info = info; 7 this.getView().setVisibility(View.VISIBLE); 8 9 // The owner IP is now known. 10 TextView view = (TextView) mContentView.findViewById(R.id.group_owner); 11 view.setText(getResources().getString(R.string.group_owner_text) 12 + ((info.isGroupOwner == true) ? getResources().getString(R.string.yes) 13 : getResources().getString(R.string.no))); 14 15 // InetAddress from WifiP2pInfo struct. 16 view = (TextView) mContentView.findViewById(R.id.device_info); 17 view.setText("群主IP - " + info.groupOwnerAddress.getHostAddress()); 18 19 // After the group negotiation, we assign the group owner as the file 20 // server. The file server is single threaded, single connection server 21 // socket. 22 if (info.groupFormed && info.isGroupOwner) { 23 new FileServerAsyncTask(getActivity(), mContentView.findViewById(R.id.status_text)) 24 .execute(); 25 } else if (info.groupFormed) { 26 mContentView.findViewById(R.id.btn_start_client).setVisibility(View.VISIBLE); 27 ((TextView) mContentView.findViewById(R.id.status_text)).setText(getResources() 28 .getString(R.string.client_text)); 29 } 30 31 // hide the connect button 32 mContentView.findViewById(R.id.btn_connect).setVisibility(View.GONE); 33 }
完整代码如下:
1 @TargetApi(Build.VERSION_CODES.HONEYCOMB) 2 @SuppressLint("NewApi") 3 public class DeviceDetailFragment extends Fragment implements ConnectionInfoListener { 4 5 protected static final int CHOOSE_FILE_RESULT_CODE = 20; 6 private View mContentView = null; 7 private WifiP2pDevice device; 8 private WifiP2pInfo info; 9 ProgressDialog progressDialog = null; 10 11 @Override 12 public void onActivityCreated(Bundle savedInstanceState) { 13 super.onActivityCreated(savedInstanceState); 14 } 15 16 @Override 17 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 18 19 mContentView = inflater.inflate(R.layout.device_detail, null); 20 mContentView.findViewById(R.id.btn_connect).setOnClickListener(new View.OnClickListener() { 21 22 @Override 23 public void onClick(View v) { 24 WifiP2pConfig config = new WifiP2pConfig(); 25 config.deviceAddress = device.deviceAddress; 26 config.wps.setup = WpsInfo.PBC; 27 if (progressDialog != null && progressDialog.isShowing()) { 28 progressDialog.dismiss(); 29 } 30 progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel", 31 "Connecting to :" + device.deviceAddress, true, true); 32 } 33 }); 34 35 mContentView.findViewById(R.id.btn_disconnect).setOnClickListener( 36 new View.OnClickListener() { 37 38 @Override 39 public void onClick(View v) { 40 ((DeviceActionListener) getActivity()).disconnect(); 41 } 42 }); 43 44 mContentView.findViewById(R.id.btn_start_client).setOnClickListener( 45 new View.OnClickListener() { 46 47 @Override 48 public void onClick(View v) { 49 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 50 intent.setType("image/*"); 51 startActivityForResult(intent, CHOOSE_FILE_RESULT_CODE); 52 } 53 }); 54 55 return mContentView; 56 } 57 58 @Override 59 public void onActivityResult(int requestCode, int resultCode, Intent data) { 60 61 // User has picked an image. Transfer it to group owner i.e peer using 62 // FileTransferService. 63 Uri uri = data.getData(); 64 TextView statusText = (TextView) mContentView.findViewById(R.id.status_text); 65 statusText.setText("Sending: " + uri); 66 Log.d(WiFiDirectActivity.TAG, "Intent----------- " + uri); 67 Intent serviceIntent = new Intent(getActivity(), FileTransferService.class); 68 serviceIntent.setAction(FileTransferService.ACTION_SEND_FILE); 69 serviceIntent.putExtra(FileTransferService.EXTRAS_FILE_PATH, uri.toString()); 70 serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_ADDRESS, 71 info.groupOwnerAddress.getHostAddress()); 72 serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_PORT, 8888); 73 getActivity().startService(serviceIntent); 74 } 75 76 @Override 77 public void onConnectionInfoAvailable(final WifiP2pInfo info) { 78 if (progressDialog != null && progressDialog.isShowing()) { 79 progressDialog.dismiss(); 80 } 81 this.info = info; 82 this.getView().setVisibility(View.VISIBLE); 83 84 // The owner IP is now known. 85 TextView view = (TextView) mContentView.findViewById(R.id.group_owner); 86 view.setText(getResources().getString(R.string.group_owner_text) 87 + ((info.isGroupOwner == true) ? getResources().getString(R.string.yes) 88 : getResources().getString(R.string.no))); 89 90 // InetAddress from WifiP2pInfo struct. 91 view = (TextView) mContentView.findViewById(R.id.device_info); 92 view.setText("群主IP - " + info.groupOwnerAddress.getHostAddress()); 93 94 // After the group negotiation, we assign the group owner as the file 95 // server. The file server is single threaded, single connection server 96 // socket. 97 if (info.groupFormed && info.isGroupOwner) { 98 new FileServerAsyncTask(getActivity(), mContentView.findViewById(R.id.status_text)) 99 .execute(); 100 } else if (info.groupFormed) { 101 mContentView.findViewById(R.id.btn_start_client).setVisibility(View.VISIBLE); 102 ((TextView) mContentView.findViewById(R.id.status_text)).setText(getResources() 103 .getString(R.string.client_text)); 104 } 105 106 // hide the connect button 107 mContentView.findViewById(R.id.btn_connect).setVisibility(View.GONE); 108 } 109 110 /** 111 * Updates the UI with device data 112 * 113 * @param device the device to be displayed 114 */ 115 public void showDetails(WifiP2pDevice device) { 116 this.device = device; 117 this.getView().setVisibility(View.VISIBLE); 118 TextView view = (TextView) mContentView.findViewById(R.id.device_address); 119 view.setText(device.deviceAddress); 120 view = (TextView) mContentView.findViewById(R.id.device_info); 121 view.setText(device.toString()); 122 123 } 124 125 /** 126 * Clears the UI fields after a disconnect or direct mode disable operation. 127 */ 128 public void resetViews() { 129 mContentView.findViewById(R.id.btn_connect).setVisibility(View.VISIBLE); 130 TextView view = (TextView) mContentView.findViewById(R.id.device_address); 131 view.setText(R.string.empty); 132 view = (TextView) mContentView.findViewById(R.id.device_info); 133 view.setText(R.string.empty); 134 view = (TextView) mContentView.findViewById(R.id.group_owner); 135 view.setText(R.string.empty); 136 view = (TextView) mContentView.findViewById(R.id.status_text); 137 view.setText(R.string.empty); 138 mContentView.findViewById(R.id.btn_start_client).setVisibility(View.GONE); 139 this.getView().setVisibility(View.GONE); 140 } 141 142 public static class FileServerAsyncTask extends AsyncTask<Void, Void, String> { 143 144 private Context context; 145 private TextView statusText; 146 147 public FileServerAsyncTask(Context context, View statusText) { 148 this.context = context; 149 this.statusText = (TextView) statusText; 150 } 151 152 @Override 153 protected String doInBackground(Void... params) { 154 try { 155 ServerSocket serverSocket = new ServerSocket(8888); 156 //服务器端口号 157 Log.d(WiFiDirectActivity.TAG, "Server: Socket opened"); 158 Socket client = serverSocket.accept(); 159 //客户端绑定服务器端 160 //!!!!!!!!!!使用accept方法等待客户机发送数据 161 Log.d(WiFiDirectActivity.TAG, "Server: connection done"); 162 163 164 final File f = new File(Environment.getExternalStorageDirectory() + "/" 165 + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis() 166 + ".jpg"); 167 168 File dirs = new File(f.getParent()); 169 if (!dirs.exists()) 170 dirs.mkdirs(); 171 f.createNewFile(); 172 173 Log.d(WiFiDirectActivity.TAG, "server: copying files " + f.toString()); 174 InputStream inputstream = client.getInputStream(); 175 copyFile(inputstream, new FileOutputStream(f)); 176 serverSocket.close(); 177 return f.getAbsolutePath(); 178 } catch (IOException e) { 179 Log.e(WiFiDirectActivity.TAG, e.getMessage()); 180 return null; 181 } 182 } 183 184 @Override 185 protected void onPostExecute(String result) { 186 if (result != null) { 187 statusText.setText("File copied - " + result); 188 Intent intent = new Intent(); 189 intent.setAction(android.content.Intent.ACTION_VIEW); 190 intent.setDataAndType(Uri.parse("file://" + result), "image/*"); 191 context.startActivity(intent); 192 } 193 194 } 195 196 @Override 197 protected void onPreExecute() { 198 statusText.setText("Opening a server socket"); 199 } 200 201 } 202 203 public static boolean copyFile(InputStream inputStream, OutputStream out) { 204 byte buf[] = new byte[1024]; 205 int len; 206 try { 207 while ((len = inputStream.read(buf)) != -1) { 208 out.write(buf, 0, len); 209 210 } 211 out.close(); 212 inputStream.close(); 213 } catch (IOException e) { 214 Log.d(WiFiDirectActivity.TAG, e.toString()); 215 return false; 216 } 217 return true; 218 } 219 220 }
文件传输代码如下:
1 public class FileTransferService extends IntentService { 2 3 private static final int SOCKET_TIMEOUT = 5000; 4 public static final String ACTION_SEND_FILE = "com.example.android.wifidirect.SEND_FILE"; 5 public static final String EXTRAS_FILE_PATH = "file_url"; 6 public static final String EXTRAS_GROUP_OWNER_ADDRESS = "go_host"; 7 public static final String EXTRAS_GROUP_OWNER_PORT = "go_port"; 8 9 public FileTransferService(String name) { 10 super(name); 11 } 12 13 public FileTransferService() { 14 super("FileTransferService"); 15 } 16 17 @Override 18 protected void onHandleIntent(Intent intent) { 19 20 Context context = getApplicationContext(); 21 if (intent.getAction().equals(ACTION_SEND_FILE)) { 22 String fileUri = intent.getExtras().getString(EXTRAS_FILE_PATH); 23 String host = intent.getExtras().getString(EXTRAS_GROUP_OWNER_ADDRESS); 24 Socket socket = new Socket(); 25 int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT); 26 27 try { 28 Log.d(WiFiDirectActivity.TAG, "Opening client socket - "); 29 socket.bind(null); 30 socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT); 31 32 Log.d(WiFiDirectActivity.TAG, "Client socket - " + socket.isConnected()); 33 OutputStream stream = socket.getOutputStream(); 34 ContentResolver cr = context.getContentResolver(); 35 InputStream is = null; 36 try { 37 is = cr.openInputStream(Uri.parse(fileUri)); 38 } catch (FileNotFoundException e) { 39 Log.d(WiFiDirectActivity.TAG, e.toString()); 40 } 41 DeviceDetailFragment.copyFile(is, stream); 42 Log.d(WiFiDirectActivity.TAG, "Client: Data written"); 43 } catch (IOException e) { 44 Log.e(WiFiDirectActivity.TAG, e.getMessage()); 45 } finally { 46 if (socket != null) { 47 if (socket.isConnected()) { 48 try { 49 socket.close(); 50 } catch (IOException e) { 51 // Give up 52 e.printStackTrace(); 53 } 54 } 55 } 56 } 57 58 } 59 60 } 61 }
作者:pngcui
博客园:http://www.cnblogs.com/pngcui/
github:https://github.com/pngcui
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明。