Асинхронная задача внутри метода onReceive BroadcastReceiver не работает?

У меня есть рабочее приложение, которое отправляет строковые данные из текстового поля ввода приложения на TCP-сервер на моем компьютере. Я добавил класс BroadcastReceiver для отправки сообщения на компьютер при его получении. Приложение получает сообщения (Toast работает), но я не могу отправить полученное сообщение на ПК.

Ошибок нет. Приложение просто пропускает вызываемые методы.

Как мне вызвать метод внутри onReceive?

Спасибо за любую помощь!

public class SmsReceiver extends BroadcastReceiver {
String phone;
String message;

@Override
public void onReceive(Context context, Intent intent) {
    Bundle intentExtras = intent.getExtras();

    if (intentExtras != null) {
        // Get Messages
        Object[] sms = (Object[]) intentExtras.get("pdus");

        for (int i = 0; i < sms.length; ++i) {
            // Parse Each Message
            SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) sms[i]);

            phone = smsMessage.getOriginatingAddress();
            message = smsMessage.getMessageBody().toString();
            System.out.println("Message is: "+message);
            Log.i("sth","message "+message);

            //What i have tried
            sendMessage(message);

            //creating instance
            MessageSender messageSender = new MessageSender();
            messageSender.execute(message);

            //calling metod from MainActivity
            ((MainActivity)context.getApplicationContext()).send2(message);

            MainActivity.send2(message);

            //toast is working fine
            Toast.makeText(context,"Alert:"+ phone + ": " + message, Toast.LENGTH_SHORT).show();
        }
    }
}
public void sendMessage(String s)
{
    MessageSender messageSender = new MessageSender();
    messageSender.execute(s);
}

Основная деятельность

public class MainActivity extends AppCompatActivity {

EditText e1;

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

    e1 = (EditText)findViewById(R.id.editText);
    //This works
    MessageSender messageSender = new MessageSender();
    messageSender.execute("App launched!");
}

//On Button click. Working good.
public void send(View v)
{
    MessageSender messageSender = new MessageSender();
    messageSender.execute(e1.getText().toString());
}
//For test purposes
public static void send2(String s)
{
    MessageSender messageSender = new MessageSender();
    messageSender.execute(s);
}

MessageSender

public class MessageSender extends AsyncTask<String,Void,Void>
{
Socket s;
DataOutputStream dos;
PrintWriter pw;

@Override
public Void doInBackground(String... voids) {

    String message = voids[0];
try {
    s = new Socket("192.168.1.69", 7800);
    pw = new PrintWriter(s.getOutputStream());
    pw.write(message);
    pw.flush();
    pw.close();
    s.close();

}catch(IOException e)
{
    e.printStackTrace();
}
    return null;
}

}

Добавлен код сервера:

  public class MyServerFrame extends javax.swing.JFrame 
  {
  static Socket s;
   static ServerSocket ss;
  static InputStreamReader isr;
  static BufferedReader br;
  static String message;public MyServerFrame() 
 {

}

public static void main(String args[]) 
{   
    try 
    {
        ss = new ServerSocket(7800);
        while(true)
        {
            s=ss.accept();
            isr = new InputStreamReader(s.getInputStream());
            br = new BufferedReader(isr);
            StringBuilder everything = new StringBuilder();
            String line;
            while( (line = reader.readLine()) != null) 
            {
               everything.append(line);
            }
            message = everything.toString();//multiple to onelinestring 
            System.out.println(message);
        }   
    }catch (IOException e) 
    {
        e.printStackTrace();
    }
}

}

Как вы думаете, почему приложение пропускает метод? А можете ли вы разместить код своего сервера на компьютере?

SteelToe 27.03.2018 19:21

Спасибо за ваш ответ! Добавлен серверный код. У меня нет ошибок при получении сообщения, однако я не получаю сообщение на сервер. С другой стороны, Toast работает, поэтому строка сообщения не равна нулю.

shtob 28.03.2018 00:51

Буду признателен за любые мысли или возможные решения.

shtob 28.03.2018 00:52

Я бы посоветовал вам использовать цикл while при чтении из буферизованного считывателя, пока он не равен нулю, чтобы убедиться, что он печатает все данные, которые поступают из сокета, а не только первую строку

SteelToe 28.03.2018 02:54

Да, он не читал все строки в нескольких строках. Обновлен код сервера выше. Теперь это так (проверено на получение текста из многострочного текстового поля при нажатии кнопки). Однако это не решило проблему. При получении смс по-прежнему ничего не происходит. Я только что получил тост.

shtob 28.03.2018 14:27

Есть ли у них брандмауэр на компьютере, можете ли вы попробовать пинговать компьютер с устройства Android?

SteelToe 28.03.2018 16:50

Нет брандмауэра. Pinging (реальное устройство) успешно (без потери пакетов, разумный ping ~ 10 мс). Я тестирую это приложение как на AVD, так и на реальном смартфоне, подключенном к Wi-Fi в той же сети, что и ПК. Получение таких же результатов. Спасибо за попытку. Есть другие предложения?

shtob 28.03.2018 18:13

Я просто пытаюсь прояснить ситуацию, когда вы нажимаете кнопку, она работает нормально, но приемник по щелчку только показывает тост, но не отправляет сообщение, это правильно?

SteelToe 28.03.2018 18:46

Я понял это, поскольку широковещательные приемники предназначены для короткого кода, который работает быстро, ваша программа будет убита до выполнения AsyncTask. Чтобы решить эту проблему, нужно запустить службу, которая выполняет сетевой запрос в вашем методе onREceive.

SteelToe 28.03.2018 18:51

Да, я могу отправить набранный текст из текстового поля или любой другой строки при вызове метода нажатием кнопки. Когда Android получает sms-сообщение, onReceive показывает тост, но не отправляет сообщение на ПК.

shtob 28.03.2018 19:03

Я попробую ваше предложение и сообщу, когда решу.

shtob 28.03.2018 19:06
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
11
1 283
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема в том, что, поскольку вы выполняете сетевой запрос с помощью асинхронной задачи в методе onReceive, метод onReceive возвращается до выполнения AsyncTask, поскольку асинхронная задача является асинхронной. Поэтому процесс, в котором запущен метод onRequest, становится низкоприоритетным, поскольку метод onReceive вернулся, и операционная система убьет его до того, как ваша асинхронная задача действительно выполнится.

Вот два решения:

Сначала убедитесь, что получатель объявлен в вашем манифесте Android, а затем выполните одно из следующих действий.

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

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

    @Override
    public void onReceive(final Context context, final Intent intent) {
    //create a pending intend that you will pass to the Async task so you can tell the system when the Async Task finished so that it can recycle.
    final PendingResult pendingResult = goAsync();
     AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String,  Integer, String>() {
        @Override
        protected String doInBackground(String... params) {
            //put the network calling code in here
    
            // Must call finish() so the BroadcastReceiver can be recycled.
            pendingResult.finish();
            return data;
        }
    };
    asyncTask.execute();
     }
    

Второй способ, на мой взгляд, будет проще, поэтому я бы рекомендовал использовать его, поскольку он не требует от вас создания совершенно новой службы.

Прежде всего, я хочу извиниться за то, что потратил ваше время. Ваше второе решение работает так же, как и простые вызовы методов в моем вопросе. Причина была в том, что получатель не был указан в файле манифеста. Я даже не подумал это проверить, так как Toast от ресивера работал в AVD (не знаю почему). Теперь все работает нормально.

shtob 29.03.2018 13:51

Спасибо за ваши усилия и извините за отсутствие у меня опыта программирования на Android.

shtob 29.03.2018 13:52

если мы реализуем метод onPostExecute, то где будет место для pendingResult.finish (); ?

user1090751 06.10.2020 05:17

Я получаю эту ошибку "Вызвано java.lang.IllegalStateException: трансляция уже завершена на android.content.BroadcastReceiver $ PendingResult.sendFinished‌ (BroadcastReceiver.j‌ ava: 248) в android.content.BroadcastReceiver $ PendingResult.finish (BroadcastReceiver castReceiver.java:23‌ 3) "

user1090751 06.10.2020 05:18

Спасибо SteelToe за то, что подвели меня к этому тривиальному решению:

Receiver was not declared in manifest file.

Другие вопросы по теме