gabriel gabriel - 3 months ago 18
Android Question

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.

Answer

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");
       }
    });
}