8

I have a service that needs to notify the main activity. I use LocalBroadcastManager, and it works fine, but LocalBroadcastManager has been deprecated.

This is my actual code in the service:

public void onTokenRefresh() {
      
    /* build the intent */
    Intent intent = new Intent(ACTION_TOKENREFRESHED);
    intent.putExtra("token", "xxx");
    
    /* send the data to registered receivers */
    try{
      LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    } catch (Throwable e){
      //no exception handling
    }  
  
  }

In the main activity, I get informed of the notification like this :

context.registerReceiver(broadcastReceiver, intentFilter);

What can I use now to remove the deprecated warning? All examples I found regarding sending data from service to activity use LocalBroadcastManager. Can someone give me a workable model for migrating my existing code?

NOTE

In my example, The onTokenRefresh is called from inside a background thread. That is very important because it means I can simultaneously receive several onTokenRefresh, and I must forward all these tokens to the activity. Most of the offered solutions use live data but make a declaration like :

public static final MutableLiveData<String> tokenLiveData = new MutableLiveData<>();

Background Thread1:
tokenLiveData.postValue(Token1);

Background Thread2 (at same time):
tokenLiveData.postValue(Token2);

Will forward ALL tokens received simultaneously to the main activity that observes the tokenLiveData? Will the main activity always receive for sure token1 and token2?

Ere Männistö
  • 518
  • 2
  • 20
zeus
  • 12,173
  • 9
  • 63
  • 184
  • 1
    One way to solve this dilemma is to use application level live data that any activity can observe & manipulate it from service. – Jeel Vankhede Oct 31 '22 at 14:58
  • @JeelVankhede thanks, but do you have a sample of this? – zeus Oct 31 '22 at 15:37
  • 1
    Have the service update some sort of repository or other singleton. Have that repository expose a reactive API (RxJava, `LiveData`, etc.) that your UI layer (e.g., viewmodel) can observe. – CommonsWare Oct 31 '22 at 15:53
  • thanks @commonsWare, do you have a work example? on the net when I search for sending data from service to activity I only found sample with LocalBroadcastManager – zeus Oct 31 '22 at 16:01
  • If you mean examples of repository singletons and reactive APIs, I demonstrate it in [this free book](https://commonsware.com/Jetpack) in several places, including [this sample](https://commonsware.com/Jetpack/pages/chap-internet-003). I do not show anything involving a service, as few apps need services nowadays. But, in the end, it is not significantly different than having a UI work with a repository, other than perhaps differences in how you get your hands on the repository singleton. – CommonsWare Oct 31 '22 at 16:16
  • More generally, the use of a layered architecture, including repositories, has been covered quite a bit online in the past few years. See, for example, [the documentation](https://developer.android.com/topic/architecture). – CommonsWare Oct 31 '22 at 16:18
  • Use AIDL feature of Android. It's inbuilt feature of android. Reference : https://developer.android.com/guide/components/aidl – Aniruddh Parihar Nov 25 '22 at 07:10
  • thanks @AniruddhParihar but how to use AIDL ? – zeus Nov 25 '22 at 07:15
  • wait I am Posting the Answer. – Aniruddh Parihar Nov 25 '22 at 07:17
  • 1
    @zeus : Check the Posted Answer. – Aniruddh Parihar Nov 25 '22 at 10:47

5 Answers5

4

Make a service class and define a LiveData to replace the LocalBroadcastManager responsibility like so:

//This service sends an example token ten times to its observers
public class MyService extends Service {
    //Define a LiveData to observe in activity
    public static final MutableLiveData<String> tokenLiveData = new MutableLiveData<>();

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //You need a separate thread if you don not use IntentService

        Thread thread1 = new Thread() {
            public void run() {
                for (int i = 0; i < 10; i++) {
                    //send random strings az an example token ten times.
                    //You can remove this loop and replace it with your logic
                    String token1 = UUID.randomUUID().toString();
                    new Handler(Looper.getMainLooper()).post(() -> sendTokenToObserver("Thread1: " + token1));

                }
            }
        };
        thread1.start();

        Thread thread2 = new Thread() {
            public void run() {
                for (int i = 0; i < 10; i++) {
                    String token2 = UUID.randomUUID().toString();
                    new Handler(Looper.getMainLooper()).post(() -> sendTokenToObserver("Thread2: " + token2));
                }
            }
        };
        thread2.start();
        return START_STICKY;
    }

    //Post token to observers
    public void sendTokenToObserver(String token) {
        tokenLiveData.setValue(token);

    }
}

Then start the service in the activity and observe the LiveData like below:

public class MainActivity extends AppCompatActivity {

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

        //You can observe the emitted token here and do what you want(Show in view or notification). 
        //I've just logged it to the console.
        startService(new Intent(this,MyService.class));
        MyService.tokenLiveData.observe(this, token -> Log.d("token", token));
    }
}

You can also start the service from another activity and observe it in the MainActivity;

Squti
  • 4,171
  • 3
  • 10
  • 21
  • 1
    Static data can be very error prone, especially for testing. If you use "static" data, better move it to your application class. – Aorlinn Nov 23 '22 at 21:00
  • How can it be error-prone in my example? Moreover, the application class is not responsible for handling static data. @Aorlinn – Squti Nov 24 '22 at 03:59
  • For instance when you have tests that read from it or behavior that rely on it. The next test will always have the value of the previous one, and you don't know if the value was written this time or in the test before. If you put it to application class it's removed and recreated next time. – Aorlinn Nov 24 '22 at 09:45
  • You use services in integration tests. In integration tests all of the states and variables will be reset in eache test case. Moreover the LiveData field is final so you can't reassign it. – Squti Nov 24 '22 at 13:55
  • When you say integration tests you are speaking about Android Instrumentation tests, right? If so, static variables are not reset automatically (only if you do it by your self explicitly. But in that case you always need to remember to do so). If not, tell me what you are using, then I definitely need take a look at it. – Aorlinn Nov 24 '22 at 14:14
  • Every time you test a scenario test runner runs a new instance of the app with default states. – Squti Nov 24 '22 at 18:58
  • That is done by "killing" all objects, such as Application etc. But as all tests are run within the same process it does not affect static fields. Test it yourself: add a static field to e.g. your Application class and initialize it with new Random().nextInt(). You will see that every test will have the same value, which was not the case, if it was reinitialized for every new test execution. – Aorlinn Nov 24 '22 at 19:11
  • @Squti I have one question, if we remove the "Thread.sleep(1000);" does the activity will still receive ALL the messages sent via sendTokenToObserver or some will not view/handled by the activity ? IE does it's will work like a queue ? – zeus Nov 25 '22 at 22:50
  • LiveData postValue() posts a task to the main thread to set the given value. If you called this method multiple times before the main thread executed a posted task, only the last value would be dispatched and previous values will be dropped. If you need to access all dispatched values you have to use setValue() method instead. But the problem is that you need to call setValue() on the main thread and in the above example you need to move sendTokenToObserver() out of run() method of Thread class. If you do a heavy or long-running operation on the main thread you may block the UI rendering.@zeus – Squti Nov 26 '22 at 05:47
  • @Squti thanks for your help, I updated the question and add a note at the end regarding background threading. does your solution will still work in such scenario ? – zeus Nov 26 '22 at 08:51
  • I've updated my solution and it will guarantee all of the tokens emitted by any thread would be observable by main activity. @zeus – Squti Nov 26 '22 at 10:11
  • 2
    Have you heard of Kotlin and Flows? – IgorGanapolsky Mar 08 '23 at 15:26
3

If you have the possibility to use LiveData, this would be the simplest and cleanest option. If you have complex data which you don't want to share within the LiveData or if it's not thread save, you can just use LiveData for signaling and read the particular data in a thread save way.

However, if this is not possible/accurate there are options with event busses, such as GreenRobot (there is that guide for instance). But they may lack some of the same problems, why they deprecated LocalBroadcastManager.

An alternative is to use a global broadcast. If you have complex data you might use it for signaling only and add a ContentResolver or similar to get the data. But note that it is not very performant, as it's handled by the OS and trying to find any appropriate BroadcastRecievers on the device and doing a lot of RAM transactions (you find it in that video from Google)

So for a local only used Service you could use IBinder/Binder.

If you have simple data, you use transact(int code, Parcel data, Parcel reply, int flags), where the third parameter is the data you want back as an answer from the Service. But there are two restrictions:

  1. It's a parcel, so only serializable data is possible
  2. From the service you cannot directly notify the client (Activity in your case).

If you have more complex data, you can implement a specific method in your Binder class and use parameters and return values.

If you implement a LiveData in the Binder, you can use that for notification or, if the data is not very complex (like the token in your case) to contain the whole data.

You would implement it like that:

public class MyService extends Service
{
    protected MutableLiveData<String> mToken;

    public MyService()
    {
        mToken = new MutableLiveData<>("current token");
    }

    @Override
    public IBinder onBind(Intent intent)
    {
        return new Binder();
    }

    public class Binder extends android.os.Binder
    {
        public void changeToken(String newToken)
        {
            mToken.postValue(newToken);
        }

        public LiveData<String> getToken()
        {
            return mToken;
        }
    }
}

and your Activity:

public class MainActivity extends AppCompatActivity
{
    private ActivityMainBinding binding;
    private ServiceConnection mConnection;
    
    @Override
    protected void onStart()
    {
        super.onStart();
        // Bind to LocalService
        Intent intent = new Intent(this, MyService.class);
        mConnection = new ServiceConnection();
        // If your service is already running, this will just bind it.
        // If it's not running, it will also start it because of the second argument
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop()
    {
        super.onStop();
        if (mConnection != null)
        {
            unbindService(mConnection);
        }
    }

    /**
     * Defines callbacks for service binding, passed to bindService()
     */
    private class ServiceConnection implements android.content.ServiceConnection
    {
        @Override
        public void onServiceConnected(ComponentName className,
                                       IBinder service)
        {
            // We've bound to MyService.Binder, cast the IBinder and get MyService.Binder instance
            MyService.Binder binder = (MyService.Binder) service;
            binder.getToken().observe(MainActivity.this, (newToken) -> binding.Token.setText(newToken));
            binding.StartButton.setOnClickListener((click) -> binder.changeToken("newToken" + new Random().nextInt()));
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0)
        {
            mConnection = null;
        }
    }
}

I tested the example, it's working ;-)

If for any reason you cannot bind Service and Activity that tight, you can instead establish a LiveData (of whatever kind) in your Application class, listen to it in your Activity and change it from your Service. But only do that, if the Service is a) very independent from your Activity and b) running most of the time. Otherwise you will have LiveData that is inappropriate most of the time.

I personally often use "Services". Not in the means of how Android describe them, but singleton classes that are responsible for some part of my application (you might also call them controllers, even though it's not completely accurate). I then register them as singleton with Hilt (Dagger some time ago). In your case you could then establish LiveData on such a "Service".

Update

Because of your update that you must not loose any of these tokens, the answer is a bit trickier.

If you are sure, that the update might not happen more frequent than, let's say every 10 seconds, you can safely use the code above. If it can happen more frequent or you are not sure then don't.

Why? Because there are two methods on LiveData: setValue and postValue. setValue performes the notification immediately and so every new token will be dispatched to the observer. But it can only be called from the main thread, which is not the case for you. So you need to call postValue which posts a task to the end of the internal task list of the main thread. If a new call to postValue is done before the notification is executed, no new task is added to the queue and only the latest value will be delivered.

What can you do instead? You might alter the service to the following:

public class MyService extends Service
{
    protected MutableLiveData<Integer> mVersion = new MutableLiveData<>(0);
    protected ConcurrentLinkedQueue<String> mTokens = new ConcurrentLinkedQueue<>();

    public void newToken(String token)
    {
        mTokens.offer(token);
        mVersion.postValue(mVersion.getValue() + 1);
    }

    @Override
    public IBinder onBind(Intent intent)
    {
        return new Binder();
    }

    public class Binder extends android.os.Binder
    {
        public AbstractQueue<String> getTokens()
        {
            return mTokens;
        }

        public LiveData<Integer> getSignal()
        {
            return mVersion;
        }
    }
}

On your activity you just call mTokens.poll() as long as the queue is not empty. But this only works, if your service only runs, when the activity is present (more or less), and your activity is the only one that binds to your service (=> is the only consumer). If the service might run in background, this could eventually end in a memory overflow, if you have lots of new tokens. Plus you could loose your data, if for any reason your service is shut down by the OS.

A better way is to persist the data somewhere and only use the LiveData to keep the version. Your activity can then keep a local variable containing the latest version and only read the remaining tokens. To do so you can use several approaches:

There are of course more ways, but as I don't know enough about the tokens and what you want to do with them I cannot even say, which of these approaches fits the best for you.

Aorlinn
  • 718
  • 5
  • 16
  • thanks Aorlin, but I need a workable example, do you have one ? I do not want to use a tiers library like GreenRobot for the same reason why they deprecated LocalBroadCastManager – zeus Nov 23 '22 at 06:05
  • 1
    Because the answer of @MariosP does not show how you bind service and activity I added an example. – Aorlinn Nov 23 '22 at 20:26
  • thanks for your help, I updated the question and add a note at the end regarding background threading. does your solution will still work in such scenario ? – zeus Nov 26 '22 at 08:51
  • It depends ;-) See my update... – Aorlinn Nov 26 '22 at 16:46
  • Your answer was very good, thanks! but it's like all the others, so i simply gave the bounty to the one who have the most vote :( – zeus Nov 29 '22 at 08:41
3

As per the official documentation of LocalBroadcastManager:

This class is deprecated. LocalBroadcastManager is an application-wide event bus and embraces layer violations in your app: any component may listen events from any other. You can replace usage of LocalBroadcastManager with other implementation of observable pattern, depending on your usecase suitable options may be androidx.lifecycle.LiveData or reactive streams.

So you can replace it with other observable pattern like LiveData or reactive streams. Because LiveData is most popular nowadays and it is lifecycle-aware which ensures that it updates app component observers like activities, fragments, or services which have an active lifecycle state only below i will describe the steps to replace the LocalBroadcastManager using LiveData.

1.)Declare a NotificationLiveData class which extends from LiveData<T> where T is a String in the below example:

public class NotificationLiveData extends LiveData<String> {

    public void setNotification(String message){
        postValue(message);
    }
}

2.)Declare a NotificationManager class which is responsible to update the message like below:

public class NotificationManager {

    private static final NotificationLiveData notificationLiveData = new NotificationLiveData();

    public static void updateNotificationMessage(String message){
        notificationLiveData.setNotification(message);
    }

    public static NotificationLiveData getNotificationLiveData() {
        return notificationLiveData;
    }
}

3.)In every Activity you want to listen for updates you can observe the change like below:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    NotificationManager.getNotificationLiveData().observe(this, notificationObserver);
}

private final Observer<String> notificationObserver = new Observer<String>() {
    @Override
    public void onChanged(String s) {
        //do anything after a change happened
    }
};

4.)In every Fragment you want to listen for updates you can observe the change like below:

public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    NotificationManager.getNotificationLiveData().observe(getViewLifecycleOwner(), notificationObserver);
} 

private final Observer<String> notificationObserver = new Observer<String>() {
    @Override
    public void onChanged(String s) {
        //do anything after a change happened
    }
};

5.)From your Service you can send a message like below:

NotificationManager.updateNotificationMessage("My message");

6.)Finally don't forget to remove the observer from any Activity or Fragment in onDestroy() method like below:

@Override
protected void onDestroy() {
    super.onDestroy();
    NotificationManager.getNotificationLiveData().removeObserver(notificationObserver);
}

The above will listen the change only when the Activity/Fragment has an active lifecycle state. In case you want to listen always the change independent of the lifecycle state you can use observeForever instead of observe like below:

NotificationManager.getNotificationLiveData().observeForever(notificationObserver);

Update:

As per your new requirement having multiple of background Threads which are sending simultaneously a number of Tokens you cannot use anymore the postValue method as it does not guarantee that the specific Token will be received from the observer if the postValue is called a lot of times simultaneously so in that case only the latest token will be received to the observer. You can test this if you call the postValue() and immediately call the getValue() method where you may not receive the value you have just set. However this can be solved only by using the setValue method which is executed from the Main Thread only. When the Main Thread sets the value, then you will get immediately the value in the observer but be careful the observe listens the change only if the Activity/Fragment has an active lifecycle state. If you want to listen always the change independent of the lifecycle you must use the observeForever instead.

So you need to change the NotificationLiveData class to use the setValue in the Main Thread like below:

public class NotificationLiveData extends MutableLiveData<String> {

    public void setNotification(String message){
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                setValue(message);
            }
        });
    }
}

But in the Multithreading environment it is better to use RxJava which uses reactive streams for sending bunch of data simultaneously. RxJava has a better level of abstraction around threading which simplifies the implementation of complex concurrent behavior so it suits better in this case. Below i will describe how you can achieve this using RxJava instead of LiveData.

RxJava - Approach

1.)First declare in your app dependencies the below Reactivex implementations:

implementation 'io.reactivex.rxjava3:rxandroid:3.0.2'
implementation 'io.reactivex.rxjava3:rxjava:3.1.5'

Here is the official documentation of ReactiveX/RxAndroid for more details.

2.)Declare the new NotificationManager class to be like the below:

public class NotificationManager {

    private static final Subject<String> notificationSubject = PublishSubject.create();

    public static void updateNotificationMessage(String message){
        notificationSubject.onNext(message);
    }

    public static Subject<String> getNotificationSubject() {
        return notificationSubject;
    }
} 

The PublishSubject represents an Observer and an Observable at the same time, allowing multicasting events from a single source to multiple child Observers. It emits to an observer all the subsequent items of the source Observable at the time of the subscription.

3.)You can subscribe to any Activity like the below sample:

Disposable disposable = NotificationManager.getNotificationSubject()
        .doOnNext(s -> { Log.d("OnNext=", s);})
        .doOnComplete(() -> { })
        .doOnError(throwable -> { })
        .subscribe();

The doOnNext is called every time you do a call to the notificationSubject.onNext(token).

4.)In your Service in each of your Threads you can emit simultaneously the new tokens like the below sample:

Thread thread1 = new Thread() {
    public void run() {
        for (int i = 0; i < 10; i++) {
            NotificationManager.updateNotificationMessage("Thread1:" + i);
        }
    }
};
thread1.start();

Thread thread2 = new Thread() {
    public void run() {
        for (int i = 0; i < 10; i++) {
            NotificationManager.updateNotificationMessage("Thread2:" + i);
        }
    }
};
thread2.start();

5.) Finally in onDestroy() method of your Activity you have to dispose the PublishSubject like below:

@Override
protected void onDestroy() {
  super.onDestroy();
  disposable.dispose();
}

And the results of the above multithreading example will be something like the below:

D/OnNext=: Thread2:0
D/OnNext=: Thread2:1
D/OnNext=: Thread2:2
D/OnNext=: Thread2:3 
D/OnNext=: Thread2:4
D/OnNext=: Thread2:5
D/OnNext=: Thread1:0
D/OnNext=: Thread2:6
D/OnNext=: Thread2:7
D/OnNext=: Thread1:1
D/OnNext=: Thread2:8
D/OnNext=: Thread1:2
D/OnNext=: Thread2:9
D/OnNext=: Thread1:3
D/OnNext=: Thread1:4
D/OnNext=: Thread1:5
D/OnNext=: Thread1:6
D/OnNext=: Thread1:7
D/OnNext=: Thread1:8
D/OnNext=: Thread1:9

Also if you know when the transmission of data is finished you can simply use the notificationSubject.onComplete() method and the .doOnComplete(() -> { }) will be called in the Activity and automatically disposes the PublishSubject Observable. A similar call can be used when something goes wrong using the notificationSubject.onError(new Throwable()) which triggers the Activity .doOnError(throwable -> { }) callback.

MariosP
  • 8,300
  • 1
  • 9
  • 30
  • thanks for your help, I updated the question and add a note at the end regarding background threading. does your solution will still work in such scenario ? – zeus Nov 26 '22 at 08:51
  • Your answer was very good, thanks! but it's like all the others, so i simply gave the bounty to the one who have the most vote :( – zeus Nov 29 '22 at 08:40
1

The Android Interface Definition Language (AIDL) is analogous to other IDLs you would possibly have worked with. It allows you to define the programming interface that both the client and repair agree upon so as to speak with one another using interprocess communication (IPC). Traditionally the Android way a process cannot access the memory of some other process. So in order to be able to communicate they decompose their objects into primitives that the Operating System can understand well, and marshall the objects across that boundary for you. The code to try to do that marshaling is tedious to write down, so Android handles it for you with AIDL.

Note: Using AIDL is important as long as you permit clients from different applications to access your service for IPC and need to handle multithreading in your service.

But Before you go off start designing your own AIDL, do note that calls to an AIDL are straightaway right function calls! So you shouldn’t assume about the thread during the execution of whom the decision occurs. What happens is different counting on whether the decision is from a thread within the local process or a foreign process.

Specifically

  1. Calls made up of the local process are executed within the same thread that’s making the decision. If this call is from your main UI thread often then that will continue to exec within the AIDL interface, however in case if it is another one (thread) then that one will execute yours within the service!

  2. Calls from a foreign process are dispatched from a thread pool the platform maintains inside your own process. you want to be prepared for incoming calls from unknown threads, with multiple calls happening at an equivalent time.

  3. The oneway keyword decides how the behavior of the remote call would be, When used, a foreign call doesn’t block; it simply sends the transaction data and immediately returns. The implementation of the interface eventually receives this as a daily call from the Binder thread pool as a traditional remote call. If oneway is employed with an area call, there’s no impact and therefore the call remains synchronous.

Defining an AIDL Interface

You must define your AIDL interface in a .aidl file using the Java programming language syntax, then reserve it within the ASCII text file (in the src/ directory) of both the appliance hosting the service and the other application that binds to the service. To create a .aidl file use this link!

Well, here’s an example:

Process A needs info of Call status to work out whether it must change Call Type (for instance changing voice to video). you’ll get call status from certain listeners but to vary Call type from Audio to Video, Process A needs a hook to vary. This “Hook” or way of adjusting calls is usually a part of “> a part of Telephony Classes which are part of Telephony Process. So as to get such a piece of information from the Telephony process, One may write a telephony service (which runs as a neighborhood of android telephony process), which can allow you to question or change call type. Since Process A(Client) here is using this remote Service which communicates with the Telephony process to change call type, it must have an interface to speak to the service. Since Telephony service is that the provider and Process A (client) is that the user, they both got to agree on an interface (protocol) they will understand and cling to. Such an interface is AIDL, which allows you to speak (via a foreign service) to the Telephony process and obtain some work done.

Simply put in laymen’s terms, AIDL is an “agreement” the Client gets, which tells it about the way to ask for service. The service itself will have a replica of that agreement(since it published for its clients). Service will then implement details on how it handles once an invitation arrives or say when someone is lecturing it

So process A requests to vary call via Service, Service gets the request, it talks to telephony process(since it’s a part of it) and changes call to video.

An important point to notice is, AIDL is merely necessary for a multithreading environment. you’ll do away with Binders if you do not get to affect multithreaded arch. Example: Process A needs info of Call status to work out whether it must change Call Type (for example Audio to Video Call or Vice-versa). you’ll get call status from certain listeners but to vary Call type from Audio to Video, Process A needs a hook to vary. This “Hook” or way of adjusting calls is usually a part of “> a part of Telephony Classes which are part of Telephony Process. So as to get such information from the Telephony process, One may write a telephony service (which runs as a neighborhood of the android telephony process), which can allow you to question or change call type. Since Process A(Client) here is using this remote Service which communicates with the Telephony process to change call type, it must have an interface to speak to the service. Since Telephony service is that the provider and Process A (client) is that the user, they both got to agree on an interface (protocol) they will understand and cling to. Such an interface is AIDL, which allows you to speak (via a foreign service) to the Telephony process and obtain some work done.

Simply put in laymen’s terms, AIDL is an “agreement” the Client gets, which tells it about the way to ask for service. The service itself will have a replica of that agreement(since it published for its clients). Service will then implement details on how it handles once an invitation arrives or say when someone is lecturing it

So process A requests to vary call via Service, Service gets the request, it talks to telephony process(since it’s a part of it) and changes call to video.

An important point to notice is, AIDL is merely necessary for a multithreading environment. you’ll do away with Binders if you do not get to affect multithreaded arch.

Here’s a code snippet to guide you in action:

// Declare any non-default types
// here with import statements
interface AidlInterface {
        int add(int x,int y);
        int sub(int x,int y);
    }

The AIDL Service for the above code is:

public class AidlServiceGfG extends Service {

    private static final String TAG = "AIDLServiceLogs";
    private static final String className = " AidlService";

    public AidlService() {
        Log.i(TAG, className+" Constructor");
    }

    @Override
    public IBinder onBind(Intent intent) {
        // GfG Example of Binder
        Log.i(TAG, className+" onBind");
        return iCalculator.asBinder();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, className+" onCreate");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, className+" onDestroy");
    }

    ICalculator.Stub iCalculator = new ICalculator.Stub() {
        @Override
        public int add(int x, int y) throws RemoteException {
            Log.i(TAG, className+" add Thread Name: "+Thread.currentThread().getName());
            int z = x+y;
            return z;
        }

        @Override
        public int sub(int x, int y) throws RemoteException {
            Log.i(TAG, className+" add Thread Name: "+Thread.currentThread().getName());
            int z = x-y;
            return z;
        }
    };

}

And here’s the service connection:

// Returns the stub
ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, className + " onServiceConnected");
            iCalculator = ICalculator.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

            unbindService(serviceConnection);
        }
    };

Conclusion

AIDL uses the binder kernel driver to form calls. once you make a call, a way identifier and every one of the objects are packed onto a buffer and copied to a foreign process where a binder thread waits to read the info. When calls are made within the same process and therefore the same backend, no proxy objects exist, then calls are direct with no packing or unpacking.

Aniruddh Parihar
  • 3,072
  • 3
  • 21
  • 39
0

I think you can consinder to use EventBus, link here: https://github.com/greenrobot/EventBus

FAQ: https://greenrobot.github.io/EventBus/

Q: How's EventBus different to Android's BroadcastReceiver/Intent system?

A: Unlike Android's BroadcastReceiver/Intent system, EventBus uses standard Java classes as events and offers a more convenient API. EventBus is intended for a lot more uses cases where you wouldn't want to go through the hassle of setting up Intents, preparing Intent extras, implementing broadcast receivers, and extracting Intent extras again. Also, EventBus comes with a much lower overhead.

So you can declare you data class like:

public class RefreshTokenData {
  private String token;
  private String...
  ...// Other field
}

In your activity, simple way:

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onRefreshToken(RefreshTokenData data) {
    // Do something
}

You can switch to other thread instead main by ThreadMode.

In your service:

public void onTokenRefresh() {
      
    final RefreshTokenData refreshTokenData = new RefreshTokenData("xxx", ...);
    
    EventBus.getDefault().post(refreshTokenData);
}

After the token will be send to activity in method onRefreshToken declared above.

Don't forget unsubscribe your activity:

@Override
public void onStart() {
     super.onStart();
     EventBus.getDefault().register(this);
}

@Override
public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
}

More details: https://greenrobot.org/eventbus/documentation/how-to-get-started/

Thread Mode and your note

Link: https://greenrobot.org/eventbus/documentation/delivery-threads-threadmode/

I suggest to use ThreadMode: MAIN_ORDERED, you will get all your refresh token sequentially because With MAIN_ORDERED, the first event handler will finish, and then the second one will be invoked at a later point in time.

dinhlam
  • 708
  • 3
  • 14