Programmgesteuert Relaunch / Neuerstellung einer Aktivität?

121

Nachdem ich einige Änderungen an meiner Datenbank vorgenommen habe, die erhebliche Änderungen an meinen Ansichten mit sich bringen, möchte ich onCreate neu zeichnen und erneut ausführen.

Wie ist das möglich?

Pentium10
quelle

Antworten:

129

UPDATE : Android SDK 11 hat recreate()Aktivitäten eine Methode hinzugefügt .


Ich habe das getan, indem ich einfach die Absicht wiederverwendet habe, mit der die Aktivität gestartet wurde. Definieren Sie eine Absicht starterIntentin Ihrer Klasse und weisen Sie sie onCreate()mit zu starterIntent = getIntent();. Wenn Sie die Aktivität neu starten möchten, rufen Sie anfinish(); startActivity(starterIntent);

Es ist keine sehr elegante Lösung, aber es ist eine einfache Möglichkeit, Ihre Aktivität neu zu starten und sie zu zwingen, alles neu zu laden.

Steve Haley
quelle
8
Eigentlich denke ich, wenn sich die Geräteorientierung ändert, wird das Oncreate der Aktivität auf ähnliche Weise aufgerufen. Es macht mir also nichts aus, dies zu tun. startActivity (getIntent ()); Fertig();
Raja
Es ist nicht klug, startActivity (getIntent ()) auszuführen, da nur Aktivitäten über Aktivitäten gelegt werden.
Müssen
8
@Fallenreaper Ich schlug vor, finish()sofort nach dem startActivity()aus genau diesem Grund anzurufen ...
Steve Haley
7
Ich habe es finish();dann zuerst erreichtstartActivity(starterIntent);
Carlo Rodríguez
3
Also was ist es? finish () und dann startActivity? Oder umgekehrt?
Jeff Padgett
92

Rufen Sie die Methode zum erneuten Erstellen der Aktivität auf.

FernandoEscher
quelle
19
Dies ist in Ordnung, wenn Ihre App nur auf SDK Level 11 und höher abzielt. Sonst würde ich mich an Steve Haleys Ansatz halten.
TalkLittle
1
@FernandoEscher Leider ist das nur auf Honeycomb-Geräten und höher verfügbar.
IgorGanapolsky
2
wo recreate Methode aufrufen
SAndroidD
1
Methodenaufruf neu erstellen, die App automatisch schließen
SAndroidD
Früher habe ich verwendet, recreate()aber jetzt sehe ich ein seltsames Problem, bei dem Optionsfelder beim Neuerstellen nicht zurückgesetzt werden, aber wenn finish(); startActivity(getIntent());dies der Fall ist, verwende ich dies jetzt und sehe, wie es in den nächsten Tagen oder Wochen funktioniert.
Ben
35

Wenn Sie hier einige Antworten kombinieren, können Sie Folgendes verwenden.

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

Testen

Ich habe es ein bisschen getestet und es gibt einige Probleme:

  1. Wenn die Aktivität die niedrigste auf dem Stapel ist, startActivity(...); finish();existiert beim Aufrufen nur die App und die Aktivität wird nicht neu gestartet.
  2. super.recreate()verhält sich nicht so, als würde man die Aktivität vollständig neu erstellen. Dies entspricht dem Drehen des Geräts. Wenn Sie also Fragments haben, werden setRetainInstance(true)diese nicht neu erstellt. nur angehalten und wieder aufgenommen.

Daher glaube ich derzeit nicht, dass es eine akzeptable Lösung gibt.

Timmmm
quelle
5
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMBanstatt zu verwenden11
S.Thiongane
4
11 ist genau, .HONEYCOMB nicht richtig, weil Ihr Code in SDK <11 nicht weiß, was HONEYCOMB ist.
Tapa Save
6
ersetzen startActivity(getIntent());finish();zufinish();startActivity(getIntent());
ahmed hamdy
Nein, laut Empfehlung sollten Sie das höchste verfügbare SDK auswählen, damit die Wabe zur Kompilierungszeit verfügbar ist und die int-Konstante in Ihrer Anwendung enthalten ist. Ich glaube, es ist ein gängiges Muster, ein sdk int zu verwenden, das größer als das Minimum sdk int ist.
Hai Zhang
32

Option 1

Rufen recreate()Sie an Activity. Bei dieser Methode wird jedoch während der Neuerstellung der Aktivität ein blinkender schwarzer Bildschirm angezeigt.

Option 2

finish();
startActivity(getIntent());

Hier gibt es keinen "blinkenden" schwarzen Bildschirm, aber Sie sehen einen Übergang zwischen der alten und der neuen Instanz mit einem nicht so angenehmen schwarzen Hintergrund. Wir können es besser machen.

Option 3

Um dies zu beheben, können wir einen Aufruf hinzufügen an overridePendingTransition():

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

Auf Wiedersehen schwarzer Bildschirm, aber in meinem Fall sehe ich immer noch eine Art Übergang (eine Überblendanimation), diesmal auf einem farbigen Hintergrund. Dies liegt daran, dass Sie die aktuelle Instanz Ihrer Aktivität beenden, bevor die neue erstellt wird und vollständig sichtbar wird. Die Zwischenfarbe ist der Wert des windowBackgroundThemenattributs.

Option 4

startActivity(getIntent());
finish();

Wenn Sie finish() danach anrufen, startActivity()wird der Standardübergang zwischen Aktivitäten verwendet, häufig mit einer kleinen Einschubanimation. Der Übergang ist aber noch sichtbar.

Option 5

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

Für mich ist dies die beste Lösung, da die Aktivität ohne sichtbaren Übergang neu gestartet wird, als wäre nichts passiert.

Dies kann hilfreich sein, wenn Sie beispielsweise in Ihrer App eine Möglichkeit zum Ändern der Anzeigesprache unabhängig von der Systemsprache bereitstellen. In diesem Fall möchten Sie Ihre Aktivität wahrscheinlich immer dann ohne Übergang neu starten, wenn der Benutzer die Sprache Ihrer App ändert, damit der Sprachwechsel sofort angezeigt wird.

fehlerhaft
quelle
1
Ein Problem mit Option 5 besteht darin, dass die vorherige Aktivität zum Backstack hinzugefügt wird. Rufen Sie dies mehrmals auf und Ihr Benutzer muss einige Male zurückklicken, um zur eigentlichen vorherigen Seite zu gelangen.
Ollie
23

Wenn ich eine Aktivität neu starten muss, verwende ich folgenden Code. Obwohl es nicht empfohlen wird.

Intent intent = getIntent();
finish();
startActivity(intent);
Ayush Goyal
quelle
1
Sehr saubere und elegante Lösung. Funktioniert hervorragend auf Pre-SDK 11-Geräten.
IgorGanapolsky
Hatte Probleme mit der super.recreate () Methode, aber dies funktioniert Ok auf Lollipop
6

Für API vor 11 können Sie recreate () nicht verwenden. Ich habe auf diese Weise gelöst:

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

und in 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
quelle
3

Nachdem recreateich nach dem Lebkuchengerät gesucht habe , möchte ich folgende Codes (für Lebkuchen) verwenden:

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

Für diese Codes stammt es aus der Implementierung in einer höheren 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 hat keine requestRelaunchActivity, aber aus dem Diff habe ich folgendes gefunden:

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

Also ich denke ich könnte scheduleRelaunchActivitystattdessen verwenden requestRelaunchActivity.

Und ich habe sie mit Reflect geschrieben:

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

}

Ich verwende diese Codes für die Rückportierung von xposed Framework.

liudongmiao
quelle
Fantastische Arbeit! Ich habe in einem Emulator getestet und dieser Ansatz ist abwärtskompatibel zu Build.VERSION_CODES.ECLAIR_MR1(v7). Es kann auch auf älteren Versionen funktionieren.
Tim Cooke
3

Rufen Sie die recreate() Methode auf, von der aus Sie Ihre Aktivität neu erstellen möchten. Diese Methode zerstört die aktuelle Instanz von Aktivität mit onDestroy()und erstellt dann Aktivität mit neu onCreate().

Neo
quelle
1

Wenn dies Ihr Problem ist, sollten Sie wahrscheinlich eine andere Methode implementieren, um die Ansicht in Ihrer Aktivität auszufüllen. Anstatt es erneut auszuführen onCreate(), sollten Sie es so machen, dass onCreate()Ihre Füllmethode mit einem Argument aufgerufen wird. Wenn sich die Daten ändern, sollte die Füllmethode mit einem anderen Argument aufgerufen werden.

MrSnowflake
quelle
1

Ich habe es so gelöst, indem ich Fragmente verwendet habe . Diese sind bis API 4 mithilfe der Support-Bibliothek abwärtskompatibel.

Sie erstellen ein "Wrapper" -Layout mit einem FrameLayout.

Beispiel:

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

Anschließend erstellen Sie eine FragmentActivity, in der Sie das FrameLayout jederzeit ersetzen können.

Beispiel:

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

In dem Fragment, das Sie aufblasen / ersetzen, können Sie onStart und onCreateView so verwenden, wie Sie normalerweise das onCreate einer Aktivität verwenden würden.

Beispiel:

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

Abhängig von Ihrer Situation benötigen Sie möglicherweise getActivity().recreate();nicht nur recreate().

Sie sollten es beispielsweise verwenden, wenn Sie recreate()in der Klasse arbeiten, die innerhalb der Aktivitätsklasse erstellt wurde.

Danyapd
quelle
0

Ich habe einmal eine Test-App erstellt, die die Datenbankdatei mithilfe des Firebase-Cloud-Speichers hochlädt, löscht und dann erneut herunterlädt. Um die Daten in der Datenbank anzuzeigen, war der folgende Code die einzige Lösung, die ich gefunden habe. Weder recreate()noch finish()in diesem Fall funktioniert.

Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
System.exit(0);
Hasan El-Hefnawy
quelle
0

Ich habe herausgefunden, wie Sie Ihr Fragment am besten aktualisieren können, wenn sich Daten ändern

Wenn Sie eine Schaltfläche "Suchen" haben, müssen Sie Ihre ARRAY-Liste innerhalb der Schaltfläche initialisieren

mSearchBtn.setOnClickListener (neuer 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 );

    }
Francis Eyogo
quelle
-2

Wenn Sie einen Parameter an onCreate () übergeben möchten, müssen Sie eine neue Absicht mit dem Hinzufügen von extra erstellen und damit StartActivity aufrufen. Hier ist ein einfaches Beispiel, das ich auf diese Weise gemacht habe.

              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
quelle
schlechte Namenskonvention, schlechte Variablennamen, wirklich verwirrender Code ... -1
Ahmed Adel Ismail
-4

Wenn Sie nur versuchen, Ihre Ansicht zu wiederholen, hatte ich genau das gleiche Problem. onResumeVersuchen Sie in der Funktion Folgendes:

mView = new AndroidPinballView(getApplication());

Das war auch in meinem onCreate(), also hat das onResumefür mich funktioniert :)

Scumble373
quelle