Relancer / recréer une activité par programmation?

121

Après avoir apporté des modifications à ma base de données, ce qui implique un changement significatif de mes vues, je voudrais redessiner, ré-exécuter onCreate.

Comment est-ce possible?

Pentium10
la source

Réponses:

129

MISE À JOUR : Android SDK 11 a ajouté une recreate()méthode aux activités.


Je l'ai fait en réutilisant simplement l'intention qui a lancé l'activité. Définissez une intention starterIntentdans votre classe et attribuez-la en onCreate()utilisant starterIntent = getIntent();. Ensuite, lorsque vous souhaitez redémarrer l'activité, appelezfinish(); startActivity(starterIntent);

Ce n'est pas une solution très élégante, mais c'est un moyen simple de redémarrer votre activité et de la forcer à tout recharger.

Steve Haley
la source
8
en fait, je pense que lorsque l'orientation de l'appareil change, oncreate de l'activité est appelé de la même manière .. donc ça ne me dérange pas de faire ça. startActivity (getIntent ()); terminer();
Raja
il n'est pas intelligent de faire startActivity (getIntent ()) car il ne fera que superposer les activités au-dessus des activités. Besoin de mettre fin à l'ancienne activité
Fallenreaper
8
@Fallenreaper, j'ai suggéré d'appeler finish()immédiatement après le startActivity()pour cette raison précisément ...
Steve Haley
7
Je parvins appeler d' abord , finish();puisstartActivity(starterIntent);
Carlo Rodríguez
3
Alors qu'est-ce que c'est? finish () et puis startActivity? Ou l'inverse?
Jeff Padgett
92

Appelez la méthode recréer de l'activité.

FernandoEscher
la source
19
Ce n'est pas grave si votre application cible uniquement le niveau 11 et supérieur du SDK. Sinon, j'irais avec l'approche de Steve Haley.
TalkLittle
1
@FernandoEscher Malheureusement, cela n'est disponible que sur les appareils Honeycomb et plus.
IgorGanapolsky
2
où appeler la méthode recréer
SAndroidD
1
recréez un appel de méthode à plusieurs reprises fermez complètement l'application
SAndroidD
J'avais l'habitude d'utiliser recreate()mais maintenant je vois un problème étrange où les boutons radio ne sont pas réinitialisés lors de la recréation, mais ils le font quand finish(); startActivity(getIntent());je l'utilise pour le moment et je vois comment cela fonctionne au cours des prochains jours ou semaines.
Ben
35

En combinant quelques réponses ici, vous pouvez utiliser quelque chose comme ce qui suit.

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

Essai

Je l'ai testé un peu, et il y a quelques problèmes:

  1. Si l'activité est la plus basse de la pile, l'appel startActivity(...); finish();existe simplement et ne redémarre pas l'activité.
  2. super.recreate()n'agit pas réellement de la même manière que recréer totalement l'activité. Il est équivalent à la rotation du dispositif , donc si vous avez des Fragments avec setRetainInstance(true)elles ne seront pas recréés; simplement mis en pause et repris.

Donc, actuellement, je ne pense pas qu'il existe une solution acceptable.

Timmmm
la source
5
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMBau lieu d'utiliser11
S.Thiongane
4
11 est exact, .HONEYCOMB n'est pas correct, car votre code dans SDK <11 ne sait pas wtat est HONEYCOMB.
Tapa Save
6
replace startActivity(getIntent());finish();tofinish();startActivity(getIntent());
ahmed hamdy
Non, conformément aux recommandations, vous devez cibler le sdk disponible le plus élevé, de sorte que le nid d'abeille sera disponible au moment de la compilation et la constante int est transportée dans votre application. Je crois que c'est un modèle courant d'utiliser un sdk int supérieur au minimum sdk int.
Hai Zhang
32

Option 1

Appelez recreate()votre Activity. Cependant, cette méthode fait apparaître un écran noir clignotant lors de la recréation de l'activité.

Option 2

finish();
startActivity(getIntent());

Pas d'écran noir "clignotant" ici, mais vous verrez une transition entre les anciennes et les nouvelles instances avec un fond noir pas si agréable. On peut faire mieux.

Option 3

Pour résoudre ce problème, nous pouvons ajouter un appel à overridePendingTransition():

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

Au revoir écran noir, mais dans mon cas je vois encore une sorte de transition (une animation de fondu), sur un fond coloré cette fois. C'est parce que vous terminez l'instance actuelle de votre activité avant que la nouvelle ne soit créée et devienne entièrement visible, et la couleur intermédiaire est la valeur de l' windowBackgroundattribut de thème.

Option 4

startActivity(getIntent());
finish();

L'appel finish() après startActivity() utilisera la transition par défaut entre les activités, souvent avec une petite animation de diapositives. Mais la transition est toujours visible.

Option 5

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

Pour moi, c'est la meilleure solution car elle redémarre l'activité sans aucune transition visible, comme si de rien n'était.

Cela peut être utile si, par exemple, dans votre application, vous exposez un moyen de changer la langue d'affichage indépendamment de la langue du système. Dans ce cas, chaque fois que l'utilisateur change la langue de votre application, vous voudrez probablement redémarrer votre activité sans transition, ce qui rend le changement de langue instantané.

imparfait
la source
1
Un problème avec l'option 5 est qu'elle ajoute l'activité précédente à la backstack. Appelez cela plusieurs fois et votre utilisateur doit cliquer plusieurs fois pour accéder à la vraie page précédente.
Ollie
23

Lorsque j'ai besoin de redémarrer une activité, j'utilise le code suivant. Bien que ce ne soit pas recommandé.

Intent intent = getIntent();
finish();
startActivity(intent);
Ayush Goyal
la source
1
Solution très propre et élégante. Fonctionne très bien sur les appareils pré-sdk 11.
IgorGanapolsky
J'ai eu des problèmes avec la méthode super.recreate (), mais cela fonctionne bien sur Lollipop
6

pour l'API antérieure à 11, vous ne pouvez pas utiliser receate (). J'ai résolu de cette manière:

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

et dans 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
la source
3

Après avoir recherché l'outil de pain d'épice pour recreate, j'aimerais utiliser les codes suivants (pour le pain d'épice):

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

Pour ces codes, c'est de l'implémentation dans une API supérieure.

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 n'a pas de requestRelaunchActivity, cependant, à partir du diff, j'ai trouvé ceci:

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

Je pense donc que je pourrais utiliser à la scheduleRelaunchActivityplace de requestRelaunchActivity.

Et je les ai écrits en utilisant refléter:

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

}

J'utilise ces codes pour le back-porting du framework xposed.

liudongmiao
la source
Travail fantastique! J'ai testé dans un émulateur, et cette approche est rétrocompatible avec Build.VERSION_CODES.ECLAIR_MR1(v7). Cela peut également fonctionner sur les anciennes versions.
Tim Cooke
3

Appelez la recreate() méthode à partir de laquelle vous souhaitez recréer votre activité. Cette méthode détruira l'instance actuelle d'Activité avec onDestroy(), puis recréera l'activité avec onCreate().

néo
la source
1

Si tel est votre problème, vous devriez probablement implémenter une autre façon de remplir la vue de votre activité. Au lieu de le relancer, onCreate()vous devriez faire en sorte qu'il onCreate()appelle votre méthode de remplissage avec un argument. Lorsque les données changent, la méthode de remplissage doit être appelée avec un autre argument.

MrSnowflake
la source
1

La façon dont je l'ai résolu est en utilisant des fragments . Ceux-ci sont rétrocompatibles jusqu'à l'API 4 en utilisant la bibliothèque de support.

Vous créez une mise en page "wrapper" avec un FrameLayout.

Exemple:

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

Ensuite, vous créez un FragmentActivity dans lequel vous pouvez remplacer le FrameLayout à tout moment.

Exemple:

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

Dans le fragment que vous gonflez / remplacez, vous pouvez utiliser onStart et onCreateView comme vous utiliseriez normalement le onCreate d'une activité.

Exemple:

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
la source
1

En fonction de votre situation, vous devrez peut-être getActivity().recreate();au lieu de simplement recreate().

Par exemple, vous devriez l'utiliser si vous faites recreate()dans la classe qui a été créée à l'intérieur de la classe d'activité.

Danyapd
la source
0

Une fois, j'ai créé une application de test qui télécharge, supprime, puis retélécharge le fichier de base de données à l'aide du stockage cloud Firebase. Pour afficher les données dans la base de données, le code suivant était la seule solution que j'ai trouvée. Ni recreate()ni finish()travaillé dans ce cas.

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

J'ai découvert la meilleure façon d'actualiser votre fragment lorsque les données changent

si vous avez un bouton "recherche", vous devez initialiser votre liste ARRAY à l'intérieur du bouton

mSearchBtn.setOnClickListener (nouveau 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
la source
-2

Si vous souhaitez passer un paramètre à onCreate (), vous devez créer un nouvel intent en ajoutant un supplément et appeler StartActivity avec. Voici un exemple simple que j'ai fait en utilisant cette façon.

              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
la source
mauvaise convention de dénomination, mauvais noms de variables, code vraiment déroutant ... -1
Ahmed Adel Ismail
-4

Si vous cherchez simplement à refaire votre vue, j'ai eu exactement le même problème. Dans la onResumefonction, essayez de mettre ceci:

mView = new AndroidPinballView(getApplication());

C'était aussi dans mon onCreate(), donc mettre cela dans le onResumetravaillé pour moi :)

Scumble373
la source