Android NDK开发详解连接性之通过 Wi-Fi 直连创建点对点连接


通过 Wi-Fi 直连(也称为对等连接或点对点连接),您的应用可以在超出蓝牙功能的范围内快速查找附近的设备并与之互动。

通过 Wi-Fi 对等连接(点对点连接)API,应用无需连接到网络或热点就可以连接到附近的设备。如果您的应用旨在成为安全、近距离网络的一部分,则 Wi-Fi 直连选项比传统 Wi-Fi 临时网络更合适,原因如下:

Wi-Fi 直连支持 WPA2 加密。(一些临时网络仅支持 WEP 加密。)
设备可以广播其提供的服务,这有助于其他设备更容易地发现合适的对等设备。
在确定哪个设备应该是网络的群组所有者时,Wi-Fi 直连会检查各设备的电源管理、界面和服务功能,并使用该信息选择可最有效处理服务器职责的设备。
Android 不支持 Wi-Fi 临时模式。
本课介绍如何使用 Wi-Fi 点对点连接查找并连接附近的设备。

设置应用权限

如需使用 Wi-Fi 点对点,请在清单中添加 ACCESS_FINE_LOCATION、CHANGE_WIFI_STATE、ACCESS_WIFI_STATE 和 INTERNET 权限。尽管 Wi-Fi P2P 不需要互联网连接,但它使用标准 Java 套接字,而这需要获得 INTERNET 权限。因此,您需要以下权限才能使用 Wi-Fi 点对点连接:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.android.nsdchat"
        ...
        <uses-permission
            android:required="true"
            android:name="android.permission.ACCESS_FINE_LOCATION"/>
        <uses-permission
            android:required="true"
            android:name="android.permission.ACCESS_WIFI_STATE"/>
        <uses-permission
            android:required="true"
            android:name="android.permission.CHANGE_WIFI_STATE"/>
        <uses-permission
            android:required="true"
            android:name="android.permission.INTERNET"/>
        ...

除了上面的权限外,以下 API 还需要启用位置信息模式:

discoverPeers
discoverServices
requestPeers

设置广播接收器和对等连接管理器

如需使用 Wi-Fi 点对点连接,您需要监听广播 intent,其告知您的应用某些事件是何时发生的。在您的应用中,实例化 IntentFilter 并将其设置为监听以下内容:

WIFI_P2P_STATE_CHANGED_ACTION
指示是否启用 Wi-Fi 点对点连接
WIFI_P2P_PEERS_CHANGED_ACTION
指示可用的对等设备列表已更改。
WIFI_P2P_CONNECTION_CHANGED_ACTION
指示 Wi-Fi 点对点连接的状态已更改。从 Android 10 开始,这不是固定的。如果您的应用依赖于在注册时接收这些广播(因为其之前一直是固定的),请在初始化时使用适当的 get 方法获取信息。
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
指示此设备的配置详细信息已更改。从 Android 10 开始,这不是固定的。如果您的应用依赖于在注册时接收这些广播(因为其之前一直是固定的),请在初始化时使用适当的 get 方法获取信息。
Kotlin

  private val intentFilter = IntentFilter()
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)

        // Indicates a change in the Wi-Fi P2P status.
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)

        // Indicates a change in the list of available peers.
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)

        // Indicates the state of Wi-Fi P2P connectivity has changed.
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)

        // Indicates this device's details have changed.
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)
        ...
    }

Java

private final IntentFilter intentFilter = new IntentFilter();
    ...
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Indicates a change in the Wi-Fi P2P status.
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);

        // Indicates a change in the list of available peers.
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);

        // Indicates the state of Wi-Fi P2P connectivity has changed.
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);

        // Indicates this device's details have changed.
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
        ...
    }

在 onCreate() 方法结束时,获取 WifiP2pManager 的实例并调用其 initialize() 方法。该方法会返回一个 WifiP2pManager.Channel 对象,稍后您将使用该对象将您的应用连接到 Wi-Fi 点对点连接框架。

Kotlin

    private lateinit var channel: WifiP2pManager.Channel
    private lateinit var manager: WifiP2pManager

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        manager = getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager
        channel = manager.initialize(this, mainLooper, null)
    }

Java

   Channel channel;
    WifiP2pManager manager;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
        channel = manager.initialize(this, getMainLooper(), null);
    }

现在创建一个新的 BroadcastReceiver 类,您将使用该类监听对系统的 Wi-Fi 点对点连接状态进行的更改。在 onReceive() 方法中,添加一个条件以处理上面列出的每个点对点连接状态更改。

Kotlin

    override fun onReceive(context: Context, intent: Intent) {
        when(intent.action) {
            WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> {
                // Determine if Wifi P2P mode is enabled or not, alert
                // the Activity.
                val state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1)
                activity.isWifiP2pEnabled = state == WifiP2pManager.WIFI_P2P_STATE_ENABLED
            }
            WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {

                // The peer list has changed! We should probably do something about
                // that.

            }
            WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> {

                // Connection state changed! We should probably do something about
                // that.

            }
            WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> {
                (activity.supportFragmentManager.findFragmentById(R.id.frag_list) as DeviceListFragment)
                        .apply {
                            updateThisDevice(
                                    intent.getParcelableExtra(
                                            WifiP2pManager.EXTRA_WIFI_P2P_DEVICE) as WifiP2pDevice
                            )
                        }
            }
        }
    }

Java

  @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
            // Determine if Wifi P2P mode is enabled or not, alert
            // the Activity.
            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
            if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                activity.setIsWifiP2pEnabled(true);
            } else {
                activity.setIsWifiP2pEnabled(false);
            }
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {

            // The peer list has changed! We should probably do something about
            // that.

        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {

            // Connection state changed! We should probably do something about
            // that.

        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager()
                    .findFragmentById(R.id.frag_list);
            fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(
                    WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));

        }
    }

最后,添加代码以在您的主 Activity 处于活动状态时注册 intent 过滤器和广播接收器,并在该 Activity 暂停时取消注册 intent 过滤器和广播接收器。最好使用 onResume() 和 onPause() 方法。

Kotlin

    /** register the BroadcastReceiver with the intent values to be matched  */
    public override fun onResume() {
        super.onResume()
        receiver = WiFiDirectBroadcastReceiver(manager, channel, this)
        registerReceiver(receiver, intentFilter)
    }

    public override fun onPause() {
        super.onPause()
        unregisterReceiver(receiver)
    }

Java

   /** register the BroadcastReceiver with the intent values to be matched */
    @Override
    public void onResume() {
        super.onResume();
        receiver = new WiFiDirectBroadcastReceiver(manager, channel, this);
        registerReceiver(receiver, intentFilter);
    }

    @Override
    public void onPause() {
        super.onPause();
        unregisterReceiver(receiver);
    }

启动对等设备发现

如需使用 Wi-Fi 点对点连接开始搜索附近的设备,请调用 discoverPeers()。此方法采用以下参数:

WifiP2pManager.Channel,在初始化对等设备 mManager 时收回的参数
WifiP2pManager.ActionListener 的实现,包含系统为成功和未成功发现调用的方法。
Kotlin

    manager.discoverPeers(channel, object : WifiP2pManager.ActionListener {

        override fun onSuccess() {
            // Code for when the discovery initiation is successful goes here.
            // No services have actually been discovered yet, so this method
            // can often be left blank. Code for peer discovery goes in the
            // onReceive method, detailed below.
        }

        override fun onFailure(reasonCode: Int) {
            // Code for when the discovery initiation fails goes here.
            // Alert the user that something went wrong.
        }
    })

Java

  manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {

        @Override
        public void onSuccess() {
            // Code for when the discovery initiation is successful goes here.
            // No services have actually been discovered yet, so this method
            // can often be left blank. Code for peer discovery goes in the
            // onReceive method, detailed below.
        }

        @Override
        public void onFailure(int reasonCode) {
            // Code for when the discovery initiation fails goes here.
            // Alert the user that something went wrong.
        }
    });

请记住,这仅启动对等设备发现。discoverPeers() 方法启动发现过程,然后立即返回。系统通过在提供的操作监听器中调用方法通知您是否成功启动对等设备发现过程。此外,在启动某个连接或形成点对点连接群组之前,发现一直处于活跃状态。

获取对等设备列表

现在编写获取并处理对等设备列表的代码。首先实现 WifiP2pManager.PeerListListener 接口,该接口提供有关 Wi-Fi 点对点连接检测到的对等设备的信息。通过这些信息,您的应用还可以确定对等设备何时加入或离开网络。以下代码段展示了这些与对等设备相关的操作:

Kotlin


    private val peers = mutableListOf<WifiP2pDevice>()
    ...

    private val peerListListener = WifiP2pManager.PeerListListener { peerList ->
        val refreshedPeers = peerList.deviceList
        if (refreshedPeers != peers) {
            peers.clear()
            peers.addAll(refreshedPeers)

            // If an AdapterView is backed by this data, notify it
            // of the change. For instance, if you have a ListView of
            // available peers, trigger an update.
            (listAdapter as WiFiPeerListAdapter).notifyDataSetChanged()

            // Perform any other updates needed based on the new list of
            // peers connected to the Wi-Fi P2P network.
        }

        if (peers.isEmpty()) {
            Log.d(TAG, "No devices found")
            return@PeerListListener
        }
    }

Java

private List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>();
    ...

    private PeerListListener peerListListener = new PeerListListener() {
        @Override
        public void onPeersAvailable(WifiP2pDeviceList peerList) {

            List<WifiP2pDevice> refreshedPeers = peerList.getDeviceList();
            if (!refreshedPeers.equals(peers)) {
                peers.clear();
                peers.addAll(refreshedPeers);

                // If an AdapterView is backed by this data, notify it
                // of the change. For instance, if you have a ListView of
                // available peers, trigger an update.
                ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();

                // Perform any other updates needed based on the new list of
                // peers connected to the Wi-Fi P2P network.
            }

            if (peers.size() == 0) {
                Log.d(WiFiDirectActivity.TAG, "No devices found");
                return;
            }
        }
    }

现在修改广播接收器的 onReceive() 方法,以便在收到具有操作 WIFI_P2P_PEERS_CHANGED_ACTION 的 intent 时调用 requestPeers()。您需要以某种方式将此监听器传递给接收器。一种方式是将其作为一个参数发送给广播接收器的构造函数。

Kotlin

    fun onReceive(context: Context, intent: Intent) {
        when (intent.action) {
            ...
            WifiP2pManager.WIFI_P2P_PEERS_CHANGED_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()
                mManager?.requestPeers(channel, peerListListener)
                Log.d(TAG, "P2P peers changed")

            }
            ...
        }
    }

Java

  public void onReceive(Context context, Intent intent) {
        ...
        else 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(channel, peerListListener);
            }
            Log.d(WiFiDirectActivity.TAG, "P2P peers changed");
        }...
    }
    

现在,具有操作 WIFI_P2P_PEERS_CHANGED_ACTION intent 的 intent 触发对更新的对等设备列表的请求。

连接到对等设备

如需连接到对等设备,请创建一个新的 WifiP2pConfig 对象,并从代表您要连接到的设备的 WifiP2pDevice 将数据复制到该对象中。然后调用 connect() 方法。

Kotlin


    override fun connect() {
        // Picking the first device found on the network.
        val device = peers[0]

        val config = WifiP2pConfig().apply {
            deviceAddress = device.deviceAddress
            wps.setup = WpsInfo.PBC
        }

        manager.connect(channel, config, object : WifiP2pManager.ActionListener {

            override fun onSuccess() {
                // WiFiDirectBroadcastReceiver notifies us. Ignore for now.
            }

            override fun onFailure(reason: Int) {
                Toast.makeText(
                        this@WiFiDirectActivity,
                        "Connect failed. Retry.",
                        Toast.LENGTH_SHORT
                ).show()
            }
        })
    }

Java

@Override
    public void connect() {
        // Picking the first device found on the network.
        WifiP2pDevice device = peers.get(0);

        WifiP2pConfig config = new WifiP2pConfig();
        config.deviceAddress = device.deviceAddress;
        config.wps.setup = WpsInfo.PBC;

        manager.connect(channel, config, new ActionListener() {

            @Override
            public void onSuccess() {
                // WiFiDirectBroadcastReceiver notifies us. Ignore for now.
            }

            @Override
            public void onFailure(int reason) {
                Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",
                        Toast.LENGTH_SHORT).show();
            }
        });
    }

如果群组中的每个设备都支持 Wi-Fi 直连,则在连接时无需明确提示输入群组的密码。但如需允许不支持 Wi-Fi 直连的设备加入群组,则需要通过调用 requestGroupInfo() 检索此密码,如以下代码段所示:

Kotlin

    manager.requestGroupInfo(channel) { group ->
        val groupPassword = group.passphrase
    }

Java

manager.requestGroupInfo(channel, new GroupInfoListener() {
      @Override
      public void onGroupInfoAvailable(WifiP2pGroup group) {
          String groupPassword = group.getPassphrase();
      }
    });

请注意,在 connect() 方法中实现的 WifiP2pManager.ActionListener 仅在启动成功或失败时通知您。如需监听连接状态的更改,请实现 WifiP2pManager.ConnectionInfoListener 接口。其 onConnectionInfoAvailable() 回调在连接状态更改时通知您。如果多个设备要连接到单个设备(就像有三个或更多玩家的游戏,或聊天应用),应将一个设备指定为“群组所有者”。您可以按照创建群组部分中的步骤,将特定设备指定为网络的群组所有者。

Kotlin

    private val connectionListener = WifiP2pManager.ConnectionInfoListener { info ->

        // InetAddress from WifiP2pInfo struct.
        val groupOwnerAddress: String = info.groupOwnerAddress.hostAddress

        // After the group negotiation, we can determine the group owner
        // (server).
        if (info.groupFormed && info.isGroupOwner) {
            // Do whatever tasks are specific to the group owner.
            // One common case is creating a group owner thread and accepting
            // incoming connections.
        } else if (info.groupFormed) {
            // The other device acts as the peer (client). In this case,
            // you'll want to create a peer thread that connects
            // to the group owner.
        }
    }

Java

  @Override
    public void onConnectionInfoAvailable(final WifiP2pInfo info) {

        // InetAddress from WifiP2pInfo struct.
        InetAddress groupOwnerAddress = info.groupOwnerAddress.getHostAddress();

        // After the group negotiation, we can determine the group owner
        // (server).
        if (info.groupFormed && info.isGroupOwner) {
            // Do whatever tasks are specific to the group owner.
            // One common case is creating a group owner thread and accepting
            // incoming connections.
        } else if (info.groupFormed) {
            // The other device acts as the peer (client). In this case,
            // you'll want to create a peer thread that connects
            // to the group owner.
        }
    }

现在返回广播接收器的 onReceive()方法,并修改监听 WIFI_P2P_CONNECTION_CHANGED_ACTION intent 的部分。收到此 intent 时,调用 requestConnectionInfo()。这是一个异步调用,因此,由您作为参数提供的连接信息监听器接收结果。

Kotlin

    when (intent.action) {
        ...
        WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> {

            // Connection state changed! We should probably do something about
            // that.

            mManager?.let { manager ->

                val networkInfo: NetworkInfo? = intent
                        .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO) as NetworkInfo

                if (networkInfo?.isConnected == true) {

                    // We are connected with the other device, request connection
                    // info to find group owner IP

                    manager.requestConnectionInfo(channel, connectionListener)
                }
            }
        }
        ...
    }

Java

      ...
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {

            if (manager == null) {
                return;
            }

            NetworkInfo networkInfo = (NetworkInfo) intent
                    .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);

            if (networkInfo.isConnected()) {

                // We are connected with the other device, request connection
                // info to find group owner IP

                manager.requestConnectionInfo(channel, connectionListener);
            }
            ...

创建群组

如果您希望运行应用的设备充当包括传统设备(即不支持 Wi-Fi 直连的设备)的网络的群组所有者,则请遵循连接到对等设备部分中的相同步骤序列,除非您使用 createGroup() 而不是 connect() 创建新的 WifiP2pManager.ActionListener。WifiP2pManager.ActionListener 中的回调处理方式相同,如以下代码段所示:

Kotlin

    manager.createGroup(channel, object : WifiP2pManager.ActionListener {
        override fun onSuccess() {
            // Device is ready to accept incoming connections from peers.
        }

        override fun onFailure(reason: Int) {
            Toast.makeText(
                    this@WiFiDirectActivity,
                    "P2P group creation failed. Retry.",
                    Toast.LENGTH_SHORT
            ).show()
        }
    })

Java

    manager.createGroup(channel, new WifiP2pManager.ActionListener() {
        @Override
        public void onSuccess() {
            // Device is ready to accept incoming connections from peers.
        }

        @Override
        public void onFailure(int reason) {
            Toast.makeText(WiFiDirectActivity.this, "P2P group creation failed. Retry.",
                    Toast.LENGTH_SHORT).show();
        }
    });
    

注意:如果网络中的所有设备都支持 Wi-Fi 直连,则可在每个设备上使用 connect() 方法,因为该方法随后将自动创建群组并选择群组所有者。

创建群组后,可以调用 requestGroupInfo() 检索有关网络上对等设备的详细信息,包括设备名称和连接状态。

本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。

最后更新时间 (UTC):2020-06-10。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Android NDK开发是指利用NDK(Native Development Kit)将C/C++开发的代码编译成so库,然后通过JNI(Java Native Interface)让Java程序调用。在Android开发中,默认使用的是Android SDK进行Java语言的开发,而对于一些需要使用C/C++的高性能计算、底层操作或跨平台需求的场景,可以使用NDK进行开发。 在Android Studio中进行NDK开发相对于Eclipse来说更加方便,特别是在Android Studio 3.0及以上版本中,配置更加简化,并引入了CMake等工具,使得开发更加便捷。首先要进行NDK开发,需要配置环境,包括导入NDK、LLDB和CMake等工具。可以通过打开Android Studio的SDK Manager,选择SDK Tools,在其中选中相应的工具进行导入。 在项目的build.gradle文件中,可以配置一些NDK相关的参数,例如编译版本、ABI过滤器等。其中,可以通过externalNativeBuild配置CMake的相关设置,包括CMakeLists.txt文件的路径和版本号。此外,在sourceSets.main中还可以设置jniLibs.srcDirs,指定so库的位置。 在进行NDK开发时,可以在jni文件夹中编写C/C++代码,并通过JNI调用相关的函数。通过JNI接口,可以实现Java与C/C++之间的相互调用,从而实现跨语言的开发。 综上所述,Android NDK开发是指利用NDK将C/C++开发的代码编译成so库,并通过JNI实现与Java的相互调用。在Android Studio中进行NDK开发相对方便,可以通过配置环境和相应的参数来进行开发。<span class="em">1</span><span class="em">2</span><span class="em">3</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五一编程

程序之路有我与你同行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值