Видимость p2p-пиров Wi-Fi Android

Я пытаюсь подключить 2 устройства с прямым Wi-Fi, скажем, устройство A и устройство B с Android > 4.1. Если я нахожусь на устройстве А и нажимаю кнопку для поиска других устройств, это не всегда работает одинаково.

Например, если я нажму кнопку поиска на устройстве A, он ничего не найдет, пока я не нажму кнопку поиска на устройстве B, даже если приложение работает на обоих. Поэтому устройство B не видно, пока оно не начнет поиск других устройств.

В других случаях, если я ищу устройства с устройством A, оно находит устройство B, даже если приложение было недавно закрыто на устройстве B, и если я пытаюсь подключиться к устройству B, оно работает. Проблема в том, что я хочу установить соединение, только если приложение запущено на обоих устройствах.

Иногда, когда устройство A обнаруживает устройство B и пытается подключиться к нему, оно не работает, пока устройство B не начнет находить устройства. Поэтому, когда я запускаю поиск на устройстве B, оно получает запрос на подключение от A, но до этого ничего не происходит.

В других случаях после нажатия кнопки поиска на устройстве А появляются устройства, на которых в данный момент не включен Wi-Fi или которые находятся вне зоны действия.

Вот мой код:

MainActivity.java

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
import android.net.wifi.p2p.WifiP2pGroup;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager.ActionListener;
import android.net.wifi.p2p.WifiP2pManager.Channel;
import android.net.wifi.p2p.WifiP2pManager.GroupInfoListener;
import android.net.wifi.p2p.WifiP2pManager.PeerListListener;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckedTextView;
import android.widget.ListView;
import android.widget.Toast;


public class MainActivity extends Activity implements OnItemClickListener, PeerListListener {

    private WifiP2pManager mManager;
    private Channel mChannel;
    private BroadcastReceiver mReceiver;
    private IntentFilter mIntentFilter;
    private List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>();
    private List<WifiP2pDevice> peersConnect = new ArrayList<WifiP2pDevice>();
    private ArrayList<String> peersName = new ArrayList<String>();
    private ListView list;
    private Button bSearch;
    private Button bConnect;
    private Button bDisconnect;
    private int nSelectedDevices = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
        mChannel = mManager.initialize(this, getMainLooper(), null);

        try {
            Class<?> wifiManager = Class
                    .forName("android.net.wifi.p2p.WifiP2pManager");

            Method method = wifiManager
                    .getMethod(
                            "enableP2p",
                            new Class[] { android.net.wifi.p2p.WifiP2pManager.Channel.class });

            method.invoke(mManager, mChannel);

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


        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);
        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);

        bSearch = (Button) this.findViewById(R.id.searcher);
        bSearch.setOnClickListener(new OnClickListener() {
            public void onClick (View v) {
                list.setVisibility(ListView.INVISIBLE);
                bConnect.setVisibility(View.INVISIBLE);
                bDisconnect.setVisibility(View.INVISIBLE);
                nSelectedDevices = 0;
                peersConnect.clear();
                peers.clear();
                peersName.clear();
                searchDevices();        
            }
        });

        bConnect = (Button) this.findViewById(R.id.connecter);
        bConnect.setOnClickListener(new OnClickListener() {
            public void onClick (View v) {
                bDisconnect.setVisibility(View.VISIBLE);
                connectDevices();       
                bConnect.setVisibility(View.INVISIBLE);
                nSelectedDevices = 0;
                peersConnect.clear();
            }
        });

        bDisconnect = (Button) this.findViewById(R.id.disconnecter);
        bDisconnect.setOnClickListener(new OnClickListener() {
            public void onClick (View v) {
                disconnectDevices();
                peersConnect.clear();
                bDisconnect.setVisibility(View.INVISIBLE);
            }
        });

        list = (ListView) this.findViewById(R.id.list);
        list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
         //--   text filtering
        list.setTextFilterEnabled(true);


    }

    /* register the broadcast receiver with the intent values to be matched */
    @Override
    protected void onResume() {
        super.onResume();
        mReceiver = new WifiReceiver(mManager, mChannel, this);
        registerReceiver(mReceiver, mIntentFilter);
    }

    /* unregister the broadcast receiver */
    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mReceiver);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        finish();
    }



    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
        CheckedTextView item = (CheckedTextView) v;
        if(item.isChecked()) {
            nSelectedDevices++;
            peersConnect.add(peers.get(position));
        }
        else {
            nSelectedDevices--;
            peersConnect.remove(peers.get(position));
        }
        if(nSelectedDevices == 1)
            bConnect.setVisibility(View.VISIBLE);
        else if(nSelectedDevices == 0)
            bConnect.setVisibility(View.INVISIBLE);
    }

    @Override
    public void onPeersAvailable(WifiP2pDeviceList peerList) {

        // Out with the old, in with the new.
        peers.clear();
        peers.addAll(peerList.getDeviceList());
        getDeviceName();
        list.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_checked, peersName));
        list.setOnItemClickListener(this);
        list.setVisibility(ListView.VISIBLE);
        // 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) getListAdapter()).notifyDataSetChanged();
        if (peers.size() == 0) {
            return;
        }
    }

    private void searchDevices() {
        mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
                //Toast.makeText(MainActivity.this, "Inizio ricerca...", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailure(int reasonCode) {
                //Toast.makeText(MainActivity.this, "Ricerca fallita!", Toast.LENGTH_SHORT).show();
            }
        });

    }

    private void connectDevices() {
        for(int i = 0; i < peersConnect.size(); i++) {

            // Picking the first device found on the network.
            WifiP2pDevice device = peersConnect.get(i);

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

            mManager.connect(mChannel, config, new ActionListener() {

                @Override
                public void onSuccess() {
                    // WiFiDirectBroadcastReceiver will notify us. Ignore for now.
                    Toast.makeText(MainActivity.this, "Connection requested...", Toast.LENGTH_SHORT).show();
                }

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

    public void disconnectDevices() {
        if (mManager != null && mChannel != null) {
            mManager.requestGroupInfo(mChannel, new GroupInfoListener() {
                @Override
                public void onGroupInfoAvailable(WifiP2pGroup group) {
                    if (group != null && mManager != null && mChannel != null && group.isGroupOwner()) {
                        mManager.removeGroup(mChannel, new ActionListener() {
                            @Override
                            public void onSuccess() {

                            }

                            @Override
                            public void onFailure(int reason) {

                            }
                        });
                    }
                }
            });
        }
    }


    private void getDeviceName() {
        int i = 0;
        peersName.clear();
        while(i < peers.size()) {
            peersName.add(peers.get(i).deviceName);
            i++;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

WifiReceiver.java

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager.Channel;
import android.widget.Toast;

public class WifiReceiver extends BroadcastReceiver {

    private WifiP2pManager mManager;
    private Channel mChannel;
    private MainActivity mActivity;

    public WifiReceiver(WifiP2pManager manager, Channel channel, MainActivity activity) {
        super();
        this.mManager = manager;
        this.mChannel = channel;
        this.mActivity = activity;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { // check if wifi is enabled/disabled
            System.out.println("Connection changed");
            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
            if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                //mActivity.setIsWifiP2pEnabled(true);
            } else {
                //mActivity.setIsWifiP2pEnabled(false);
            }
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
            // Call WifiP2pManager.requestPeers() to get a list of current peers
            // 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()
            System.out.println("Peers changed");
            if (mManager != null) {
                mManager.requestPeers(mChannel, mActivity);

            }
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
            // Respond to new connection or disconnections
            System.out.println("Connection changed");
        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            // Respond to this device's wifi state changing
            System.out.println("This device changed");
        }
        else if (WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION.equals(action)) {
            // Respond to this device's wifi state changing
            System.out.println("Search peers");
        }
    }

}

Activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:id="@+id/linearlayout1" >
        <Button
            android:id="@+id/searcher"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/button_send" />

        <Button
            android:id="@+id/connecter"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/button_connect"
            android:visibility="invisible" />

        <Button
            android:id="@+id/disconnecter"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/button_disconnect"
            android:visibility="invisible" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/linearlayout1" >

        <ListView
            android:id="@+id/list"
            android:layout_width="match_parent"
            android:layout_height="380dp"
            android:layout_alignParentLeft="true"
            android:visibility="invisible" >
        </ListView>
    </LinearLayout>
"

</RelativeLayout>

Я не понимаю, как работают mManager.discoverPeers() и mManager.requestPeers().

Спасибо за Ваше внимание!!


person Matteo Bernardon    schedule 23.06.2015    source источник


Ответы (2)


На самом деле у вас есть действительные наблюдения, и именно так на самом деле работает API:

  1. Таким образом, устройство B не видно, пока оно не начнет поиск других устройств.

Так работает API. По сути, чтобы быть видимым для других устройств, интерфейс Wi-Fi должен быть включен и активен, и до сих пор я видел, как это происходит, когда устройство либо выполняет активное обнаружение, либо имеет активное соединение.

  1. Проблема в том, что я хочу установить соединение, только если приложение запущено на обоих устройствах.

По сути, я полагаю, что лучшее, что вы можете сделать, это рекламировать службу во время работы приложения и обнаруживать службу при подключении. Это не на 100% точно, поэтому вы также можете реализовать соединение и рукопожатие от клиента к владельцу группы, чтобы полностью проверить, что оба конца в порядке и присутствуют. Если рукопожатие не удается, отключите соединение.

  1. Поэтому, когда я начинаю поиск на устройстве B, он получает запрос на подключение от A, но до этого ничего.

На этот у меня нет прямого ответа, в основном может быть, что что-то не так. Я знаю, что если устройство B не активно, оно не должно быть видно, и если устройство B не находится в фактическом списке обнаруженных узлов API, тогда все попытки подключения к нему будут неудачными, поэтому может быть какая-то комбинация проблем, что происходит здесь на самом деле.

  1. он показывает некоторые устройства, на которых в данный момент не включен Wi-Fi или которые находятся вне зоны действия.

Предположим, что API несколько раз кэширует некоторые результаты, хотя должен признать, что я не видел этой проблемы, я обычно получаю события изменения однорангового узла, когда я выключаю соседнее устройство, а затем обнаружение службы не дает никаких результатов на любое устройство, которого на самом деле не было бы там, поэтому всегда пытайтесь обнаружить службу после того, как вы получите событие Peers Changed.

person Dr.Jukka    schedule 24.06.2015
comment
Спасибо за ответы!! - person Matteo Bernardon; 24.06.2015

У меня была аналогичная проблема. Некоторые из моих устройств не могли найти другие устройства, но другие могли подключиться к ним.

Проблема заключалась в том, что я не запрашивал разрешение на определение местоположения.

Manifest.permission.ACCESS_COARSE_LOCATION;
person Cililing    schedule 09.07.2019