Android Wi-Fi直连开发指南

作者: hcancan | 来源:发表于2016-07-29 15:34 被阅读2613次

    <h4>如何创建一个使用Wi-Fi P2P的应用</h4>
    创建使用Wi-Fi P2P功能的应用需要多个步骤,其中包括注册广播接受者,发现设备,连接设备,传输数据等步骤。下面是关于这些步骤的介绍。

    初始化设置
    在调用wifi-p2p的API前,你须要确保相应设备支持Wi-Fi P2P。如果该设备支持Wi-Fi P2P,那么你可以在你的应用中获取WifiP2pManager实例,并进行广播接受者的创建和注册,以及调用其它Wi-Fi P2P 的API。

    1.在manifest文件当中声明相关权限,同时声明你应用的SDK 最小版本号(因为Android4.0才开始支持WiFi直连,所以版本号是14)。

    <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" />
    

    2.检查是WiFi是否开启。一个比较好的检查方式是,当广播接受者收到WIFI_P2P_STATE_CHANGED_ACTION广播时,对其进行判断看WiFi是否开启。然后将WiFi的状态通知给Activity,让其对此作出相应的处理(例如提示用户或者开启WiFi)。

    @Override
    public void onReceive(Context context, Intent intent) {
        ...
        String action = intent.getAction();
        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
            if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                // Wifi P2P is enabled
            } else {
                // Wi-Fi P2P is not enabled
            }
        }
        ...
    }
    

    3、在你的Activity的onCreate方法当中,获取WifiP2pManager的实例。然后调用initialize()方法来将你的应用注册到 Wi-Fi P2P framework当中,这个方法将会返回一个WifiP2pManager.Channel对象,它把你的应用与底层的 Wi-Fi P2P framework连接起来。

    建议你同时创建一个广播接受者,并把WifiP2pManager和WifiP2pManager.Channel以及Activity引用传给它。这可以让你的broadcast receiver在接收到特定时间的广播时,能够很方便的通知Activity或者对Wi-Fi进行操作。

    WifiP2pManager mManager;
    Channel mChannel;
    BroadcastReceiver mReceiver;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState){
        ...
        mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
        mChannel = mManager.initialize(this, getMainLooper(), null);
        mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
        ...
    }
    

    4、创建一个Intent filter,然后把下面这些intent加入其中,这样你的广播接收者就能接收到WiFi变化相应的广播。

    IntentFilter mIntentFilter;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState){
        ...
        mIntentFilter = new IntentFilter();
        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、在Activity的onResume()方法当中注册广播接受者,在onPause方法当中注销它。

    /* register the broadcast receiver with the intent values to be matched */
    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(mReceiver, mIntentFilter);
    }
    /* unregister the broadcast receiver */
    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mReceiver);
    }
    

    发现设备
    调用discoverPeers()方法,就可以开始发现周边适合连接的设备。这个方法的调用是异步的,如果你设置了一个WifiP2pManager.ActionListener监听器,那么这个方法的调用结果会通过ActionListener回调返回。onSuccess()方法只告诉你该发现节点的任务执行成功,但不会提供所发现节点的任何信息。

    mManager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
        @Override
        public void onSuccess() {
            ...
        }
    
        @Override
        public void onFailure(int reasonCode) {
            ...
        }
    });
    

    如果发现节点的任务执行成功,并且检测到了适合节点。系统会发送WIFI_P2P_PEERS_CHANGED_ACTION广播,当你收到这个广播的时候,你就可以调用requestPeers()方法,来获取发现了节点列表。

    PeerListListener myPeerListListener;
    ...
    if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
    
        // request available peers from the wifi p2p manager. This is an
        // asynchronous call and the calling activity is notified with a
        // callback on PeerListListener.onPeersAvailable()
        if (mManager != null) {
            mManager.requestPeers(mChannel, myPeerListListener);
        }
    }
    

    连接节点
    当你找到自己想要连接的设备时,你可以通过调用connect()方法来连接这个设备,调用这个方法需要一个WifiP2pConfig对象作为参数。WifiP2pConfig对象包含了进行连接的一些配置信息,包括设备地址,认证方式,谁作为GroupOwner等。这个方法调用的结果会通过WifiP2pManager.ActionListener返回。

    //obtain a peer from the WifiP2pDeviceList
    WifiP2pDevice device;
    WifiP2pConfig config = new WifiP2pConfig();
    config.deviceAddress = device.deviceAddress;
    mManager.connect(mChannel, config, new ActionListener() {
    
        @Override
        public void onSuccess() {
            //success logic
        }
    
        @Override
        public void onFailure(int reason) {
            //failure logic
        }
    });
    
    

    传输数据
    一旦设备间的连接成功建立,你就可以通过socket来传输数据,基本的步骤如下。
    1、创建ServerSocket,并调用accept()方法在指定端口等待客户端的连接,注意这个过程将会阻塞线程,请在后台线程完成这个它。

    2、创建ClientSocket,然后使用服务器IP和端口去连接充当服务器的设备。

    3、 客户端往服务器发送数据。当客户端成功连接上服务器端之后,你就可以以字节流的形式向服务器发送数据了。

    4、服务器接收数据。当服务器的accept()接受一个客户端连接之后,服务器端口能够收到客户端发来的数据了。

    注意在WiFi P2P连接中,Group Owner和Group Client都能够作为Server Socket,并且当一个Socket连接建立后,双方都可以收发数据。

    下面是一个通过WiFi P2P从客户端往服务器端发送图片的例子,以下是服务器端部分代码。

    public static class FileServerAsyncTask extends AsyncTask {
    
        private Context context;
        private TextView statusText;
    
        public FileServerAsyncTask(Context context, View statusText) {
            this.context = context;
            this.statusText = (TextView) statusText;
        }
    
        @Override
        protected String doInBackground(Void... params) {
            try {
    
                /**
                 * Create a server socket and wait for client connections. This
                 * call blocks until a connection is accepted from a client
                 */
                ServerSocket serverSocket = new ServerSocket(8888);
                Socket client = serverSocket.accept();
    
                /**
                 * If this code is reached, a client has connected and transferred data
                 * Save the input stream from the client as a JPEG file
                 */
                final File f = new File(Environment.getExternalStorageDirectory() + "/"
                        + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis()
                        + ".jpg");
    
                File dirs = new File(f.getParent());
                if (!dirs.exists())
                    dirs.mkdirs();
                f.createNewFile();
                InputStream inputstream = client.getInputStream();
                copyFile(inputstream, new FileOutputStream(f));
                serverSocket.close();
                return f.getAbsolutePath();
            } catch (IOException e) {
                Log.e(WiFiDirectActivity.TAG, e.getMessage());
                return null;
            }
        }
    
        /**
         * Start activity that can handle the JPEG image
         */
        @Override
        protected void onPostExecute(String result) {
            if (result != null) {
                statusText.setText("File copied - " + result);
                Intent intent = new Intent();
                intent.setAction(android.content.Intent.ACTION_VIEW);
                intent.setDataAndType(Uri.parse("file://" + result), "image/*");
                context.startActivity(intent);
            }
        }
    }
    
    

    以下是客户端部分代码

    Context context = this.getApplicationContext();
    String host;
    int port;
    int len;
    Socket socket = new Socket();
    byte buf[]  = new byte[1024];
    ...
    try {
        /**
         * Create a client socket with the host,
         * port, and timeout information.
         */
        socket.bind(null);
        socket.connect((new InetSocketAddress(host, port)), 500);
    
        /**
         * Create a byte stream from a JPEG file and pipe it to the output stream
         * of the socket. This data will be retrieved by the server device.
         */
        OutputStream outputStream = socket.getOutputStream();
        ContentResolver cr = context.getContentResolver();
        InputStream inputStream = 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 (FileNotFoundException e) {
        //catch logic
    } catch (IOException e) {
        //catch logic
    }
    
    /**
     * Clean up any open sockets when done
     * transferring or if an exception occurred.
     */
    finally {
        if (socket != null) {
            if (socket.isConnected()) {
                try {
                    socket.close();
                } catch (IOException e) {
                    //catch logic
                }
            }
        }
    }
    

    本文档翻译自google官方文档,并作了部分补充。

    相关文章

      网友评论

        本文标题:Android Wi-Fi直连开发指南

        本文链接:https://www.haomeiwen.com/subject/xzlzjttx.html