Программно перезапустить / воссоздать действие?

121

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

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

Pentium10
источник

Ответы:

129

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


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

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

Стив Хейли
источник
8
на самом деле я думаю, что при изменении ориентации устройства oncreate активности вызывается аналогичным образом ... так что я не возражаю сделать это. startActivity (getIntent ()); финиш();
Raja
Неразумно делать startActivity (getIntent ()), так как он просто накладывает действия поверх действий. Нужно прекратить старую деятельность
Fallenreaper
8
@Fallenreaper Я предложил позвонить finish()сразу после этого startActivity()именно по этой причине ...
Стив Хейли
7
finish();Тогда я добился этого, позвонив первымstartActivity(starterIntent);
Карло Родригес
3
Так что это? finish (), а затем startActivity? Или наоборот?
Джефф Пэджетт,
92

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

FernandoEscher
источник
19
Это нормально, если ваше приложение предназначено только для SDK уровня 11 и выше. В противном случае я бы согласился с подходом Стива Хейли.
TalkLittle
1
@FernandoEscher К сожалению, это доступно только на устройствах Honeycomb и выше.
Игорь Ганапольский
2
где вызвать метод воссоздания
SAndroidD
1
повторно создать вызов метода четыре раза подряд закрыть приложение
SAndroidD
Раньше я использовал это, recreate()но теперь я вижу странную проблему, когда переключатели не сбрасываются при воссоздании, но они сбрасываются, когда finish(); startActivity(getIntent());я использую это сейчас и смотрю, как это будет работать в следующие дни или недели.
Бен
35

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

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()на самом деле не действует так же, как полное воссоздание деятельности. Это эквивалентно вращению устройства, поэтому, если у вас есть какие-либо Fragments, setRetainInstance(true)они не будут воссозданы; просто приостановился и возобновился.

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

Timmmm
источник
5
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMBвместо использования11
S.Thiongane
4
11 правильно, .HONEYCOMB неверно, потому что ваш код в SDK <11 не знает, что wtat - это HONEYCOMB.
Tapa Save
6
заменить startActivity(getIntent());finish();наfinish();startActivity(getIntent());
ahmed hamdy 09
Нет, в соответствии с рекомендацией вы должны нацеливаться на самый высокий доступный SDK, поэтому соты будут доступны во время компиляции, а константа int переносится в ваше приложение. Я считаю, что это распространенный образец использования sdk int больше минимального sdk int.
Hai Zhang
32

Опция 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);

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

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

flawyte
источник
1
Проблема с вариантом 5 заключается в том, что он добавляет предыдущее действие в backstack. Вызовите это несколько раз, и ваш пользователь должен вернуться кучу раз, чтобы перейти на настоящую предыдущую страницу.
Олли
23

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

Intent intent = getIntent();
finish();
startActivity(intent);
Аюш Гоял
источник
1
Очень чистое и элегантное решение. Отлично работает на устройствах pre-sdk 11.
Игорь Ганапольский
Были проблемы с методом super.recreate (), однако он работает
6

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

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

После поиска имбирного пряника 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, однако из сравнения я обнаружил следующее:

             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 framework.

liudongmiao
источник
Фантастическая работа! Я тестировал в эмуляторе, и этот подход обратно совместим с Build.VERSION_CODES.ECLAIR_MR1(v7). Это может работать и на более старых версиях.
Тим Кук,
3

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

нео
источник
1

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

MrSnowflake
источник
1

Я решил это с помощью фрагментов . Они обратно совместимы до 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();
    }
}
Spikey
источник
1

Также в зависимости от вашей ситуации вам может потребоваться getActivity().recreate();вместо просто recreate().

Например, вы должны использовать его, если вы работаете recreate()в классе, который был создан внутри класса активности.

danyapd
источник
0

Однажды я сделал тестовое приложение, которое загружает, удаляет, а затем повторно загружает файл базы данных, используя облачное хранилище firebase. Для отображения данных в базе данных единственным решением, которое я нашел, был следующий код. Ни то, recreate()ни другое finish()в данном случае не сработало.

Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
System.exit(0);
Хасан Эль-Хефнави
источник
0

я нашел лучший способ обновить ваш фрагмент при изменении данных

если у вас есть кнопка "поиск", вы должны инициализировать свой список ARRAY внутри кнопки

mSearchBtn.setOnClickListener (новый View.OnClickListener () {

@Override public void onClick (View v) {

mList = new ArrayList<Node>();

firebaseSearchQuery.addValueEventListener(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {


      for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {

        Node p = dataSnapshot1.getValue(Node .class);
        mList.add(p);
      }
      YourAdapter = new NodeAdapter(getActivity(), mList);
      mRecyclerView.setAdapter(YourAdapter );

    }
Фрэнсис Эйого
источник
-2

Если вы хотите передать параметр в onCreate (), вам нужно создать новое намерение с добавлением дополнительных и вызвать StartActivity с ним. Вот простой пример, который я использовал таким образом.

              String eczSabit = sa.getItem(position).getValue();
              if(!Util.IsNullOrEmpty(eczSabit)){
                  sabit = Long.parseLong(eczSabit);
                  Intent intent = new Intent(eczaneSegmentasyon.this,eczaneSegmentasyon.class);
                  intent.putExtra("sabit", sabit);
                  startActivity(intent);
              }
Мустафа Гювен
источник
неправильное соглашение об именах, неправильные имена переменных, действительно запутанный код ... -1
Ахмед Адель Исмаил
-4

Если вы просто хотите переделать свой взгляд, у меня была точно такая же проблема. В onResumeфункции попробуйте поместить это:

mView = new AndroidPinballView(getApplication());

Это тоже было у меня onCreate(), так что положив это на работу onResumeдля меня :)

Scumble373
источник