首页 > 代码库 > 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直连并互发图片