¿Relanzar / recrear programáticamente una actividad?

121

Después de hacer algún cambio en mi base de datos, que implica un cambio significativo en mis vistas, me gustaría volver a dibujar, volver a ejecutar onCreate.

¿Cómo es eso posible?

Pentium10
fuente

Respuestas:

129

ACTUALIZACIÓN : Android SDK 11 agregó un recreate()método a las actividades.


Lo hice simplemente reutilizando la intención que inició la actividad. Defina una intención starterIntenten su clase y asígnela onCreate()usando starterIntent = getIntent();. Luego, cuando quieras reiniciar la actividad, llamafinish(); startActivity(starterIntent);

No es una solución muy elegante, pero es una forma sencilla de reiniciar tu actividad y obligarla a recargar todo.

Steve Haley
fuente
8
De hecho, creo que cuando la orientación del dispositivo cambia, la creación de la actividad se llama de manera similar ... así que no me importa hacer esto. startActivity (getIntent ()); terminar();
Raja
No es inteligente hacer startActivity (getIntent ()) ya que solo superpondrá actividades encima de las actividades. Necesito terminar la actividad anterior
Fallenreaper
8
@Fallenreaper Sugerí llamar finish()inmediatamente después del startActivity()precisamente por esa razón ...
Steve Haley
7
Lo logré llamando primero finish();luegostartActivity(starterIntent);
Carlo Rodríguez
3
Entonces cual es? finish () y luego startActivity? ¿O al revés?
Jeff Padgett
92

Llame al método recrear de la actividad.

FernandoEscher
fuente
19
Esto está bien si su aplicación solo tiene como objetivo el nivel de SDK 11 y superior. De lo contrario, seguiría el enfoque de Steve Haley.
TalkLittle
1
@FernandoEscher Desafortunadamente, solo está disponible en dispositivos Honeycomb y posteriores.
IgorGanapolsky
2
dónde llamar al método de recreación
SAndroidD
1
llamada al método recrear repaetdedly fourcefully cerrar la aplicación
SAndroidD
Solía ​​utilizar, recreate()pero ahora veo un problema extraño en el que los botones de radio no se restablecen cuando se vuelven a crear, pero lo hacen cuando, finish(); startActivity(getIntent());así que lo estoy usando por ahora y veo cómo funciona en los próximos días o semanas.
Ben
35

Combinando algunas respuestas aquí, puede usar algo como lo siguiente.

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

Pruebas

Lo probé un poco y hay algunos problemas:

  1. Si la actividad es la más baja de la pila, la llamada startActivity(...); finish();solo existe a la aplicación y no reinicia la actividad.
  2. super.recreate()en realidad, no actúa de la misma manera que recrear totalmente la actividad. Es equivalente a rotar el dispositivo, por lo que si tiene mensajes de Fragmentcorreo setRetainInstance(true)electrónico, no se volverán a crear; simplemente hizo una pausa y reanudó.

Así que actualmente no creo que haya una solución aceptable.

Timmmm
fuente
5
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMBen lugar de usar11
S.Thiongane
4
11 es correcto, .HONEYCOMB no es correcto, porque su código en SDK <11 no sabe qué es HONEYCOMB.
Tapa Save
6
reemplazar startActivity(getIntent());finish();afinish();startActivity(getIntent());
ahmed hamdy
No, de acuerdo con la recomendación, debe apuntar al SDK más alto disponible, por lo que Honeycomb estará disponible en el momento de la compilación y la constante int se transportará dentro de su aplicación. Creo que es un patrón común usar un sdk int mayor que el mínimo sdk int.
Hai Zhang
32

Opción 1

Llame recreate()a su Activity. Sin embargo, este método hace que aparezca una pantalla negra intermitente durante la recreación de la actividad.

opcion 2

finish();
startActivity(getIntent());

Aquí no hay pantalla negra "parpadeante", pero verá una transición entre las instancias antiguas y nuevas con un fondo negro no tan agradable. Podemos hacerlo mejor.

Opción 3

Para solucionar esto, podemos agregar una llamada a overridePendingTransition():

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

Adiós pantalla negra, pero en mi caso todavía veo algún tipo de transición (una animación de desvanecimiento), esta vez sobre un fondo de color. Esto se debe a que está terminando la instancia actual de su actividad antes de que se cree la nueva y se vuelva completamente visible, y el color intermedio es el valor del windowBackgroundatributo del tema.

Opción 4

startActivity(getIntent());
finish();

Llamar finish() después startActivity() utilizará la transición predeterminada entre actividades, a menudo con una pequeña animación deslizante. Pero la transición aún es visible.

Opcion 5

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

Para mí, esta es la mejor solución porque reinicia la actividad sin ninguna transición visible, como si no pasara nada.

Podría ser útil si, por ejemplo, en su aplicación expone una forma de cambiar el idioma de visualización independientemente del idioma del sistema. En este caso, siempre que el usuario cambie el idioma de su aplicación, probablemente querrá reiniciar su actividad sin transición, haciendo que el cambio de idioma parezca instantáneo.

flawyte
fuente
1
Un problema con la opción 5 es que agrega la actividad anterior al backstack. Llame a esto varias veces y su usuario tendrá que hacer clic atrás varias veces para llegar a la página anterior real.
Ollie
23

Cuando necesito reiniciar una actividad, uso el siguiente código. Aunque no se recomienda.

Intent intent = getIntent();
finish();
startActivity(intent);
Ayush Goyal
fuente
1
Solución muy limpia y elegante. Funciona muy bien en dispositivos anteriores a SDK 11.
IgorGanapolsky
Tuve problemas con el método super.recreate (), sin embargo, esto funciona bien en Lollipop
6

para API anteriores a 11, no puede usar recreate (). Resolví de esta manera:

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

y en 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
fuente
3

Después de buscar el implemento de pan de jengibre recreate, me gustaría usar los siguientes códigos (para pan de jengibre):

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

Para estos códigos, proviene de la implementación en una api superior.

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 no tiene requestRelaunchActivity, sin embargo, desde el diff, encontré esto:

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

Entonces creo que podría usar en scheduleRelaunchActivitylugar de requestRelaunchActivity.

Y los he escrito usando reflect:

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

}

Estoy usando estos códigos para el back-porting de xposed framework.

liudongmiao
fuente
¡Fantástico trabajo! Probé en un emulador, y este enfoque es compatible con versiones anteriores Build.VERSION_CODES.ECLAIR_MR1(v7). También puede funcionar en versiones anteriores.
Tim Cooke
3

Llame al recreate() método desde donde desea recrear su actividad. Este método destruirá la instancia actual de Activity cononDestroy() y luego crear la actividad con onCreate().

neo
fuente
1

Si este es su problema, probablemente debería implementar otra forma de completar la vista en su Actividad. En lugar de volver a ejecutarlo onCreate(), debe hacer que onCreate()llame a su método de llenado con algún argumento. Cuando los datos cambian, el método de relleno debería llamarse con otro argumento.

MrSnowflake
fuente
1

La forma en que lo resolví es usando Fragmentos . Estos son compatibles con versiones anteriores hasta API 4 mediante el uso de la biblioteca de soporte.

Realiza un diseño "contenedor" con un FrameLayout en él.

Ejemplo:

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

Luego, crea una FragmentActivity en la que puede reemplazar el FrameLayout en cualquier momento que desee.

Ejemplo:

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

En el Fragmento que infla / reemplaza, puede usar onStart y onCreateView como normalmente usaría onCreate de una actividad.

Ejemplo:

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

Además, dependiendo de su situación, es posible que necesite en getActivity().recreate();lugar de solorecreate() .

Por ejemplo, debe usarlo si lo está haciendo recreate()en la clase que se ha creado dentro de la clase de actividad.

danyapd
fuente
0

Una vez hice una aplicación de prueba que carga, elimina y luego vuelve a descargar el archivo de base de datos usando el almacenamiento en la nube de firebase. Para mostrar los datos en la base de datos, el siguiente código fue la única solución que encontré. Ni recreate()tampoco finish()funcionó en este caso.

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

Descubrí la mejor manera de actualizar su Fragmento cuando cambian los datos

si tiene un botón "buscar", debe inicializar su lista ARRAY dentro del botón

mSearchBtn.setOnClickListener (nuevo View.OnClickListener () {

@Override public void onClick (Ver 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
fuente
-2

Si desea pasar un parámetro a onCreate (), entonces debe crear una nueva intención agregando extra y llamar a StartActivity con ella. Aquí hay un ejemplo simple que hice de esta manera.

              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
fuente
convención de nomenclatura incorrecta, nombres de variables incorrectos, código realmente confuso ... -1
Ahmed Adel Ismail
-4

Si solo está buscando rehacer su vista, tuve exactamente el mismo problema. En la onResumefunción, intente poner esto:

mView = new AndroidPinballView(getApplication());

Esto también estaba en mi onCreate(), así que poner esto en el onResumetrabajo para mí :)

Scumble373
fuente