Программно перезапустить / воссоздать активность? - PullRequest
105 голосов
/ 21 марта 2010

После того, как я внесу некоторые изменения в свою базу данных, которые повлекут за собой значительные изменения в моих представлениях, я хотел бы перерисовать, повторно выполнить onCreate.

Как это возможно?

Ответы [ 12 ]

109 голосов
/ 21 марта 2010

ОБНОВЛЕНИЕ : Android SDK 11 добавил метод recreate() к действиям.


Я сделал это, просто повторно использовав начатое намерениеактивность.Определите намерение starterIntent в своем классе и назначьте его в onCreate(), используя starterIntent = getIntent();.Затем, когда вы захотите перезапустить действие, позвоните finish(); startActivity(starterIntent);

Это не очень элегантное решение, но это простой способ перезапустить вашу деятельность и заставить ее перезагрузить все.

85 голосов
/ 22 октября 2011

Вызовите воссоздать метод действия.

35 голосов
/ 30 октября 2012

Комбинируя здесь несколько ответов, вы можете использовать что-то вроде следующего:

class BaseActivity extends SherlockFragmentActivity
{
    // Backwards compatible recreate().
    @Override
    public void recreate()
    {
        if (android.os.Build.VERSION.SDK_INT >= 11)
        {
            super.recreate();
        }
        else
        {
            startActivity(getIntent());
            finish();
        }
    }
}

Тестирование

Я немного его протестировал, и есть некоторые проблемы:

  1. Если активность является самой низкой в ​​стеке, то для вызова startActivity(...); finish(); просто существует приложение, а не перезапускает действие.
  2. super.recreate() на самом деле не действуеттак же, как полностью воссоздать деятельность.Это эквивалентно вращению устройства, поэтому, если у вас есть Fragment s с setRetainInstance(true), они не будут воссозданы;просто приостановился и возобновил.

Так что в настоящее время я не верю, что есть приемлемое решение.

20 голосов
/ 30 октября 2012

Когда мне нужно перезапустить занятие, я использую следующий код. Хотя это не рекомендуется.

Intent intent = getIntent();
finish();
startActivity(intent);
14 голосов
/ 14 сентября 2018

Вариант 1

Звоните recreate() на свой Activity.Однако этот метод приводит к появлению мигающего черного экрана во время повторного создания действия.

Опция 2

finish();
startActivity(getIntent());

Здесь нет «мигающего» черного экрана, но вы увидите переход междустарые и новые экземпляры с не очень приятным черным фоном.Мы можем сделать лучше.

Вариант 3

Чтобы исправить это, мы можем добавить вызов к overridePendingTransition():

finish();
startActivity(getIntent());
overridePendingTransition(0, 0);

До свидания черный экран, но в моемНа этот раз я все еще вижу какой-то переход (анимация исчезновения) на цветном фоне.Это потому, что вы заканчиваете текущий экземпляр вашей активности до , новый создается и становится полностью видимым, а промежуточный цвет является значением атрибута темы windowBackground.

Опция 4

startActivity(getIntent());
finish();

Вызов finish() после startActivity() будет использовать переход по умолчанию между действиями, часто с небольшой вставкой анимации.Но переход все еще виден.

Опция 5

startActivity(getIntent());
finish();
overridePendingTransition(0, 0);

Для меня это лучшее решение, потому что оно перезапускает активность без видимого перехода, как будто ничего не произошло.

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

6 голосов
/ 12 июня 2013

для API до 11 вы не можете использовать restate (). Я решил таким образом:

Bundle temp_bundle = new Bundle();
onSaveInstanceState(temp_bundle);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("bundle", temp_bundle);
startActivity(intent);
finish();

и в onCreate ..

@Override
public void onCreate(Bundle savedInstanceState) {

    if (getIntent().hasExtra("bundle") && savedInstanceState==null){
        savedInstanceState = getIntent().getExtras().getBundle("bundle");
    }

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //code

}
3 голосов
/ 02 июня 2014

После поиска пряничного инвентаря для recreate я бы хотел использовать следующие коды (для пряников):

activity.mMainThread.mAppThread.scheduleRelaunchActivity(activity.mToken, null, null, 0, false, null);

Для этих кодов это от реализации в более высоком API.

public void recreate() {
    if (mParent != null) {
        throw new IllegalStateException("Can only be called on top-level activity");
    }
    if (Looper.myLooper() != mMainThread.getLooper()) {
        throw new IllegalStateException("Must be called from main thread");
    }
    mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, false);
}

Api-10 не имеет requestRelaunchActivity, однако, из diff я нашел это:

             public final void scheduleRelaunchActivity(IBinder token,
                     List<ResultInfo> pendingResults, List<Intent> pendingNewIntents,
                     int configChanges, boolean notResumed, Configuration config) {
    -            ActivityClientRecord r = new ActivityClientRecord();
    -
    -            r.token = token;
    -            r.pendingResults = pendingResults;
    -            r.pendingIntents = pendingNewIntents;
    -            r.startsNotResumed = notResumed;
    -            r.createdConfig = config;
    -
    -            synchronized (mPackages) {
    -                mRelaunchingActivities.add(r);
    -            }
    -
    -            queueOrSendMessage(H.RELAUNCH_ACTIVITY, r, configChanges);
    +            requestRelaunchActivity(token, pendingResults, pendingNewIntents,
    +                    configChanges, notResumed, config, true);
             }

Так что я думаю, что мог бы использовать scheduleRelaunchActivity вместо requestRelaunchActivity.

И я написал их, используя отражение:

package me.piebridge.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.res.Configuration;
import android.os.Build;
import android.os.IBinder;

public class GingerBreadUtil {

    private static Field scanField(Class<?> clazz, String... names) {
        for (String name : names) {
            Field field;
            try {
                field = clazz.getDeclaredField(name);
                field.setAccessible(true);
                return field;
            } catch (NoSuchFieldException e) {
            }
            try {
                field = clazz.getField(name);
                field.setAccessible(true);
                return field;
            } catch (NoSuchFieldException e) {
            }
        }
        return null;
    }

    public static void recreate(Activity activity) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
            recreateHC(activity);
        } else {
            try {
                recreateGB(activity);
            } catch (InvocationTargetException e) {
                e.getTargetException().printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private static void recreateHC(Activity activity) {
        ((Activity) activity).recreate();
    }

    private static void recreateGB(Activity activity) throws IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Field Activity$mToken = scanField(Activity.class, "mToken");
        IBinder mToken = (IBinder) Activity$mToken.get(activity);
        Field Activity$mMainThread = scanField(Activity.class, "mMainThread");
        Object mMainThread = Activity$mMainThread.get(activity);
        Field ActivityThread$mAppThread = scanField(mMainThread.getClass(), "mAppThread");
        Object mAppThread = ActivityThread$mAppThread.get(mMainThread);
        Method method = mAppThread.getClass().getMethod("scheduleRelaunchActivity",
            IBinder.class, List.class, List.class, int.class, boolean.class, Configuration.class);
        method.invoke(mAppThread, mToken, null, null, 0, false, null);
    }

}

Я использую эти коды для обратного переноса платформы xposed.

1 голос
/ 03 января 2013

Я решил это, используя Фрагменты .Они обратно совместимы до API 4 с помощью библиотеки поддержки.

Вы создаете макет «обертка» с FrameLayout в нем.

Пример:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical" >

     <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/fragment_container"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
</LinearLayout>

Тогдавы создаете FragmentActivity, в котором вы можете заменить FrameLayout в любое время.

Пример:

public class SampleFragmentActivity extends FragmentActivity
{

     @Override
 public void onCreate(Bundle savedInstanceState)
 {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.wrapper);

    // Check that the activity is using the layout version with
    // the fragment_container FrameLayout
    if (findViewById(R.id.fragment_container) != null)
    {

        // However, if we're being restored from a previous state,
        // then we don't need to do anything and should return or else
        // we could end up with overlapping fragments.
        if (savedInstanceState != null)
        {
            return;
        }
        updateLayout();
     }
  }

  private void updateLayout()
  {
     Fragment fragment = new SampleFragment();
     fragment.setArguments(getIntent().getExtras());

     // replace original fragment by new fragment
     getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment).commit();
  }

В раздуваемом / заменяемом фрагменте вы можете использовать onStart и onCreateView, как обычнобудет использовать onCreate деятельности.

Пример:

public class SampleFragment extends Fragment
{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.yourActualLayout, container, false);
    }

    @Override
    public void onStart()
    {
        // do something with the components, or not!
        TextView text = (TextView) getActivity().findViewById(R.id.text1);

        super.onStart();
    }
}
1 голос
/ 21 марта 2010

Если это ваша проблема, вам, вероятно, следует реализовать другой способ выполнения представления, заполняющего вашу активность. Вместо повторного запуска onCreate() вы должны сделать так, чтобы onCreate() вызывал ваш метод заполнения с некоторым аргументом. Когда данные изменяются, метод заполнения должен вызываться с другим аргументом.

0 голосов
/ 26 июня 2019

Вызовите метод recreate(), из которого вы хотите восстановить свою активность. Этот метод уничтожит текущий экземпляр Activity с помощью onDestroy(), а затем заново создаст действие с onCreate().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...