Search code examples
javaandroidaidl

Android AIDL: Service-to-Activity communication


I try to implement a Android AIDL communication strategy. I have an Activity and a Service. My Activity can successfully "talk" to my Service, but the reverse process does not seem to work.

To summarize, as the Activity and the Service run in different processes, they cannot share any data throw the IBinder interface. So the onServiceConnected() method receive an AIDL interface instead. This interface is implemented Service-side and is aimed at being used (called) Activity-side. I use this interface to register() another AIDL. This new AIDL is implemented Activity-side and called Service-side through the AIDL interface. It act like a listener. Unfortunatly, the method of this new AIDL does not seem to be called.

The Service run in its own process thanks to the following line in AndroidManifest.xml:

AndroidManifest.xml

<service android:name=".DemoService" android:process=":DemoServiceProcess" />

I have 2 AIDL files, one knowing the other.

IAidlActivity.aidl

package app.test.aidldemo;

interface IAidlActivity {
    void publish(int count);
}

IAidlService.aidl

package app.test.aidldemo;

import app.test.aidldemo.IAidlActivity;
interface IAidlService {
    void startCounter();
    void register(IAidlActivity activity);
}

The Service implements onBind() and run a handler in charge of incrementing a counter.

DemoService.java

package app.test.aidldemo;
import [...]
public class DemoService extends Service
{
    protected IAidlActivity aidlActivity;

    @Override
    public IBinder onBind(Intent intent)
    {
        return new IAidlService.Stub() {
            @Override
            public void startCounter() {
                DemoService.this.startJob();
            }
            @Override
            public void register(IAidlActivity activity) {
                DemoService.this.aidlActivity = activity;
            }
        };
    }

    public void startJob() {
        final Handler handler = new Handler();
        handler.post(new Runnable() {
            protected int count = 0;
            @Override
            public void run() {
                if (count < 500) {
                    count++;  // increment counter
                    try {  // then publish it to view
                        DemoService.this.aidlActivity.publish(count);  // interface, implemented activity-side
                    } catch (RemoteException e) {}
                    handler.postDelayed(this, 2000);  // 2sec.
                }
            }
        });
    }
}

The Activity only consist of a TextView. It start the bounding with the Service and update the view from time to time. It is also supposed to update the view when publish() is called. But that does not happen.

MainActivity.java

package app.test.aidldemo;
import [...]
public class MainActivity extends Activity {
    protected TextView view;
    protected ServiceConnection connection;

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

        view = new TextView(this);
        setContentView(view);
        appendToView("Let's go!");

        connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                IAidlService aidlService = IAidlService.Stub.asInterface(service);
                appendToView("IAidlService accessed");
                IAidlActivity.Stub aidlActivity = new IAidlActivity.Stub() {
                    @Override
                    public void publish(int count) {
                        appendToView("*** Hey, new count is: " + count + "!! ***");
                    }
                };
                appendToView("IAidlActivity created");
                try {
                    aidlService.register(aidlActivity);
                    aidlService.startCounter();  // interface, implemented service-side
                }
                catch (RemoteException e) { appendToView(e.toString()); }
            }
            @Override
            public void onServiceDisconnected(ComponentName name) {}
        };

        Intent intent = new Intent(MainActivity.this, DemoService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    @Override
    public void onDestroy() {
        unbindService(connection);
        super.onDestroy();
    }

    public void appendToView(String text) {
        view.append(text + "\n");
    }
}

I also try some variations like:

  1. run the appendToView("*** Hey... into runOnUiThread()
  2. delay the bindService() by using another handler + postDelayed()

My fallback technique would be to only use IAidlService and have a "watcher" Activity-side to constantly check the counter. But I would rather understand why it is not working, and what is the correct way to use AIDL.


Solution

  • Summary

    2 statements to change.

    DemoService.java

    final Handler handler = new Handler(DemoService.this.getMainLooper());
    

    MainActivity.java

    public void appendToView(String text) {
       view.post(new Runnable() {
           @Override
           public void run() {
               view.append(text + "\n");
           }
        });
    }