Вызов пользовательского исключения из службы в действие


В настоящее время я работаю над приложением XMPP на Android и размышляю о том, как лучше всего создать исключение другого типа, чем RemoteException, для моей активности из моего сервиса.

Поскольку кажется невозможным бросить другую вещь, чем RemoteException, используя IPC (вы не можете объявить, чтобы бросить что-нибудь в вашем .aidl), я просто вижу два решения:

  • Создайте прослушиватель для моей активности, чтобы слушать мое пользовательское исключение XMPP, которое на самом деле не будет выброшено, а просто отправляется как обычный объект, реализующий протокол Parcelable.

  • Поймайте мое XMPPException и бросьте RemoteException (с содержимым, обновленным с помощью моего XMPPException) - но в этом случае, как я могу узнать о своей активности, если это XMPP или реальное RemoteException ? Пометив имя исключения и проанализировав его на моей активности ? Это была бы настоящая кровь.

У тебя есть какие-нибудь идеи ? Я пропустил что-то из документации SDK ?

Спасибо.

3 6

3 ответа:

Если #1 означает то, что я думаю, что он делает, я бы использовал это -- заставил службу перехватить исключение и вызвать метод на AIDL-определенный объект обратного вызова, созданный и предоставленный действием.

Вы можете увидеть пример этой техники в этомклиентском исервисном проекте из одной из моих книг.

Похоже, что мы можем создавать пользовательские исключения, производные от RemoteException. Таким образом, Вы можете иметь XMPPRemoteException или просто универсальное MyRemoteException, которое будет содержать исходное исключение. Ниже приведена демонстрация для второго случая:

Сервер:

try {
    ...
}
catch(XMPPException e) {
    throw new MyRemoteException(e);
}

Клиент:

try {
    service.someCall();
}
catch(MyRemoteException e) {
    rethrow(e);
}

Вспомогательный метод:

private void rethrow(MyRemoteException e) throws Exception {
    if(e.innerException instanceof XMPPException)
        throw (XMPPException)e.innerException;
    else
        throw e.innerException;
}

Исключение:

public class MyRemoteException extends RemoteException {
    private static final long serialVersionUID = 1L;
    public Exception innerException;

    public MyRemoteException() {}

    public MyRemoteException(Exception innerException) {
        this.innerException = innerException;
    }
}

Я думаю, что невозможно добиться "бросить пользовательское исключение из службы в действие". Смотрите ресурс посылки:

    /**
 * Use this function for customized exception handling.
 * customized method call this method for all unknown case
 * @param code exception code
 * @param msg exception message
 */
public final void readException(int code, String msg) {
    switch (code) {
        case EX_SECURITY:
            throw new SecurityException(msg);
        case EX_BAD_PARCELABLE:
            throw new BadParcelableException(msg);
        case EX_ILLEGAL_ARGUMENT:
            throw new IllegalArgumentException(msg);
        case EX_NULL_POINTER:
            throw new NullPointerException(msg);
        case EX_ILLEGAL_STATE:
            throw new IllegalStateException(msg);
    }
    throw new RuntimeException("Unknown exception code: " + code
            + " msg " + msg);
}
Таким образом, мы можем знать, что мы просто можем бросить эти пять исключений выше.

Например: Если ваш сервис создает исключение IllegalArgumentException:

    @Override
public void addImage(final int align, final byte[] imageData) throws RemoteException {
    log("/// addImage ///");
    if (imageData == null) {
        throw new IllegalArgumentException("The second argument(image data) can not be empty!");
    }
...
}

Ваш клиент может поймать его:

            try {
                printer.addImage(0, null);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }