Android Target API 31 best way to run a service in background that ALWAYS keep listening to my messaging server (RabbitMQ)

825 Views Asked by At

I am trying to create a notification service in my android app that always keeps listening to my RabbitMQ server for new messages. I want it to be able to send notifications even from background. Basically I am trying to create a notification communication between two client side application (App1 and App2) through Rabbit MQ and send notifications to both the apps in case of an event.

I have implemented it using JOB Service class but it is not consistent and it stops after sometime. Can someone please help me in understanding the architecture in a better way. How can I achieve something like Firebase Messaging Service but through Rabbit MQ?

Sample code that I have used below:

public class StoreOrderJobService extends JobService {
private static final String TAG = "JobService";
Random random = new Random();
SharedPrefManager prefManager;
private boolean jobCancelled = false;

@Override
public boolean onStartJob(JobParameters jobParameters) {
    Log.d(TAG, "Job Started");
    prefManager = new SharedPrefManager(this);

    subscribeStore(prefManager.getUserId(), jobParameters);

    return true;
}


private void subscribeStore(String storeId, JobParameters parameters) {
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost(HOST_IP);
    factory.setAutomaticRecoveryEnabled(false);
    String queueName = prefManager.getSessionId();
    if (queueName != null) {
        Thread subscribeStoreThread = new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "Job Started");
                try {
                    if (jobCancelled) {
                        return;
                    }
                    Connection connection = factory.newConnection();
                    Channel channel = connection.createChannel();


                    Log.d("OrderService", "Session Id " + queueName);

                    channel.queueDeclare(queueName, false, false, false, null);
                    channel.queueBind(queueName, "store_test", storeId);
                    DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                        String message = new String(delivery.getBody(), "UTF-8");
                        Log.d("OrderService", "Received message " + message);
                        Envelope envelope = delivery.getEnvelope();
                        String routingKey = envelope.getRoutingKey();

                        if (routingKey.equals(storeId)) {
                            channel.basicAck(envelope.getDeliveryTag(), true);
                            String message_new = new String(delivery.getBody(), "UTF-8");
                            Gson gson = new Gson();
                            OrderSubscribePayload payload = gson.fromJson(message_new, OrderSubscribePayload.class);
                            Log.d("order Service", "Order Id " + payload.getOrderId());
                            sendOrderNotification(random.nextInt(), payload);
                        }
                    };
                    channel.basicConsume(queueName, false, deliverCallback, consumerTag -> {
                    });


                } catch (TimeoutException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (RuntimeException e) {
                    e.printStackTrace();
                }
            }
        });
        subscribeStoreThread.start();
    }

}

private void sendOrderNotification(int id, OrderSubscribePayload payload) {

    Log.d("Service", "sendOrderNotification " + payload.getOrderId());

    Intent contextIntent = new Intent(this, OrderDetails.class);

    Bundle args = new Bundle();
    args.putSerializable("orderDetails", (Serializable) payload);
    contextIntent.putExtra("Bundle", args);

    int iUniqueId = (int) (System.currentTimeMillis() & 0xfffffff);
    PendingIntent pIntent = PendingIntent.getActivity(this, iUniqueId, contextIntent, 0);


    Notification n = new NotificationCompat.Builder(this, ManagedApplication.CHANNEL_ORDER_ID)
            .setContentTitle("New Order")
            .setContentText("Received New Order")
            .setSmallIcon(R.drawable.ic_stat_name)
            .setContentIntent(pIntent)
            .setAutoCancel(true)
            .setOnlyAlertOnce(true)
            .setColor(getResources().getColor(R.color.color_primary))
            .setCategory(NotificationCompat.CATEGORY_REMINDER)
            .build();


    NotificationManager notificationManager =
            (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);

    notificationManager.notify(id, n);
}

@Override
public boolean onStopJob(JobParameters jobParameters) {
    Log.d(TAG, "Job Cancelled");
    jobCancelled = true;
    return true;
}

}

I am calling this job on users login as below:

private void startNotificationJob() {
    ComponentName componentName = new ComponentName(this, StoreOrderJobService.class);
    JobInfo info = new JobInfo.Builder(123, componentName)
            .setPersisted(true)
            .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
            .setPeriodic(15 * 60 * 1000)
            .build();
    JobScheduler jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);

    int result = jobScheduler.schedule(info);
    if (result == JobScheduler.RESULT_SUCCESS) {
        Log.d("JOB Scheduler", "Job Scheduled");
    } else Log.d("JOB Scheduler", "Job Scheduled Failed");
}
1

There are 1 best solutions below

4
CommonsWare On

I have implemented it using JOB Service class but it is not consistent and it stops after sometime.

Nothing will be keeping your process running, which you will need for your desired functionality. Nothing will be keeping the CPU powered on all the time, which also will be needed for your desired functionality.

Please bear in mind that what you want is rather harmful to the battery life.

How can I achieve something like Firebase Messaging Service but through Rabbit MQ?

Use a foreground service, with a continuous partial WakeLock, in a separate process (android:process manifest attribute) than the UI of your app. You may also need code to deal with re-establishing your MQ connection as the device changes connectivity (e.g., from WiFi to mobile data) or loses and then regains connectivity.

You will also need to ask your users to go into the Settings app and try to opt your app out of all battery optimizations. Note that this will not be possible on all devices.

You will also need to ask your users to never kill your app's task. The separate process may help on some devices if users forget, but that behavior seems to vary by device.

And you will need to take into account that your solution will not be reliable, because some device manufacturers to prevent developers from doing the sorts of things that you want to do, as those things are bad for battery life.