Riavviare / ricreare un'attività a livello di programmazione?


121

Dopo aver apportato alcune modifiche al database, che comportano un cambiamento significativo nelle mie visualizzazioni, vorrei ridisegnare, rieseguire onCreate.

Come è possibile?

Risposte:


129

AGGIORNAMENTO : Android SDK 11 ha aggiunto un recreate()metodo alle attività.


L'ho fatto semplicemente riutilizzando l'intento che ha avviato l'attività. Definisci un intento starterIntentnella tua classe e assegnalo onCreate()nell'uso starterIntent = getIntent();. Quindi, quando vuoi riavviare l'attività, chiamafinish(); startActivity(starterIntent);

Non è una soluzione molto elegante, ma è un modo semplice per riavviare la tua attività e costringerla a ricaricare tutto.


8
in realtà penso che quando l'orientamento del dispositivo cambia, l'oncreate dell'attività viene chiamato in modo simile .. quindi non mi dispiace farlo. startActivity (getIntent ()); finire();
Raja

non è intelligente fare startActivity (getIntent ()) poiché sovrapporrà le attività alle attività. È necessario terminare la vecchia attività
Fallenreaper

8
@ Fallenreaper ho suggerito di chiamare finish()subito dopo il startActivity()per proprio questo motivo ...
Steve Haley

7
L'ho raggiunto prima chiamando finish();allorastartActivity(starterIntent);
Carlo Rodríguez il

3
Allora qual è? finish () e quindi startActivity? O viceversa?
Jeff Padgett

92

Chiama il metodo di ricreazione dell'attività.


19
Va bene se la tua app ha come target solo l'SDK di livello 11 e superiore. Altrimenti seguirò l'approccio di Steve Haley.
TalkLittle

1
@FernandoEscher Purtroppo è disponibile solo su dispositivi Honeycomb e versioni successive.
IgorGanapolsky

2
dove chiamare il metodo
recreate

1
Ricrea la chiamata al metodo, chiudi completamente l'app
SAndroidD

Prima usavo, recreate()ma ora vedo uno strano problema in cui i pulsanti di opzione non vengono ripristinati durante la ricreazione, ma lo fanno quando finish(); startActivity(getIntent());lo sto usando per ora e vedo come funziona nei prossimi giorni o settimane.
Ben

35

Combinando alcune risposte qui puoi usare qualcosa come il seguente.

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

analisi

L'ho provato un po 'e ci sono alcuni problemi:

  1. Se l'attività è la più bassa nello stack, la chiamata startActivity(...); finish();esiste semplicemente l'app e non riavvia l'attività.
  2. super.recreate()in realtà non si comporta come se si ricrea completamente l'attività. È equivalente a ruotare il dispositivo, quindi se ne hai Fragmentcon setRetainInstance(true)non verranno ricreati; si è semplicemente fermato e ha ripreso.

Quindi attualmente non credo che ci sia una soluzione accettabile.


5
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMBinvece di usare11
S. Thiongane

4
11 è corretto, .HONEYCOMB non è corretto, perché il tuo codice in SDK <11 non sa wtat è HONEYCOMB.
Tapa Save

6
sostituire startActivity(getIntent());finish();afinish();startActivity(getIntent());
ahmed hamdy

No, secondo le raccomandazioni dovresti scegliere come target l'SDK più alto disponibile, quindi il nido d'ape sarà disponibile al momento della compilazione e la costante int viene trasportata all'interno dell'applicazione. Credo che sia un modello comune utilizzare un sdk int maggiore del minimo sdk int.
Hai Zhang

32

opzione 1

Chiama recreate()il tuo Activity. Tuttavia, questo metodo fa apparire una schermata nera lampeggiante durante la ricreazione dell'attività.

opzione 2

finish();
startActivity(getIntent());

Nessuno schermo nero "lampeggiante" qui, ma vedrai una transizione tra le vecchie e le nuove istanze con uno sfondo nero non così piacevole. Possiamo fare di meglio.

Opzione 3

Per risolvere questo problema, possiamo aggiungere una chiamata a overridePendingTransition():

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

Addio schermo nero, ma nel mio caso vedo ancora una sorta di transizione (un'animazione in dissolvenza), questa volta su uno sfondo colorato. Questo perché stai finendo l'istanza corrente della tua attività prima che quella nuova venga creata e diventi completamente visibile, e il colore intermedio è il valore dell'attributo del windowBackgroundtema.

Opzione 4

startActivity(getIntent());
finish();

Chiamata finish() dopo startActivity() utilizzerà la transizione predefinita tra le attività, spesso con un po 'di slide-in animazione. Ma la transizione è ancora visibile.

Opzione 5

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

Per me questa è la soluzione migliore perché riavvia l'attività senza nessuna transizione visibile, come se non fosse successo niente.

Potrebbe essere utile se, ad esempio, nella tua app esponi un modo per cambiare la lingua di visualizzazione indipendentemente dalla lingua del sistema. In questo caso, ogni volta che l'utente cambia la lingua della tua app, probabilmente vorrai riavviare la tua attività senza transizione, rendendo il cambio di lingua istantaneo.


1
Un problema con l'opzione 5 è che aggiunge l'attività precedente al backstack. Chiamalo più volte e il tuo utente deve fare clic indietro più volte per accedere alla pagina precedente reale.
Ollie

23

Quando ho bisogno di riavviare un'attività, utilizzo il seguente codice. Anche se non è consigliato.

Intent intent = getIntent();
finish();
startActivity(intent);

1
Soluzione molto pulita ed elegante. Funziona alla grande su dispositivi pre-sdk 11.
IgorGanapolsky

Ho avuto problemi con il metodo super.recreate (), tuttavia funziona bene su Lollipop

6

per le API precedenti alla 11 non è possibile utilizzare recreate (). Ho risolto in questo modo:

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

e 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

}

3

Dopo aver cercato l'attrezzo per pan di zenzero recreate, vorrei utilizzare i seguenti codici (per il pan di zenzero):

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

Per questi codici, proviene dall'implementazione in api superiori.

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 non ha requestRelaunchActivity, tuttavia, dal diff, ho trovato questo:

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

Quindi penso che potrei usare scheduleRelaunchActivityinvece di requestRelaunchActivity.

E li ho scritti usando riflettere:

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

}

Sto usando questi codici per il back-porting del framework xposed.


Fantastico lavoro! Ho provato in un emulatore e questo approccio è retrocompatibile con Build.VERSION_CODES.ECLAIR_MR1(v7). Potrebbe funzionare anche su versioni precedenti.
Tim Cooke

3

Chiama il recreate() metodo da cui desideri ricreare la tua attività. Questo metodo distruggerà l'istanza corrente di Activity con onDestroy()e quindi ricrea l'attività con onCreate().


1

Se questo è il tuo problema, dovresti probabilmente implementare un altro modo per eseguire la visualizzazione compilando la tua attività. Invece di eseguire nuovamente onCreate(), dovresti fare in modo che onCreate()chiami il tuo metodo di riempimento con qualche argomento. Quando i dati cambiano, il metodo di riempimento dovrebbe essere chiamato con un altro argomento.


1

Il modo in cui l'ho risolto è usando Fragments . Questi sono compatibili con le versioni precedenti fino all'API 4 utilizzando la libreria di supporto.

Si crea un layout "wrapper" con un FrameLayout in esso.

Esempio:

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

Quindi crei una FragmentActivity in cui puoi sostituire FrameLayout ogni volta che vuoi.

Esempio:

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

Nel Fragment che gonfi / sostituisci puoi usare onStart e onCreateView come faresti normalmente con onCreate di un'attività.

Esempio:

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

Inoltre, a seconda della situazione, potrebbe essere necessario getActivity().recreate();anziché solo recreate().

Ad esempio, dovresti usarlo se stai facendo recreate()nella classe che è stata creata all'interno della classe di attività.


0

Una volta ho creato un'app di prova che carica, elimina e quindi scarica nuovamente il file di database utilizzando l'archiviazione cloud Firebase. Per visualizzare i dati nel database, il codice seguente è stata l'unica soluzione che ho trovato. Né recreate()né ha finish()funzionato in questo caso.

Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
System.exit(0);

0

ho scoperto il modo migliore per aggiornare il tuo frammento quando i dati cambiano

se hai un pulsante "cerca", devi inizializzare la tua lista ARRAY all'interno del pulsante

mSearchBtn.setOnClickListener (new 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

Se vuoi passare un parametro a onCreate (), devi creare un nuovo intento con l'aggiunta di extra e chiamare StartActivity con esso. Ecco un semplice esempio che ho fatto usando in questo modo.

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

cattiva convenzione di denominazione, nomi di variabili errati, codice davvero confuso ... -1
Ahmed Adel Ismail

-4

Se stai solo cercando di rifare la tua visione, ho avuto lo stesso identico problema. Nella onResumefunzione prova a mettere questo:

mView = new AndroidPinballView(getApplication());

Questo era anche nel mio onCreate(), quindi mettere questo nel ha onResumefunzionato per me :)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.