Android WiFi直连并互发图片

非淡泊无以明志,非宁静无以致远。这篇文章主要讲述Android WiFi直连并互发图片相关的知识,希望能为你提供帮助。
      WiFi直连可以在不通过网络或热点的情况下,直接与周围的设备进行连接并进行信息交换。WiFi直连是在android 4.0(API level 14)或更高的版本中才加入的新功能。本文主要介绍通过Wi-Fi Direct查找附近的设备,实现连接并完成图片的发送。一般包括如下几个步骤:
? 在AndroidManifest文件中声明相关权限
? 创建对等网络管理器(WifiP2pManager),注册相关广播监听WiFi直连状态
? 创建MyAdapter,用于显示搜索到的对等点列表,监听点击连接事件
? 开始对对等点的搜索,获取对等点列表
? 连接一个对等点
? 判断一方为组拥有者(GroupOwner),提交接收图片的异步任务
? 调用系统图库选择图片,开启后台服务将其发送
? 分别设作为组拥有者,完成图片的互发
 
在AndroidManifest文件中声明相关权限

< uses-sdk android:minSdkVersion="14" /> < uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> < uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> < uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> < uses-permission android:name="android.permission.INTERNET" /> < uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> < uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> < uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

      WiFi直连是在api level 14及更高的版本才能使用,所以要声明android:minSdkVersion="14"。
 
创建对等网络管理器(WifiP2pManager),注册相关广播监听WiFi直连状态
      使用WiFi直连,需要监听如下动作:
WIFI_P2P_STATE_CHANGED_ACTION — 表明Wi-Fi对等网络(P2P)是否已经启用

WIFI_P2P_PEERS_CHANGED_ACTION — 表明可用的对等点的列表发生了改变

WIFI_P2P_CONNECTION_CHANGED_ACTION — 表明Wi-Fi对等网络的连接状态发生了改变

WIFI_P2P_THIS_DEVICE_CHANGED_ACTION — 表明该设备的配置信息发生了改变

private BroadcastReceiver mReceiver; private IntentFilter mFilter; private void initIntentFilter() { mFilter = new IntentFilter(); mFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); mFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); mFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); mFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); }
mReceiver = new WifiDirectBroadcastReceiver(mManager, mChannel, this, mPeerListListerner, mInfoListener);
@Override protected void onResume() { super.onResume(); registerReceiver(mReceiver, mFilter); } @Override public void onPause() { super.onPause(); unregisterReceiver(mReceiver); }

广播接收
public class WifiDirectBroadcastReceiver extends BroadcastReceiver {private WifiP2pManager mManager; private WifiP2pManager.Channel mChannel; private Activity mActivity; private WifiP2pManager.PeerListListener mPeerListListener; private WifiP2pManager.ConnectionInfoListener mInfoListener; public WifiDirectBroadcastReceiver(WifiP2pManager manager, WifiP2pManager.Channel channel, Activity activity, WifiP2pManager.PeerListListener peerListListener, WifiP2pManager.ConnectionInfoListener infoListener ) { this.mManager = manager; this.mChannel = channel; this.mPeerListListener = peerListListener; this.mActivity = activity; this.mInfoListener = infoListener; }@Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); /*check if the wifi is enable*/ if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1); } /*get the list*/ else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { mManager.requestPeers(mChannel, mPeerListListener); } /*Respond to new connection or disconnections*/ else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { if (mManager == null) { return; } NetworkInfo networkInfo = (NetworkInfo) intent .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO); if (networkInfo.isConnected()) { mManager.requestConnectionInfo(mChannel, mInfoListener); } else {return; } } /*Respond to this device‘s wifi state changing*/ else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {} } }

 
创建MyAdapter,用于显示接下来搜索到的对等点列表,监听点击连接事件
继承RecyclerView.Adapter,实现获取对等点列表的显示和响应事件

public class MyAdapter extends RecyclerView.Adapter< MyAdapter.MyHolder> {private List< HashMap< String, String> > mList; /* * 设置回调接口,实现点击 * */ public interface OnItemClickListener { void OnItemClick(View view, int position); }public OnItemClickListener mOnItemClickListener; public void SetOnItemClickListener(OnItemClickListener listener) { this.mOnItemClickListener = listener; }public MyAdapter(List< HashMap< String, String> > list) { super(); this.mList = list; }@Override public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View view = inflater.inflate(R.layout.card_item, parent, false); MyHolder myHolder = new MyHolder(view); return myHolder; }@Override public void onBindViewHolder(final MyHolder holder, final int position) {holder.tvname.setText(mList.get(position).get("name")); holder.tvaddress.setText(mList.get(position).get("address")); if (mOnItemClickListener != null) {holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mOnItemClickListener.OnItemClick(holder.itemView, position); } }); } }@Override public int getItemCount() { return mList.size(); }class MyHolder extends RecyclerView.ViewHolder {public TextView tvname; public TextView tvaddress; public MyHolder(View View) { super(View); tvname = (TextView) View.findViewById(R.id.tv_name); tvaddress = (TextView) View.findViewById(R.id.tv_address); } } }

开始对对等点的搜索,获取对等点列表
      首先在  onCreate()  方法中获得了WifiP2pManager  的一个实例,调用它的  initialize()  方法。这个方法返回一个  WifiP2pManager.Channel  对象,并触发WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION的广播,把应用程序连接到Wi-Fi Direct框架中。
protected void onCreate(Bundle savedInstanceState) { ... mManager = (WifiP2pManager) getSystemService(WIFI_P2P_SERVICE); mChannel = mManager.initialize(this, Looper.myLooper(), null); }

        点击开启搜索,调用discoverPeers()方法。其参数如下:
              WifiP2pManager.Channel    即从上一步调用initialize()的  方法得到
              WifiP2pManager.ActionListener    实现了系统在查找成功或失败时会调用的方法
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() { //Register for WIFI_P2P_PEERS_CHANGED_ACTION @Override public void onSuccess() { } @Override public void onFailure(int reason) { } });

      如果发现设备会回调上面的onSuccess()方法,讲触发WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION这个广播,在onReceive()中可以调用requestPeers()方法,然后在MainActivity的PeerListListerner接口中,调用onPeersAvailable()回调方法,获取到对等列表的详细信息,通过MyAdapter在RecyclerView列表中显示。
 
WifiP2pManager.PeerListListener mPeerListListerner = new WifiP2pManager.PeerListListener() { @Override public void onPeersAvailable(WifiP2pDeviceList peersList) { peers.clear(); peersshow.clear(); Collection< WifiP2pDevice> aList = peersList.getDeviceList(); peers.addAll(aList); for (int i = 0; i < aList.size(); i++) { WifiP2pDevice a = (WifiP2pDevice) peers.get(i); HashMap< String, String> map = new HashMap< String, String> (); map.put("name", a.deviceName); map.put("address", a.deviceAddress); peersshow.add(map); } mAdapter = new MyAdapter(peersshow); mRecyclerView.setAdapter(mAdapter); mRecyclerView.setLayoutManager(new LinearLayoutManager (MainActivity.this)); } };

 
连接一个对等点
        触摸RecyclerView列表上的对等设备信息,触发响应事件,调用CreateConnect()方法,方法参数为触摸列表上的设备信息。在该方法中,需要先创建一个新的WifiP2pConfig对象,记录传进来的设备参数,然后调用connect()方法。  如果设备会回调onSuccess()方法,这说明对等点连接成功。
 
private void CreateConnect(String address, final String name) { WifiP2pDevice device; WifiP2pConfig config = new WifiP2pConfig(); config.deviceAddress = address; config.wps.setup = WpsInfo.PBC; mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() { //Register for WIFI_P2P_CONNECTION_CHANGED_ACTION @Override public void onSuccess() {} @Override public void onFailure(int reason) {} }); }

  判断一方为组拥有者(GroupOwner),提交接收图片的异步任务

      对等点连接成功后,会触发WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION这个广播,在onReceive()中调用requestConnectionInfo()方法,然后在MainActivity的ConnectionInfoListerner接口中,调用onConnectionInfoAvailable()回调方法,获取到组连接信息,根据获取到的信息,判断是否为组拥有者。若是,作为服务端,向其提交图片下载、读取的异步任务。
WifiP2pManager.ConnectionInfoListener mInfoListener = new WifiP2pManager.ConnectionInfoListener() {@Override public void onConnectionInfoAvailable(final WifiP2pInfo minfo) { info = minfo; view = (TextView) findViewById(R.id.tv_main); if (info.groupFormed & & info.isGroupOwner) { //确定为组拥有者,创建线程用于接收连接请求 //提交图片下载、读取的异步任务 } else if (info.groupFormed) { //作为客户端,创建一个线程用于连接组拥有者 } } };

public class FileServerAsyncTask extends AsyncTask< Void, Void, String> {private Context context; private TextView statusText; /** * @param context * @param statusText */ public FileServerAsyncTask(Context context, View statusText) { this.context = context; this.statusText = (TextView) statusText; }@Override protected String doInBackground(Void... params) { try { ServerSocket serverSocket = new ServerSocket(8888); Socket client = serverSocket.accept(); final File f = new File( Environment.getExternalStorageDirectory() +"/" + "RecvPicture"+ "/wifip2pshared-" + System.currentTimeMillis() + ".jpg"); File dirs = new File(f.getParent()); if (!dirs.exists()) dirs.mkdirs(); f.createNewFile(); /*Returns an input stream to read data from this socket*/ InputStream inputstream = client.getInputStream(); copyFile(inputstream, new FileOutputStream(f)); serverSocket.close(); return f.getAbsolutePath(); } catch (IOException e) { Log.e("xyz", e.toString()); return null; } }/* * (non-javadoc) * * @see android.os.AsyncTask#onPostExecute(java.lang.Object) */ @Override protected void onPostExecute(String result) {if(result != null){ Toast.makeText(context, "RecvPic:"+result, Toast.LENGTH_SHORT).show(); }if (result != null) { statusText.setText("RecvPic: " + result); Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse("file://" + result), "image/*"); context.startActivity(intent); }}public static boolean copyFile(InputStream inputStream, OutputStream out) { byte buf[] = new byte[1024]; int len; try { while ((len = inputStream.read(buf)) != -1) { out.write(buf, 0, len); } out.close(); inputStream.close(); } catch (IOException e) { return false; } return true; } }

调用系统图库选择图片,开启后台服务将其发送
      点击发送图片按钮,调用系统图库。选取图片后,开启图片发送服务,并将图片路径,发送的ip地址和接收的端口号传入。


public class FileTransferService extends IntentService {private static final int SOCKET_TIMEOUT = 5000; public static final String ACTION_SEND_FILE = "com.example.android.wifidirect.SEND_FILE"; public static final String EXTRAS_FILE_PATH = "sf_file_url"; public static final String EXTRAS_GROUP_OWNER_ADDRESS = "sf_go_host"; public static final String EXTRAS_GROUP_OWNER_PORT = "sf_go_port"; public FileTransferService(String name) { super(name); }public FileTransferService() { super("FileTransferService"); }/* * (non-Javadoc) * * @see android.app.IntentService#onHandleIntent(android.content.Intent) */ @Override protected void onHandleIntent(Intent intent) {Context context = getApplicationContext(); if (intent.getAction().equals(ACTION_SEND_FILE)) { String fileUri = intent.getExtras().getString(EXTRAS_FILE_PATH); String host = intent.getExtras().getString( EXTRAS_GROUP_OWNER_ADDRESS); Socket socket = new Socket(); int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT); try { Log.d("xyz", "Opening client socket - "); socket.bind(null); socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT); Log.d("xyz", "Client socket - " + socket.isConnected()); /*returns an output stream to write data into this socket*/ OutputStream stream = socket.getOutputStream(); ContentResolver cr = context.getContentResolver(); InputStream is = null; try { is = cr.openInputStream(Uri.parse(fileUri)); } catch (FileNotFoundException e) { }
FileServerAsyncTask.copyFile(is, stream);

} catch (IOException e) { } finally { if (socket != null) { if (socket.isConnected()) { try { socket.close(); } catch (IOException e) { // Give up e.printStackTrace(); } } } }} } }

  分别设作为组拥有者,完成图片的互发
        调用createGroup()方法,分别设定本设备为组拥有者,实现设备图片的互发。
 
mManager.createGroup(mChannel, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { Log.i("tag","BeGroupOwner success"); }@Override public void onFailure(int reason) { Log.i("tag","BeGroupOwner failed"); } });

【Android WiFi直连并互发图片】 











    推荐阅读