Je me suis retrouvé avec quelque chose comme ceci:
1) donner l'auxiliaire des trucs un peu de marge, j'ai créé une classe interne. Au moins, le laid internes sont séparés du reste du code. J'avais besoin d'un service à distance de faire quelque chose, donc le mot Something
dans le nom de la classe
private RemoteSomethingHelper mRemoteSomethingHelper = new RemoteSomethingHelper();
class RemoteSomethingHelper {
//...
}
2) il y a deux choses nécessaires pour invoquer un service à distance de la méthode: la IBinder et le code à exécuter. Puisque nous ne savons pas qui l'on devient d'abord, nous les stocker:
private ISomethingService mISomethingService;
private Runnable mActionRunnable;
Chaque fois que nous écrivons à l'un de ces champs, nous invoquons _startActionIfPossible()
:
private void _startActionIfPossible() {
if (mActionRunnable != null && mISomethingService != null) {
mActionRunnable.run();
mActionRunnable = null;
}
}
private void performAction(Runnable r) {
mActionRunnable = r;
_startActionIfPossible();
}
Bien entendu, cela suppose que l'Exécutable a accès à mISomethingService, mais cela est vrai pour runnables créé dans les méthodes de la RemoteSomethingHelper
classe.
Il est vraiment bon que l' ServiceConnection
rappels sont appelées sur le thread de l'INTERFACE utilisateur: si nous allons appeler le service des méthodes à partir du thread principal, nous n'avons pas besoin de se soucier de la synchronisation.
ISomethingService
est, bien sûr, défini par AIDL.
3) au Lieu de passer des arguments aux méthodes, nous avons créer un Exécutable qui va appeler la méthode avec ces arguments plus tard, lors de l'invocation est possible:
private boolean mServiceBound;
void startSomething(final String arg1) {
// ... starting the service ...
final String arg2 = ...;
performAction(new Runnable() {
@Override
public void run() {
try {
// arg1 and arg2 must be final!
mISomethingService.startSomething(arg1, arg2);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
4) finalement, nous obtenons:
private RemoteSomethingHelper mRemoteSomethingHelper = new RemoteSomethingHelper();
class RemoteSomethingHelper {
private ISomethingService mISomethingService;
private Runnable mActionRunnable;
private boolean mServiceBound;
private void _startActionIfPossible() {
if (mActionRunnable != null && mISomethingService != null) {
mActionRunnable.run();
mActionRunnable = null;
}
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
// the methods on this class are called from the main thread of your process.
@Override
public void onServiceDisconnected(ComponentName name) {
mISomethingService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mISomethingService = ISomethingService.Stub.asInterface(service);
_startActionIfPossible();
}
}
private void performAction(Runnable r) {
mActionRunnable = r;
_startActionIfPossible();
}
public void startSomething(final String arg1) {
Intent intent = new Intent(context.getApplicationContext(),SomethingService.class);
if (!mServiceBound) {
mServiceBound = context.getApplicationContext().bindService(intent, mServiceConnection, 0);
}
ComponentName cn = context.getApplicationContext().startService(intent);
final String arg2 = ...;
performAction(new Runnable() {
@Override
public void run() {
try {
mISomethingService.startSomething(arg1, arg2);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
}
context
est un champ dans ma classe; dans une Activité, vous pouvez le définir comme Context context=this;
Je n'ai pas besoin de files d'attente actions; si vous le faites, vous pouvez la mettre en œuvre.
Il est probable que vous aurez besoin d'un résultat de rappel dans startSomething(); je l'ai fait, mais ce n'est pas indiqué dans ce code.