1

В данном приложении я пытаюсь добавить функцию изменение языка, сам принцип такой: выбираем язык, данные по переводу подтягиваются из файлов ресурса, и дальше пересоздается активити с переведенными надписями. Так вот я протестировал сам принцип перевода приложения, в отдельном приложении созданном специально для проверки и все работает как нужно. Но когда я вставляю добавляю эту функцию в свой проект, у меня выводится эта ошибка. Может кто сможет мне ее расшифровать и сказать в чем проблема падения проги, буду очень благодарен. Сам принцип локализации описан в моем вопросе Принцип локализации на Android. Вот класс который вызывается в активити:

private class ThreadConnected extends Thread {
        private final BluetoothSocket connectedBluetoothSocket;        private final InputStream connectedInputStream;        private final OutputStream connectedOutputStream; 

        ThreadConnected(BluetoothSocket socket) 
        {
            connectedBluetoothSocket = socket; 
            InputStream in = null; 
            OutputStream out = null; 

            try {
                in = socket.getInputStream();
                out = socket.getOutputStream();            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            connectedInputStream = in;
            connectedOutputStream = out;
        }

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

            while (true) {
                try {
                    bytes1 = connectedInputStream.read();

                    /*String strReceived = String.valueOf(bytes1);
                    final String msgReceived = String.valueOf(bytes1) + "  received:\n" + strReceived;
                    runOnUiThread(new Runnable(){
                        @Override
                        public void run() {
                            textStatus.setText(msgReceived);
                        }});*/

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

                    final String msgConnectionLost = "Connection lost:\n" + e.getMessage();
                    runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            textStatus.setText(msgConnectionLost);
                            byte l;
                            l = 'O';
                            myThreadConnected.write2(l);
                        }
                    });
                }
            }
        }

        void write(byte[] buffer)        {
            try {
                connectedOutputStream.write(buffer);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }


        void write2(byte buffer2)        {
            try {
                connectedOutputStream.write(buffer2);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        public void cancel() {
            try {
                connectedBluetoothSocket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}

То место где показывает на ошибку:

 @Override
        public void run() 
        {
            boolean success = false;
            try {
                bluetoothSocket.connect(); // попытка соединится
                success = true;
            } catch (IOException e) {
                e.printStackTrace();
                try {
                    bluetoothSocket.close(); // если ошибка вывод сообщения
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }

            if (success) {
                //connect successful
                final String msgconnected = "Connected with:  " + bluetoothDevice.getName();

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        textStatus.setText(msgconnected); 
                        listViewPairedDevice.setVisibility(View.GONE);
                        textInfo.setVisibility(View.GONE);
                        inputPane.setVisibility(View.VISIBLE); 
                        byte l = 'O';
                        myThreadConnected.write2(l); // здесь ошибку показывает
                    }
                });

                startThreadConnected(bluetoothSocket); // запуск калсса для отправки и приема данных с клиента
            } else {
                //fail
            }
        }

Ошибка:

05-07 09:30:22.813 4735-4735/com.example.andrew.prog  E/AndroidRuntime: FATAL EXCEPTION: main
                                                                             Process: com.example.andrew.diplom, PID: 4735
                                                                             java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.andrew.diplom.MainActivity$ThreadConnected.write2(byte)' on a null object reference
                                                                                 at com.example.andrew.diplom.MainActivity$ThreadConnectBTdevice$1.run(MainActivity.java:371)
                                                                                 at android.os.Handler.handleCallback(Handler.java:739)
                                                                                 at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                                 at android.os.Looper.loop(Looper.java:168)
                                                                                 at android.app.ActivityThread.main(ActivityThread.java:5885)
                                                                                 at java.lang.reflect.Method.invoke(Native Method)
                                                                                 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
                                                                                 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)
Andrew
  • 17,943
  • а myThreadConnected вы где определяете? В этой строчке ошибку выдает или в другой? – Jarvis_J May 07 '18 at 07:01
  • myThreadConnected определяется в MainActivity. Вообще все работало замечательно, пока я не решил добавить локализацию в приложение)) я не сильно понял как найти строку в которой выдает ошибку, объясните пожалуйста – Andrew May 07 '18 at 07:08
  • судя по логу: MainActivity.java:371 строчка с ошибкой. Возможно ваш тред запускается до определения переменной. – Jarvis_J May 07 '18 at 07:10
  • 1
    в стектрейсе с ошибкой эта строка подсвечена синим (имя класса и номер строки). кликнув на это вы перейдете к строке в коде – pavlofff May 07 '18 at 07:12
  • я добавил код, где показывает ошибку, в коментах в коде подписана строка – Andrew May 07 '18 at 07:19
  • myThreadConnected где-то не так инициализирован. Ошибка там. Например, непонятно, откуда .write2(). Стэктрейс напрямую говорит: не могу выполнить метод .MainActivity$ThreadConnected.write2(byte), так как он null – Jarvis_J May 07 '18 at 07:28
  • 2
    По приведённым кускам всю логику не понять, но суть в том, что при пересоздании активности у Вас потоки продолжают работать, держать в памяти и использовать поля старого её экземпляра. Вам нужно либо останавливать все движения вместе с Activity и запускать по новой, либо отвязать весь фон от UI-компонентов и вынести его в сервис. – woesss May 07 '18 at 07:30
  • Что то совсем сложно теперь, я короче кину наверное полностью код, так будет проще я думаю. Просто не сильно понятно, как пересоздать полностью всю прошу на другой язык, потому что я так понял что у меня переводится только один экран и все – Andrew May 07 '18 at 09:14
  • Вообще потоки ничего не убивали, проблемы появились после внедрения локализации в приложение. Насколько я понял проблема теперь состоит в процессе пересоздания Активити – Andrew May 07 '18 at 09:23
  • Проблема в том, что Вы не завершаете потоки вообще (while(true) и ни одного break - такой цикл завершится только с RuntimeException, либо при остановке всего процесса приложения другим способом). В пересозданной активности запускаются новые потоки, а ссылки на старые теряются и что в них происходит, одному Андроиду известно. Тоже самое у Вас могло случиться и без внедрения выбора локали - при повороте экрана, например. – woesss May 07 '18 at 09:32
  • То есть в коде, после каждого цикла while нужно поставить break?? Потому что при повороте экрана у меня тоже приложение слетает, я все искал как исправить но не смог найти решение данной проблемы. Мне вообще нужно чтобы приложение работало стабильно и без слетов, если как-то можно решить проблему падений то буду благодарен за советы)) – Andrew May 07 '18 at 09:42
  • Нужно определить условие выхода из цикла. – woesss May 07 '18 at 09:52
  • Не понял, типа через if? Просто не сильно понятно как поставить условие выхода из цикла))) я попробую скинуть сюда весь код может будет проще, а то вам по кускам кода я думаю не сильно удобно) – Andrew May 07 '18 at 09:54
  • я вот закоментил ту строку которая была подсвечена как плохая, и у меня приложение вроде не слетает, дальше я попробовал повставлять принудительное завершение потока после его использования то студия вообще не может понять что это за функция. Может есть еще какой-то способ остановления потока, потому-что я пока только приблизительно понял что нужно повставлять break в конце каждого while – Andrew May 07 '18 at 13:16
  • Я могу только посоветовать почитать уроки по циклам, ибо Вы не знаете как с ними работать. Код в теле цикла выполняется многократно, пока выражение в скобках даёт true или код не наткнётся на break - если Вы в конце поставите break без каких-либо условий, то это уже не цикл - код выполнится один раз и всё. И Вы ответ мой видели? – woesss May 07 '18 at 15:41
  • да ответ я видел, пробовал впихнуть функцию останова, но я так понял что нужно все таки будет подвязать булевскую переменную как у вас и написано. Мне почему-то показалось что можно просто написать эту функцию и она будет стопорить нужный поток. – Andrew May 07 '18 at 17:53

2 Answers2

6

Проблема в том, что Вы не завершаете потоки вообще (while(true) и ни одного break - такой цикл завершится только с RuntimeException, либо при остановке всего процесса приложения другим способом). В пересозданной активности запускаются новые потоки, а ссылки на старые теряются и что в них происходит, одному Андроиду известно. Нужно добавить логику завершения потоков (в общих чертах):

class WorkThread extends Thread {

    private boolean stopped = false;

    @Override
    public void run() {
        while(!stopped) { //проверяем в условии цикла, или
           ...
           if (/*какое-то другое условие, если нужно*/)
                break;
           ...
        }
    }

    public void stopThreadPlease() {stopped = true;}
}

Останавливаем когда поток больше не нужен:

public void onPause/*onStop?onDestroy?...*/() {
    mWorkThread.stopThreadPlease();
}
woesss
  • 12,168
  • 1
  • 17
  • 32
2

Скорей всего, вы пытаетесь обратиться к UI убитой Активити. Это происходит из-за того что вы в одном потоке убили Активити, а в другом (после того как Активити уже нет) пытаетесь ее использовать.