Я закончил с чем-то вроде этого:
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, но это справедливо для runnable, созданных в рамках методов класса 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
— это поле в моем классе; в Activity вы можете определить его как Context context=this;
Мне не нужно было ставить в очередь действия; если вы это сделаете, вы можете реализовать это.
Вероятно, вам понадобится обратный вызов результата в startSomething(); Я сделал, но это не показано в этом коде.
person
18446744073709551615
schedule
15.08.2013