У меня получилось что-то вроде этого:
1) чтобы придать вспомогательному материалу некоторую область, я создал внутренний класс. По крайней мере, уродливые внутренности отделены от остальной части кода. Мне нужен был удаленный сервис, выполняющий что-то , поэтому слово Something
в имени класса
private RemoteSomethingHelper mRemoteSomethingHelper = new RemoteSomethingHelper();
class RemoteSomethingHelper {
//...
}
2) есть две вещи, необходимые для вызова метода удаленного обслуживания: IBinder и код для выполнения. Поскольку мы не знаем, кто из них станет первым, мы храним их:
private ISomethingService mISomethingService;
private Runnable mActionRunnable;
Каждый раз, когда мы пишем в одно из этих полей, мы вызываем _startActionIfPossible()
:
private void _startActionIfPossible() {
if (mActionRunnable != null && mISomethingService != null) {
mActionRunnable.run();
mActionRunnable = null;
}
}
private void performAction(Runnable r) {
mActionRunnable = r;
_startActionIfPossible();
}
Это, конечно, предполагает, что Runnable имеет доступ к mISomethingService, но это верно для runnables, созданных в методах класса RemoteSomethingHelper
.
Действительно хорошо, что ServiceConnection
обратные вызовы вызываются в потоке пользовательского интерфейса : если мы собираемся вызывать методы обслуживания из основного потока, нам не нужно заботиться о синхронизации.
ISomethingService
, конечно, определяется через AIDL.
3) Вместо того, чтобы просто передавать аргументы в методы, мы создаем Runnable, который будет вызывать метод с этими аргументами позже, когда вызов возможен:
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) наконец, получаем:
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
- это поле в моем классе; в действии вы можете определить его как Context context=this;
Мне не нужны были действия с очередями; если вы это сделаете, вы можете реализовать его.
Вам, вероятно, понадобится обратный вызов результата в startSomething (); Я сделал, но это не показано в этом коде.