Reiniciar / recriar programaticamente uma atividade?

121

Depois de fazer algumas mudanças em meu banco de dados, que envolvem mudanças significativas em minhas visualizações, eu gostaria de redesenhar, reexecutar onCreate.

Como isso é possível?

Pentium10
fonte

Respostas:

129

ATUALIZAÇÃO : Android SDK 11 adicionou um recreate()método às atividades.


Eu fiz isso simplesmente reutilizando a intenção que iniciou a atividade. Defina uma intenção starterIntentem sua classe e atribua-a ao onCreate()uso starterIntent = getIntent();. Então, quando quiser reiniciar a atividade, liguefinish(); startActivity(starterIntent);

Não é uma solução muito elegante, mas é uma maneira simples de reiniciar sua atividade e forçá-la a recarregar tudo.

Steve Haley
fonte
8
Na verdade, acho que quando a orientação do dispositivo muda, o oncreate da atividade é chamado de forma semelhante ... então não me importo de fazer isso. startActivity (getIntent ()); terminar();
Raja de
não é inteligente fazer startActivity (getIntent ()), pois ele apenas adicionará atividades em camadas. É necessário encerrar a atividade antiga
Fallenreaper
8
@Fallenreaper sugeri ligar finish()imediatamente depois do startActivity()justamente por esse motivo ...
Steve Haley
7
Consegui chamando primeiro finish();entãostartActivity(starterIntent);
Carlo Rodríguez
3
Então qual é? terminar () e então iniciar a atividade? Ou o contrário?
Jeff Padgett
92

Chame o método recriar da atividade.

FernandoEscher
fonte
19
Isso está ok se o seu aplicativo é direcionado apenas ao SDK de nível 11 e superior. Do contrário, eu seguiria a abordagem de Steve Haley.
TalkLittle de
1
@FernandoEscher Infelizmente isso só está disponível em dispositivos Honeycomb e superiores.
IgorGanapolsky de
2
onde chamar o método recriar
SAndroidD de
1
Recriar a chamada do método novamente para fechar o aplicativo
SAndroidD
Eu costumava usar o utilitário, recreate()mas agora vejo um problema estranho em que os botões de opção não são redefinidos durante a recriação, mas eles são redefinidos quando finish(); startActivity(getIntent());estou usando isso por agora e ver como funciona nos próximos dias ou semanas.
Ben
35

Combinando algumas respostas aqui, você pode usar algo como o seguinte.

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

Testando

Eu testei um pouco e existem alguns problemas:

  1. Se a atividade for a mais baixa da pilha, chamar startActivity(...); finish();apenas existe o aplicativo e não reinicia a atividade.
  2. super.recreate()na verdade, não age da mesma maneira que recriar totalmente a atividade. É equivalente a girando o dispositivo por isso, se você tiver quaisquer Fragments comsetRetainInstance(true) eles não serão recriados; simplesmente pausou e reiniciou.

Portanto, atualmente, não acredito que haja uma solução aceitável.

Timmmm
fonte
5
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMBem vez de usar11
S.Thiongane
4
11 está correto, .HONEYCOMB não está correto, porque seu código no SDK <11 não sabe que é HONEYCOMB.
Tapa Save
6
substituir startActivity(getIntent());finish();parafinish();startActivity(getIntent());
ahmed hamdy
Não, de acordo com a recomendação, você deve ter como alvo o sdk mais alto disponível, para que o honeycomb esteja disponível no momento da compilação e a constante int seja carregada em seu aplicativo. Eu acredito que é um padrão comum usar um int sdk maior do que o int sdk mínimo.
Hai Zhang
32

Opção 1

Ligue recreate()para o seuActivity . No entanto, este método faz com que uma tela preta piscando apareça durante a recriação da atividade.

opção 2

finish();
startActivity(getIntent());

Nenhuma tela preta "piscando" aqui, mas você verá uma transição entre as instâncias antigas e novas com um fundo preto não tão agradável. Podemos fazer melhor.

Opção 3

Para corrigir isso, podemos adicionar uma chamada para overridePendingTransition():

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

Adeus tela preta, mas no meu caso ainda vejo algum tipo de transição (uma animação fade), desta vez em um fundo colorido. Isso ocorre porque você está finalizando a instância atual de sua atividade antes que a nova seja criada e se torne totalmente visível, e a cor intermediária é o valor do windowBackgroundatributo theme.

Opção 4

startActivity(getIntent());
finish();

Chamar finish() depois startActivity() usará a transição padrão entre as atividades, geralmente com uma pequena animação deslizante. Mas a transição ainda é visível.

Opção 5

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

Para mim, esta é a melhor solução porque reinicia a atividade sem nenhuma transição visível, como se nada acontecesse.

Pode ser útil se, por exemplo, em seu aplicativo você expor uma maneira de alterar o idioma de exibição, independentemente do idioma do sistema. Nesse caso, sempre que o usuário alterar o idioma do aplicativo, você provavelmente desejará reiniciar a atividade sem transição, fazendo com que a troca de idioma pareça instantânea.

imperfeito
fonte
1
Um problema com a opção 5 é que adiciona a atividade anterior à pilha de retorno. Chame isso várias vezes e seu usuário terá que clicar várias vezes para voltar à página anterior real.
Ollie
23

Quando preciso reiniciar uma atividade, uso o seguinte código. Embora não seja recomendado.

Intent intent = getIntent();
finish();
startActivity(intent);
Ayush Goyal
fonte
1
Solução muito limpa e elegante. Funciona muito bem em dispositivos pré-SDK 11.
IgorGanapolsky de
Teve problemas com o método super.recreate (), no entanto, isso funciona bem no Lollipop
6

para API antes de 11, você não pode usar recreate (). Eu resolvi desta forma:

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

e em 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
fonte
3

Depois de procurar o implemento de gingerbread para recreate, gostaria de usar os seguintes códigos (para gingerbread):

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

Para esses códigos, é a partir da implementação em 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 não tem requestRelaunchActivity, no entanto, a partir do diff, encontrei o seguinte:

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

Então, acho que poderia usar em scheduleRelaunchActivityvez de requestRelaunchActivity.

E eu os escrevi usando refletir:

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

}

Estou usando esses códigos para o back-porting do framework xposed.

Liudongmiao
fonte
Trabalho fantástico! Testei em um emulador, e essa abordagem é compatível com versões anteriores Build.VERSION_CODES.ECLAIR_MR1(v7). Pode funcionar em versões mais antigas também.
Tim Cooke
3

Chame o recreate() método de onde você deseja recriar sua atividade. Este método irá destruir a instância atual de Activity com onDestroy()e, em seguida, recriar a atividade com onCreate().

neo
fonte
1

Se este for o seu problema, você provavelmente deve implementar outra maneira de fazer o preenchimento da visualização em sua Atividade. Em vez de executar novamente, onCreate()você deve fazer com que ele onCreate()chame seu método de preenchimento com algum argumento. Quando os dados mudam, o método de preenchimento deve ser chamado com outro argumento.

MrSnowflake
fonte
1

A maneira como resolvi isso foi usando Fragments . Eles são compatíveis com versões anteriores até a API 4, usando a biblioteca de suporte.

Você faz um layout "wrapper" com um FrameLayout nele.

Exemplo:

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

Em seguida, você cria uma FragmentActivity na qual pode substituir o FrameLayout a qualquer momento.

Exemplo:

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

No Fragment que você aumenta / substitui, você pode usar onStart e onCreateView como normalmente usaria onCreate de uma atividade.

Exemplo:

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

Também dependendo da sua situação, você pode precisar em getActivity().recreate();vez de apenas recreate().

Por exemplo, você deve usá-lo se estiver fazendo recreate()na classe que foi criada dentro da classe de atividade.

danyapd
fonte
0

Certa vez, fiz um aplicativo de teste que carrega, exclui e depois baixa novamente o arquivo de banco de dados usando o armazenamento em nuvem do Firebase. Para exibir os dados no banco de dados, o código a seguir foi a única solução que encontrei. Nem recreate()nem finish()funcionou neste caso.

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

Eu descobri a melhor maneira de atualizar seu Fragment quando os dados mudam

se você tem um botão "pesquisar", você deve inicializar sua lista ARRAY dentro do botão

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

Se você deseja passar um parâmetro para onCreate (), você deve criar um novo intent com a adição de extras e chamar StartActivity com ele. Aqui está um exemplo simples que fiz dessa forma.

              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
fonte
convenção de nomenclatura inadequada, nomes de variáveis ​​inadequados, código muito confuso ... -1
Ahmed Adel Ismail
-4

Se você está apenas tentando refazer sua visualização, tive exatamente o mesmo problema. Na onResumefunção, tente colocar isto:

mView = new AndroidPinballView(getApplication());

Isso também estava no meu onCreate(), então colocar isso onResumefuncionou para mim :)

Scumble373
fonte