为什么在使用意图服务(intent service)重启应用后,界面倒计时停止更新?

huangapple go评论61阅读模式
英文:

Why does the UI stop updating countdown after restarting the app while using intent service?

问题

我正在尝试在一个Intent Service的循环内,通过一个文本视图显示倒计时。我在Intent Service和Activity之间使用了结果接收器类进行通信。当我第一次启动服务时,它运行得很好。文本视图会在服务中的每次循环运行时显示倒计时。

但是当我关闭并重新启动应用程序时,文本视图不会显示倒计时,只会显示硬编码的文本,与此同时,服务仍在后台运行。

这是我MainActivity的代码片段:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    // ... (其余代码未翻译,只保留重要部分)

    public class MessageReceiver extends ResultReceiver {

        // ... (其余代码未翻译,只保留重要部分)

        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            super.onReceiveResult(resultCode, resultData);
            if (resultCode == 1 && resultData != null){
                final String countdown = resultData.getString("countdown");

                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        serviceCountdown.setText(countdown);
                    }
                });
            }
        }
    }
}

这是我Intent Service类的代码:

public class MyIntentService extends IntentService {

    // ... (其余代码未翻译,只保留重要部分)

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {

        ResultReceiver resultReceiver = intent.getParcelableExtra(MainActivity.RECEIVER_INTENT_EXTRA_NAME);
        Log.d(TAG, "onHandleIntent: called");
        synchronized (this) {
            for (int i = 10; i >= 0; i--) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d(TAG, "Service is looping : " + i);

                Bundle bundle = new Bundle();
                bundle.putString("countdown", String.valueOf(i));
                resultReceiver.send(1, bundle);
            }
        }
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy: called");
        super.onDestroy();
    }
}

在实际项目中,我的意图并不是使用循环来显示倒计时。这只是为了测试和调试目的。

英文:

I am trying to show a countdown in a textview from a loop inside an Intent Service. I am using the result receiver class for the communication between Intent Service and Activity. It works fine when I start the service for the first time. The textview shows the countdown for each time the loop runs in the service.

But when I close and launch the app again the textview doesn't show the countdown and only shows the hard coded text, while on the other hand the service stills runs in the background.

Here is my code snippet for the MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

 public static final String RECEIVER_INTENT_EXTRA_NAME = "message_receiver_intent_extra";
 private static final String TAG = "MainActivity";
 private Intent intent;
 MyIntentService myIntentService;
 public TextView serviceCountdown;
 private Button startButton, stopButton;
 private Handler handler;

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

    myIntentService = new MyIntentService();
    startButton = findViewById(R.id.startServiceButton);
    stopButton = findViewById(R.id.stopServiceButton);

    startButton.setOnClickListener(this);
    stopButton.setOnClickListener(this);

    handler = new Handler();
    serviceCountdown = findViewById(R.id.serviceCountdown);
    MessageReceiver messageReceiver = new MessageReceiver(handler);

    // send intent service
    intent = new Intent(this, MyIntentService.class);
    intent.putExtra(RECEIVER_INTENT_EXTRA_NAME, messageReceiver);

 }

 @Override
 public void onClick(View v) {

    if (startButton.equals(v)) {
        ContextCompat.startForegroundService(getApplicationContext(), intent);
    }
    if (stopButton.equals(v)){
        stopService(intent);
    }
 }

 public class MessageReceiver extends ResultReceiver {

    public MessageReceiver(Handler handler) {
        super(handler);
    }

    @Override
    protected void onReceiveResult(int resultCode, Bundle resultData) {
        super.onReceiveResult(resultCode, resultData);
        if (resultCode == 1 && resultData != null){
            final String countdown = resultData.getString("countdown");

            handler.post(new Runnable() {
                @Override
                public void run() {
                    serviceCountdown.setText(countdown);
                }
            });
        }
    }
 }
}

And here is my code for the Intent Service Class

public class MyIntentService extends IntentService {

private static final String CHANNEL_ID = "my_channel_id";
private static final String TAG = "MyIntentService";

public MyIntentService() {
    super("MyIntentService");
    setIntentRedelivery(true);
}

@Override
public void onCreate() {
    super.onCreate();

    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent,0);

    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("My Service Title")
            .setContentText("This is sample notification text")
            .setSmallIcon(R.drawable.ic_battery)
            .setContentIntent(pendingIntent)
            .build();
    startForeground(1, notification);
}


@Override
protected void onHandleIntent(@Nullable Intent intent) {

    ResultReceiver resultReceiver = intent.getParcelableExtra(MainActivity.RECEIVER_INTENT_EXTRA_NAME);
    Log.d(TAG, "onHandleIntent: called");
    synchronized (this) {
        for (int i = 10; i >= 0; i--) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.d(TAG, "Service is looping : " + i);

            Bundle bundle = new Bundle();
            bundle.putString("countdown", String.valueOf(i));
            resultReceiver.send(1, bundle);
        }
    }
}

@Override
public void onDestroy() {
    Log.d(TAG, "onDestroy: called");
    super.onDestroy();
}
}

In the real project, my intention is not to using the loop to show a countdown. It is just for testing and debugging purpose.

答案1

得分: 0

从您的服务向活动使用本地广播接收器。现在您从MainActivity Intent中获取ResultReceiver。当活动被销毁时,意图也被销毁。在您的服务类中使用此广播接收器代码。

LocalBroadcastManager localBroadcastManager = 
        LocalBroadcastManager.getInstance(this);
Intent sendIntent = new Intent(INTENT_ACTION_KEY);
sendIntent.putExtra(DATA_KEY, data);
localBroadcastManager.sendBroadcast(sendIntent);

在您的活动中获取此本地广播接收器。

BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent != null) {
            //执行您的逻辑。
        }
    }
};

确保在活动启动时注册此广播,并在停止时取消注册。

@Override
public void onStart() {
    super.onStart();

    LocalBroadcastManager.getInstance(this).registerReceiver((broadcastReceiver),
            new IntentFilter(INTENT_ACTION_KEY));
}

@Override
public void onStop() {
    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
    super.onStop();
}
英文:

Use Local Broadcast Receiver from your service to activity. Now you getting the ResultReceiver from the MainActivity Intent. When the activity destroyed intents are also destroyed.
Use this Broadcast Receiver code in your service class.

LocalBroadcastManager localBroadcastManager = 
        LocalBroadcastManager.getInstance(this);
        Intent sendIntent = new Intent(INTENT_ACTION_KEY);
        sendIntent.putExtra(DATA_KEY, data);
        localBroadcastManager.sendBroadcast(sendIntent);

Get this local broadcast receiver in your activity.

  BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null) {
             //Perform your logic.
            }
        }
    };

Make sure you register this broadcast when activity starts and unregister it when stop.

 @Override
public void onStart() {
    super.onStart();

    LocalBroadcastManager.getInstance(this).registerReceiver((broadcastReceiver),
            new IntentFilter(INTENT_ACTION_KEY));
}

@Override
public void onStop() {
    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
    super.onStop();
}

huangapple
  • 本文由 发表于 2020年5月5日 15:39:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/61608081.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定