Wi-Fi Direct (P2P라고도 함)를 사용하면 애플리케이션이 주변을 빠르게 찾고 상호작용할 수 있습니다. 블루투스의 기능을 벗어난 범위에서 작동합니다.
Wi-Fi Direct (P2P) API를 사용하면 애플리케이션이 직접 네트워크 또는 핫스팟에 연결해야 합니다. 앱이 안전한 근거리 네트워크인 Wi-Fi의 일부로 설계된 경우 Direct는 기존 Wi-Fi 임시 네트워크보다 더 적합한 옵션입니다. 네트워킹을 하게 됩니다.
- Wi-Fi Direct는 WPA2 암호화를 지원합니다. (일부 임시 네트워크는 WEP 암호화).
- 장치는 제공하는 서비스를 브로드캐스트할 수 있으며, 이는 기기에서 적합한 동종 앱을 더 쉽게 찾을 수 있습니다.
- 어떤 장치가 네트워크의 그룹 소유자가 되어야 하는지 결정할 때, Wi-Fi Direct는 각 기기의 전원 관리, UI, 서비스를 검사합니다. 이 정보를 사용하여 가장 효과적으로 일할 수 있습니다.
- Android는 Wi-Fi 임시 모드를 지원하지 않습니다.
이 과정에서는 Wi-Fi P2P를 사용하여 주변 기기를 찾고 연결하는 방법을 보여줍니다.
애플리케이션 권한 설정
Wi-Fi Direct를 사용하려면
ACCESS_FINE_LOCATION
,
CHANGE_WIFI_STATE
,
ACCESS_WIFI_STATE
및
매니페스트에 대한 INTERNET
권한
앱이 Android 13 (API 수준 33) 이상을 타겟팅하는 경우
NEARBY_WIFI_DEVICES
권한을 부여할 수 있습니다. Wi-Fi
Direct는 인터넷 연결이 필요하지 않지만 표준 Java를 사용합니다.
INTERNET
가 필요한 소켓
권한을 부여했는지 확인합니다. 따라서 Wi-Fi Direct를 사용하려면 다음 권한이 필요합니다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.nsdchat" ... <!-- If your app targets Android 13 (API level 33) or higher, you must declare the NEARBY_WIFI_DEVICES permission. --> <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" <!-- If your app derives location information from Wi-Fi APIs, don't include the "usesPermissionFlags" attribute. --> android:usesPermissionFlags="neverForLocation" /> <uses-permission android:required="true" android:name="android.permission.ACCESS_FINE_LOCATION" <!-- If any feature in your app relies on precise location information, don't include the "maxSdkVersion" attribute. --> android:maxSdkVersion="32" /> <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가 위치 모드 활성화를 요구합니다.
broadcast receiver 및 P2P 관리자 설정
Wi-Fi Direct를 사용하려면
이벤트 발생 시 애플리케이션에 전달됩니다. 애플리케이션에서
IntentFilter
를 생성하고 다음을 수신 대기하도록 설정합니다.
WIFI_P2P_STATE_CHANGED_ACTION
- Wi-Fi Direct 사용 설정 여부를 나타냅니다.
WIFI_P2P_PEERS_CHANGED_ACTION
- 사용 가능한 동종 앱 목록이 변경되었음을 나타냅니다.
WIFI_P2P_CONNECTION_CHANGED_ACTION
-
Wi-Fi Direct 연결 상태가 변경되었음을 나타냅니다. 시작
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 Direct 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 Direct 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) ... }
자바
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 Direct 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 Direct 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 Direct 프레임워크에 연결합니다.
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) }
자바
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 Wi-Fi Direct 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 ) } } } }
자바
@Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { // Determine if Wi-Fi Direct 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)); } }
마지막으로, 다음과 같은 경우 인텐트 필터와 broadcast receiver를 등록하는 코드를 추가합니다.
기본 활동이 활성화되고 활동이 일시중지되면 등록을 취소합니다.
이를 위한 가장 좋은 위치는 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) }
자바
/** 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 P2P를 사용하여 주변 기기 검색을 시작하려면 discoverPeers()
를 호출합니다. 이 메서드는
다음 인수:
- 내
WifiP2pManager.Channel
P2P 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. } })
자바
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()
메서드는 검색 프로세스를 시작한 후
즉시 반환됩니다. 시스템에서 피어 검색 프로세스가
제공된 작업 리스너에서 메서드를 호출하여 성공적으로 시작됩니다.
또한 연결이 시작되거나 P2P 그룹이
되었습니다.
동종 기기 목록 가져오기
이제 동종 기기 목록을 가져오고 처리하는 코드를 작성합니다. 첫 페이지
WifiP2pManager.PeerListListener
구현
이 인터페이스는 Wi-Fi Direct가 보유한 피어에 대한 정보를 제공합니다.
있습니다. 또한 이 정보를 통해 앱은 동료가 언제 참가하거나 참가하는지 판단할 수 있습니다.
네트워크를 떠납니다 다음 코드 스니펫은 이러한 작업을 보여줍니다.
관련:
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 } }
자바
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; } } }
이제 broadcast receiver의 onReceive()
를 수정합니다.
WIFI_P2P_PEERS_CHANGED_ACTION
작업이 포함된 인텐트가 수신될 때 requestPeers()
를 호출하는 메서드를 호출합니다. 나
이 리스너를 수신기에 전달해야 합니다. 한 가지 방법은
broadcast receiver의 생성자에 인수로 사용합니다.
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") } ... } }
자바
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
인텐트가 포함된 인텐트
업데이트된 동종 앱 목록 요청을 트리거합니다.
동종 기기에 연결
동종 앱에 연결하려면 새 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() } }) }
자바
@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 Direct를 지원하는 경우
연결 시 그룹의 비밀번호를 명시적으로 요청할 수 있습니다. 기기 허용하기
그룹에 참여하기 위해 Wi-Fi Direct를 지원하지 않지만
를 호출하여 이 비밀번호를 검색할 수 있습니다.
requestGroupInfo()
, 다음 형식:
다음 코드 스니펫에 나와 있습니다.
Kotlin
manager.requestGroupInfo(channel) { group -> val groupPassword = group.passphrase }
자바
manager.requestGroupInfo(channel, new GroupInfoListener() { @Override public void onGroupInfoAvailable(WifiP2pGroup group) { String groupPassword = group.getPassphrase(); } });
WifiP2pManager.ActionListener
는
connect()
메서드는 시작이 호출될 때만
성공 또는 실패인지 결정합니다 연결 상태의 변경사항을 수신 대기하려면 다음을 구현합니다.
WifiP2pManager.ConnectionInfoListener
인터페이스
onConnectionInfoAvailable()
콜백은
연결을 변경할 수 있습니다. 여러 장치가 서로 통신하는 경우
단일 기기 (예: 플레이어가 3명 이상인 게임 또는 채팅 앱), 기기 1대
'그룹 소유자'로 지정됩니다. 특정 기기를
네트워크의 그룹 소유자에게
그룹 만들기 섹션
Kotlin
private val connectionListener = WifiP2pManager.ConnectionInfoListener { info -> // String 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. } }
자바
@Override public void onConnectionInfoAvailable(final WifiP2pInfo info) { // String from WifiP2pInfo struct String 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. } }
이제 broadcast receiver의 onReceive()
메서드로 돌아가서 섹션을 수정합니다.
수신 대기하는 WIFI_P2P_CONNECTION_CHANGED_ACTION
인텐트를 전송합니다.
이 인텐트가 수신되면 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) } } } ... }
자바
... } 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 Direct를 사용하는 경우
동종 앱과 연결 섹션(새 링크를 만드는 경우는 제외)
<ph type="x-smartling-placeholder">WifiP2pManager.ActionListener
</ph>
connect()
대신 createGroup()
사용 이벤트 내의 콜백 처리는
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() } })
자바
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를 지원하는 경우
Direct의 경우 각 기기에서 connect()
메서드를 사용할 수 있습니다.
메서드가 그룹을 만들고 그룹 소유자를 자동으로 선택합니다.
그룹을 만든 후
피어에 대한 세부정보를 가져올 requestGroupInfo()
네트워크 정보를 볼 수 있습니다.