Потеря ссылки на службу Android при возврате в Activity после изменения ориентации экрана

У меня есть Activity, который запускается и привязывается к Service. У меня то есть еще Activity, запущенный с первого. При возвращении к первому Activity из второго мне нужно вызвать метод Service (сохраняет некоторые данные).

При просмотре каждого Activity мои методы жизненного цикла Activity, по-видимому, адекватно справляются с изменениями ориентации экрана, при условии, что я возвращаюсь к той же ориентации экрана перед выходом из Activity.

Проблема возникает, когда я возвращаюсь к первому действию с другой ориентацией, чем когда я его оставил. Если это произойдет, я потеряю ссылку на свой Service и, следовательно, столкнусь с NullPointerException в onActivityResult(). Поэтому, если я запущу свой второй Activity в портретном режиме, переключусь на альбомный режим при просмотре второго Activity и вернусь к первому Activity в ландшафтном режиме, он вылетит.

Что я могу упустить? Я не хочу использовать файл манифеста, чтобы указать, что я буду обрабатывать изменения конфигурации - мне кажется, что это несколько уродливый хак, который не решает основную проблему. Если я опять что-то не упустил...

Вот выдержки из моих методов жизненного цикла из первого Activity:

@Override
protected void onStart()
{
super.onStart();

// start and bind to service
startService(smsIntent);
connection = new SMServiceConnection();
bindService(smsIntent, connection, Context.BIND_AUTO_CREATE);

}

@Override
protected void onRestart()
{
super.onRestart();
}

@Override
protected void onResume()
{
super.onResume();
}

@Override
protected void onPause()
{
super.onPause();
sms.save(); // autosave
}

@Override
protected void onStop()
{
super.onStop();
unbindService(connection);
// stopService(smsIntent); //doesn't appear to have any effect
}

@Override
protected void onDestroy()
{
super.onDestroy();
}

РЕДАКТИРОВАТЬ: вот выдержки из моего класса SMServiceConnection, который является частным внутренним классом в моей деятельности, который расширяется из пользовательского класса ServiceConnection.

@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
    super.onServiceConnected(name, service);
    msg("Service connected");
    sms = getSMService();
    if (sms != null)
    {
    String s = sms.getStuff(); //etc.; haven't listed every method invoked on sms

        sms.saveSurvey();
    }
    } else
    {
    System.out.println("sms is null!");
    }

}

@Override
public void onServiceDisconnected(ComponentName name)
{
    super.onServiceDisconnected(name);
    msg("Service disconnected");
}

Мой суперкласс ServiceConnection выглядит примерно так:

public class MyServiceConnection implements ServiceConnection
{
    private boolean serviceAvailable = false;
    private SMService sms;

    public void onServiceConnected(ComponentName name, IBinder service)
    {
        serviceAvailable = true;
        LocalBinder b = (LocalBinder) service;
        sms = b.getService();
    }

    public void onServiceDisconnected(ComponentName name)
    {
        serviceAvailable = false;
    }

    public boolean isServiceAvailable()
    {
    return serviceAvailable;
    }

    public SMService getSMService()
    {
    return sms;
    }


    }

person Spinner    schedule 17.02.2012    source источник
comment
Можете ли вы опубликовать код SMServiceConnection. Я бы ожидал, что там будут методы onServiceConnected(), которые сохранят связующее. Я использую внутренние классы для ServiceConnection; таким образом ServiceConnection может сохранить привязку к действию (содержащему классу).   -  person Stefan    schedule 17.02.2012
comment
Хорошо, я добавил код.   -  person Spinner    schedule 17.02.2012


Ответы (1)


Ваша проблема может заключаться в том, что onActivityResult() вызывается до того, как служба будет снова привязана, когда вы вернетесь, что происходит немного позже вызова привязки в onstart.

Я бы попробовал одно из двух:

  1. Попробуйте сохранить данные как ожидающую информацию:

    if (sms == null) {
        mPendingResultCode = resultCode;
        mPedingResultData = new Bundle(intent.getExtras()); } else {
        handleData(resultCode, intent.getExtras()); }
    

    А затем позже в onServiceConnected вызовите handleData(mPedingResultCode, mPedingResultData), если, например, mPendingResultData != null

    И не забудьте отключить mPendingResultCode и mPendingResultData или какой-либо другой индикатор, когда закончите с данными.

  2. Я не уверен в этом, но, возможно, попробую добавить обработку данных в конец очереди событий, выполнив что-то вроде этого в onActivityResult:

    final Bundle data = new Bundle(intent.getExtras);
    new Handler().postRunnable(new Runnable() {
        public void run() {
            "do some stuff with data and resultcode (which should be final in the parameter list anyway)"
        }
    }
    

Как вы сказали, вы не должны изменять манифест, чтобы не воссоздавать измененную ориентацию. Это уродливый хак, который не решает проблему, и мне не нравится, когда это предлагают. Если вы сделаете это, вы все равно рискуете, что произойдет то же самое, если какая-то другая конфигурация изменилась, например, язык, или если активность была временно уничтожена фреймворком для экономии ресурсов.

configchanges в манифесте следует использовать только в том случае, если вы хотите получить обратные вызовы при изменении, а не воссоздавать его во благо. причина. Лень не одна.

person Sebastian Olsson    schedule 17.02.2012
comment
Спасибо, я сделал что-то в соответствии с вашим первым предложением. Теперь у меня есть частный вспомогательный метод, который хранит запрос на сохранение; Затем onServiceConnected() проверяет ожидающий запрос и при необходимости выполняет его. Однако сейчас обнаружил другую проблему, закрою эту. - person Spinner; 20.02.2012