Programowo ponownie uruchomić / odtworzyć działanie?

121

Po dokonaniu jakiejś zmiany w mojej bazie danych, która pociąga za sobą znaczącą zmianę w moich widokach, chciałbym ponownie narysować, ponownie wykonać onCreate.

Jak to możliwe?

Pentium10
źródło

Odpowiedzi:

129

AKTUALIZACJA : Android SDK 11 dodał recreate()metodę do działań.


Zrobiłem to, po prostu ponownie wykorzystując intencję, która zapoczątkowała działanie. Zdefiniuj intencję starterIntentw swojej klasie i przypisz ją onCreate()za pomocą starterIntent = getIntent();. Następnie, gdy zechcesz wznowić aktywność, zadzwońfinish(); startActivity(starterIntent);

Nie jest to bardzo eleganckie rozwiązanie, ale jest to prosty sposób na ponowne uruchomienie aktywności i wymuszenie ponownego załadowania wszystkiego.

Steve Haley
źródło
8
Właściwie myślę, że kiedy orientacja urządzenia zmienia się podczas tworzenia działania, nazywa się to w podobny sposób ... więc nie mam nic przeciwko zrobieniu tego. startActivity (getIntent ()); koniec();
Raja
Nie jest mądrze wykonywać startActivity (getIntent ()), ponieważ będzie to po prostu nakładać działania na działania. Trzeba zakończyć starą aktywność
Fallenreaper
8
@Fallenreaper Zasugerowałem zadzwonienie finish()zaraz po tym startActivity()właśnie z tego powodu ...
Steve Haley
7
Osiągnąłem go nazywając pierwszy finish();potemstartActivity(starterIntent);
Carlo Rodríguez
3
Więc co to jest? finish (), a następnie startActivity? Albo na odwrót?
Jeff Padgett,
92

Wywołaj metodę odtwarzania aktywności.

FernandoEscher
źródło
19
Jest to w porządku, jeśli Twoja aplikacja jest kierowana tylko na pakiet SDK na poziomie 11 lub wyższym. W przeciwnym razie wybrałbym podejście Steve'a Haleya.
TalkLittle,
1
@FernandoEscher Niestety, jest to dostępne tylko na urządzeniach Honeycomb i nowszych.
Igor Ganapolsky
2
gdzie wywołać metodę odtworzenia
SAndroidD
1
odtworzyć wywołanie metody repaetdedly zamknij aplikację
SAndroidD
Kiedyś używałem, recreate()ale teraz widzę dziwny problem, w którym przyciski radiowe nie są resetowane podczas odtwarzania, ale robią to, kiedy finish(); startActivity(getIntent());używam tego teraz i zobaczę, jak to działa w ciągu następnych dni lub tygodni.
Ben
35

Łącząc tutaj niektóre odpowiedzi, możesz użyć czegoś takiego jak poniżej.

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();
        }
    }
}

Testowanie

Trochę to przetestowałem i jest kilka problemów:

  1. Jeśli aktywność jest najniższa na stosie, wywołanie startActivity(...); finish();po prostu istnieje aplikacji i nie powoduje ponownego uruchomienia działania.
  2. super.recreate()w rzeczywistości nie działa tak samo, jak całkowite odtworzenie czynności. Jest to równoważne z obracaniem urządzenia, więc jeśli masz jakieś Fragmentpliki, setRetainInstance(true)nie zostaną one odtworzone; po prostu zatrzymał się i wznowił.

Więc obecnie nie wierzę, że istnieje akceptowalne rozwiązanie.

Timmmm
źródło
5
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMBzamiast używać11
S. Thiongane,
4
11 jest prawdą, .HONEYCOMB nie jest w porządku, ponieważ twój kod w SDK <11 nie wie, co to jest PLASTER MIODU.
Tapa Zapisz
6
zamień startActivity(getIntent());finish();nafinish();startActivity(getIntent());
ahmed hamdy
Nie, zgodnie z zaleceniami należy kierować się na najwyższy dostępny zestaw SDK, aby plaster miodu był dostępny w czasie kompilacji, a stała int jest przenoszona w ramach aplikacji. Uważam, że powszechnym wzorcem jest użycie wartości sdk int większej niż minimum sdk int.
Hai Zhang
32

opcja 1

Zadzwoń recreate()do swojego Activity. Jednak ta metoda powoduje, że podczas odtwarzania aktywności pojawia się migający czarny ekran.

Opcja 2

finish();
startActivity(getIntent());

Brak „migającego” czarnego ekranu, ale zobaczysz przejście między starymi i nowymi instancjami z niezbyt przyjemnym czarnym tłem. Możemy zrobić lepiej.

Wariant 3

Aby to naprawić, możemy dodać wywołanie do overridePendingTransition():

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

Żegnaj czarny ekran, ale w moim przypadku nadal widzę jakieś przejście (animacja zanikania), tym razem na kolorowym tle. Dzieje się tak, ponieważ kończysz bieżącą instancję swojej aktywności, zanim nowa zostanie utworzona i stanie się w pełni widoczna, a kolor pośredni jest wartością windowBackgroundatrybutu motywu.

Opcja 4

startActivity(getIntent());
finish();

Wywołanie finish() po startActivity() spowoduje użycie domyślnego przejścia między czynnościami, często z małą animacją wsuwaną. Ale przejście jest nadal widoczne.

Opcja 5

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

Dla mnie jest to najlepsze rozwiązanie, ponieważ uruchamia ponownie aktywność bez widocznego przejścia, jakby nic się nie stało.

Może to być przydatne, jeśli na przykład w swojej aplikacji ujawnisz sposób zmiany języka wyświetlania niezależnie od języka systemu. W takim przypadku za każdym razem, gdy użytkownik zmieni język aplikacji, prawdopodobnie będziesz chciał ponownie uruchomić swoją aktywność bez przejścia, dzięki czemu zmiana języka będzie wyglądać na natychmiastową.

flawyte
źródło
1
Problem z opcją 5 polega na tym, że dodaje ona poprzednią aktywność do stosu. Wywołaj to kilka razy, a użytkownik musi kilka razy kliknąć wstecz, aby przejść do prawdziwej poprzedniej strony.
Ollie
23

Kiedy muszę ponownie uruchomić działanie, używam następującego kodu. Chociaż nie jest to zalecane.

Intent intent = getIntent();
finish();
startActivity(intent);
Ayush Goyal
źródło
1
Bardzo czyste i eleganckie rozwiązanie. Działa świetnie na urządzeniach sprzed wersji SDK 11.
Igor Ganapolsky
Miałem problemy z metodą super.recreate (), jednak działa to OK na Lollipop
6

dla API przed 11 nie możesz użyć recate (). Rozwiązałem w ten sposób:

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

i w 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

}
Francesco Ditrani
źródło
3

Poszukując narzędzia do piernika dla recreate, chciałbym użyć następujących kodów (dla pierników):

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

W przypadku tych kodów pochodzi z implementacji w wyższym 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 nie ma requestRelaunchActivity, jednak z różnicy znalazłem to:

             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);
             }

Więc myślę, że mógłbym użyć scheduleRelaunchActivityzamiastrequestRelaunchActivity .

Napisałem je za pomocą refleksji:

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);
    }

}

Używam tych kodów do wstecznego przenoszenia platformy Xposed.

liudongmiao
źródło
Fantastyczna robota! Testowałem w emulatorze i to podejście jest wstecznie kompatybilne z Build.VERSION_CODES.ECLAIR_MR1(v7). Może działać również na starszych wersjach.
Tim Cooke
3

Wywołaj recreate() metodę z miejsca, w którym chcesz odtworzyć swoją aktywność. Ta metoda zniszczy bieżące wystąpienie działania z, onDestroy()a następnie ponownie utworzy działanie z onCreate().

neo
źródło
1

Jeśli to jest twój problem, prawdopodobnie powinieneś zaimplementować inny sposób wypełniania widoku w swoim działaniu. Zamiast uruchamiać ponownie onCreate(), powinieneś tak zrobić, aby wywołać onCreate()metodę wypełniania z jakimś argumentem. Gdy dane ulegną zmianie, metoda fill powinna zostać wywołana z innym argumentem.

MrSnowflake
źródło
1

Sposób, w jaki to rozwiązałem, polega na użyciu fragmentów . Są one kompatybilne wstecz do API 4 przy użyciu biblioteki obsługi.

Tworzysz układ „opakowujący” z FrameLayout w nim.

Przykład:

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

Następnie tworzysz FragmentActivity, w którym możesz zamienić FrameLayout w dowolnym momencie.

Przykład:

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();
  }

W fragmencie, który nadmuchujesz / zastępujesz, możesz użyć onStart i onCreateView tak, jak normalnie używałbyś onCreate działania.

Przykład:

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
źródło
1

W zależności od sytuacji możesz potrzebować getActivity().recreate();zamiast tylko recreate().

Na przykład, powinieneś go używać, jeśli robisz recreate()w klasie, która została utworzona w klasie aktywności.

danyapd
źródło
0

Kiedyś stworzyłem aplikację testową, która przesyła, usuwa, a następnie ponownie pobiera plik bazy danych przy użyciu magazynu w chmurze Firebase. Aby wyświetlić dane w bazie danych, poniższy kod był jedynym rozwiązaniem, które znalazłem. Ani recreate()nie finish()działał w tym przypadku.

Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
System.exit(0);
Hasan El-Hefnawy
źródło
0

Znalazłem najlepszy sposób na odświeżenie Twojego fragmentu, gdy zmienią się dane

jeśli masz przycisk „szukaj”, musisz zainicjować listę ARRAY wewnątrz przycisku

mSearchBtn.setOnClickListener (new View.OnClickListener () {

@Override public void onClick (widok 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 );

    }
Francis Eyogo
źródło
-2

Jeśli chcesz przekazać parametr do onCreate (), musisz utworzyć nową intencję, dodając dodatkowe i wywołać z nią StartActivity. Oto prosty przykład, który zrobiłem w ten sposób.

              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);
              }
Mustafa Güven
źródło
zła konwencja nazewnictwa, złe nazwy zmiennych, naprawdę zagmatwany kod ... -1
Ahmed Adel Ismail
-4

Jeśli chcesz tylko powtórzyć swój pogląd, miałem dokładnie ten sam problem. W onResumefunkcji spróbuj umieścić to:

mView = new AndroidPinballView(getApplication());

To też było w moim onCreate(), więc włożenie tego onResumezadziałało dla mnie :)

Szumówka373
źródło