В течение прошлой недели я пытался заставить это работать регулярно. Мой WifiP2P иногда будет работать, но в основном он зависает, и я понятия не имею, почему - PullRequest
0 голосов
/ 23 марта 2019

Итак, я только начинаю изучать, как подключить два телефона по беспроводной сети через WifiP2p с помощью моего приложения.

Мое приложение - простая игра «Камень, Ножницы, Бумага».Ничего слишком сложного, и я пытаюсь получить два тестовых телефона, которые я использую, чтобы «разговаривать» друг с другом (пример: один выбирает рок, другой выбирает бумагу, и они обновляются соответствующим образом).

Моя проблема в том, что мое приложение будет постоянно падать, или если я введу

    if (android.os.Build.VERSION.SDK_INT >= 23) {
            StrictMode.ThreadPolicy policy = new 
    StrictMode.ThreadPolicy.Builder().permitAll().build();
            StrictMode.setThreadPolicy(policy);
        }

только для того, чтобы запустить его в целях тестирования (я знаю, что приведенный выше код - ужасная идея), иногда это будет работать, но в основном это просто сбой.

Я также постоянно получаю NetworkOnMainThreadException, что, как я думал, не следует, поскольку я расширил класс Thread.

Так что яЯ добавил все необходимые разрешения

    <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.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

И я попытался создать методы "реализует Runnable", но безрезультатно.Я подумал об использовании AsyncTask, но из того, что я прочитал, это не совсем лучший выбор, а также уголок разработчиков Android предложил использовать класс Thread для простоты того, что я пытаюсь сделать, что, по сути, просто отправкасообщение с одного телефона на другой.

Мой код был создан в соответствии с руководством "Технологии Сарти", в частности с этой строкой видео: https://www.youtube.com/watch?v=nw627o-8Fok&list=PLFh8wpMiEi88SIJ-PnJjDxktry4lgBtN3&index=1

Все еще не получается заставить работать что-либо.

У меня есть два класса, которые пытаются запустить все, класс приемника вещания здесь:

public class Wifi_P2P_Receiver extends BroadcastReceiver {

    private WifiP2pManager mManager;
    private WifiP2pManager.Channel mChannel;
    private MultiPlayer mActivity;

    public Wifi_P2P_Receiver(WifiP2pManager manager, WifiP2pManager.Channel channel, MultiPlayer mainActivity) {
        this.mManager = manager;
        this.mChannel = channel;
        this.mActivity = mainActivity;
    }

    @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){
//                Toast.makeText(context, "Wifi is ON", Toast.LENGTH_LONG).show();
            } else {
//                Toast.makeText(context, "Wifi is OFF", Toast.LENGTH_LONG).show();
            }
        }
        if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
            if(mManager != null) {
                mManager.requestPeers(mChannel, mActivity.peerListListener);
            }
        }
        else if(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
            if(mManager == null){
                return;
            }
            NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);

            if(networkInfo.isConnected()){
                mManager.requestConnectionInfo(mChannel, mActivity.CIL);
            } else {
                mActivity.status.setText("Device Disconnected");
            }
        }
        else if(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {

        }
    }
}

А потом у меня есть мой основной вызывающий класс многопользовательского класса Multiplayer, который очень большой, поэтому яРазбей его.

Вот первая часть до метода onCreate ():

public class MultiPlayer extends AppCompatActivity {

    Button btnDiscover;
    Button sendRock;
    Button sendPaper;
    Button sendScissors;
    String tempMsg = "";
    TextView status;
    TextView opponentChoice;
    TextView gameResult;
    String playerChoice = "";
    ListView playerList;
    WifiP2pManager mManager;
    WifiP2pManager.Channel mChannel;
    IntentFilter filter;
    Wifi_P2P_Receiver mReceiver;
    WifiManager wifi;

    List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>();
    String[] deviceNameArray;
    WifiP2pDevice[] deviceArray;

    static final int MESSAGE_READ = 1;

    ServerClass serverClass;
    ClientClass clientClass;
    SendReceive sendReceive;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.connect);

        initialWork();
        exqListener();
    }

initialWork () и exqListener () выполняют всю основную работу.Вот initialWork ():

public void initialWork() {
        btnDiscover = (Button) findViewById(R.id.btnFindPlayers);
        playerList = (ListView) findViewById(R.id.listPlayerView);

        wifi = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);

        mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
        mChannel = mManager.initialize(this, getMainLooper(), null);

        mReceiver = new Wifi_P2P_Receiver(mManager, mChannel, this);

        filter = new IntentFilter();
        filter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
        filter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
        filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
        filter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);

        sendRock = (Button) findViewById(R.id.btnSendRock);
        sendPaper = (Button) findViewById(R.id.btnSendPaper);
        sendScissors = (Button) findViewById(R.id.btnSendScissors);
    }

И exqListener () обрабатывает все нажатия кнопок (у меня есть кнопка для Rock, Paper и Scissors, которая отправляет строковое «сообщение» по сети):

private void exqListener() {
        status = (TextView) findViewById(R.id.connectStatusTV);
        opponentChoice = (TextView) findViewById(R.id.opponentChoiceTV);
        gameResult = (TextView) findViewById(R.id.gameResultTV);

        btnDiscover = (Button) findViewById(R.id.btnFindPlayers);
        btnDiscover.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
                    @Override
                    public void onSuccess() {
//                        Toast.makeText(getApplication(), "Discovery Started", Toast.LENGTH_LONG).show();
                        status.setText("Discovery Started");
                    }

                    @Override
                    public void onFailure(int i) {
//                        Toast.makeText(getApplication(), "Discovery Failed", Toast.LENGTH_LONG).show();
                        status.setText("Discovery Failed");
                    }
                });
            }
        });

        playerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                final WifiP2pDevice device = deviceArray[i];
                WifiP2pConfig config = new WifiP2pConfig();
                config.deviceAddress = device.deviceAddress;

                mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
                    @Override
                    public void onSuccess() {
                        Toast.makeText(getApplication(), "Connected to " + device.deviceName, Toast.LENGTH_LONG).show();
                    }

                    @Override
                    public void onFailure(int i) {
                        Toast.makeText(getApplication(), "Not connected", Toast.LENGTH_LONG).show();
                    }
                });
            }
        });

        sendRock.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String msg = "Rock";
                playerChoice = "Rock";
                sendReceive.write(msg.getBytes());
                if(tempMsg.length() > 0){
                    colorChange();
                }
            }
        });

        sendPaper.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String msg = "Paper";
                playerChoice = "Paper";
                sendReceive.write(msg.getBytes());
                if(tempMsg.length() > 0){
                    colorChange();
                }
            }
        });

        sendScissors.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String msg = "Scissors";
                playerChoice = "Scissors";
                sendReceive.write(msg.getBytes());
                if(tempMsg.length() > 0){
                    colorChange();
                }
            }
        });
    }

У меня есть listView, который отображает все близлежащие устройства, к которым пользователь может подключиться, щелкнув по элементу.У меня также есть WifiP2pManager.ConnectionInfoListener и соответствующие методы для установки пользователя на host, а другого на client.Похоже, они работают, поэтому я не буду вставлять код, чтобы избежать как можно большего количества помех.Я также не буду публиковать методы onResume () или onPause (), поскольку они не являются проблемой.

У меня есть ServerClass, SendReceive и ClientClass, которые являются подклассами Multiplayer.Вот ServerClass:

public class ServerClass extends Thread {

        Socket socket;
        ServerSocket serverSocket;

        @Override
        public void run(){
            try {
                serverSocket = new ServerSocket(8888);
                socket = serverSocket.accept();
                sendReceive = new SendReceive(socket);
                sendReceive.start();
            } catch(IOException e) {
                e.printStackTrace();
            }
        }
    }

И мой ClientClass:

public class ClientClass extends Thread {
        Socket socket;
        String hostAdd;

        public ClientClass(InetAddress hostAddress){
            hostAdd = hostAddress.getHostAddress();
            socket = new Socket();
        }

        @Override
        public void run(){
//            super.run();
            try {
                socket.connect(new InetSocketAddress(hostAdd, 8888), 500);
                sendReceive = new SendReceive(socket);
                sendReceive.start();
            } catch(IOException e){
                e.printStackTrace();
            }
        }
    }

И, наконец, подкласс SendReceive:

private class SendReceive extends Thread {
        private Socket socket;
        private InputStream inputStream;
        private OutputStream outputStream;

        public SendReceive(Socket skt) {
            socket = skt;
            try {
                inputStream = socket.getInputStream();
                outputStream = socket.getOutputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
//            super.run();
            byte[] buffer = new byte[1024];
            int bytes;

            while(socket != null){
                try {
                    bytes = inputStream.read(buffer);
                    if(bytes > 0){
                        handler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        public void write(byte[] bytes){
            try {
                outputStream.write(bytes);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

Последнее, что я забыл упомянуть, это мойметод-обработчик, который сам по себе является частью класса Multiplayer.Вот что я делаю с сообщениями:

Handler handler = new Handler(new Handler.Callback() {
        @SuppressLint("ResourceAsColor")
        @Override
        public boolean handleMessage(Message message) {
            switch(message.what){
                case MESSAGE_READ:
                    byte[] readBuff = (byte[]) message.obj;
                    tempMsg = new String(readBuff, 0, message.arg1);
                    opponentChoice.setText("Your opponent chooses " + tempMsg + "!");
                    break;
            }
            return true;
        }
    });

Я считаю, что одной из основных проблем является метод write(byte[] bytes), так как каждый раз, когда я нажимаю одну из кнопок, например Rock, Я получаю NetworkOnMainThreadException, где stackTrace указывает на строку sendReceive.write(msg.getBytes()); кода кнопки.Поэтому я думаю, что это имеет какое-то отношение к классу SendReceive, находящемуся в основном потоке пользовательского интерфейса, чего я и не думал, поскольку я переопределил метод run (), когда у меня был подкласс, расширяющий класс Thread.

Любая помощь будет принята с благодарностью.

...