プログラムでアクティビティを再起動/再作成しますか?


121

ビューに大幅な変更を伴うデータベースの変更を行った後、onCreateを再描画して再実行したいと思います。

そんなことがあるものか?

回答:


129

更新:Android SDK 11は、recreate()アクティビティにメソッドを追加しました。


アクティビティを開始したインテントを単に再利用することでそれを実現しました。目的を定義しstarterIntent、あなたのクラスで、それが中に割り当てるonCreate()使用starterIntent = getIntent();。その後、アクティビティを再開する場合は、finish(); startActivity(starterIntent);

これは非常に洗練されたソリューションではありませんが、アクティビティを再開して強制的にすべてをリロードさせる簡単な方法です。


8
実際には、デバイスの向きが変わると、アクティビティのoncreateが同様の方法で呼び出されると思います。そのため、これを行ってもかまいません。startActivity(getIntent()); finish();
Raja

startActivity(getIntent())を実行するのは賢明ではありません。アクティビティの上にアクティビティを重ねるだけだからです。古いアクティビティを終了する必要があります
Fallenreaper 2012年

8
@Fallenreaper私はちょうどその理由のためにfinish()直後に電話することを提案しましたstartActivity()...
スティーブヘイリー

7
私はそれが最初の呼び出し達成finish();、その後startActivity(starterIntent);
カルロ・ロドリゲス

3
どっち?finish()そしてstartActivity?またはその逆ですか?
ジェフパジェット2018

92

アクティビティのrecreateメソッドを呼び出します。


19
これは、アプリがSDKレベル11以上のみを対象としている場合は問題ありません。それ以外の場合は、Steve Haleyのアプローチを使用します。
TalkLittle

1
@FernandoEscher残念ながらHoneycombデバイス以降でのみ利用できます。
IgorGanapolsky 2013

2
recreateメソッドを呼び出す場所
SAndroidD 2014年

1
recreateメソッドを再作成して、4回アプリを閉じます
SAndroidD '22

以前は利用していたのですrecreate()が、再作成時にラジオボタンがリセットされないという奇妙な問題が発生しましたが、今のところfinish(); startActivity(getIntent());これを使用していて、数日または数週間でどのように機能するかを確認しています。
ベン

35

ここでいくつかの答えを組み合わせると、次のようなものを使用できます。

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

テスト中

私はそれを少しテストしました、そしていくつかの問題があります:

  1. アクティビティがスタックの最下位のアクティビティである場合、呼び出しstartActivity(...); finish();はアプリを存在させるだけで、アクティビティを再開しません
  2. super.recreate()実際には、アクティビティを完全に再現するのと同じようには動作しません。これは、デバイスを回転させることと同じです。そのため、Fragments がある場合、setRetainInstance(true)それらは再作成されません。単に一時停止して再開しただけです。

だから現在私は許容できる解決策があるとは思いません。


5
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB代わりに11
S.Thiongane 2014

4
11は厳密です。.HONEYCOMBは正しくありません。SDK<11のコードはwtatがHONEYCOMBであることを認識していないためです。
タパ保存

6
置き換えstartActivity(getIntent());finish();finish();startActivity(getIntent());
アーメドhamdy

いいえ、推奨事項に従って、利用可能な最も高いsdkをターゲットにする必要があるため、コンパイル時にハニカムが利用可能になり、int定数がアプリケーション内に保持されます。最小sdk intより大きいsdk intを使用するのは一般的なパターンだと思います。
Hai Zhang

32

オプション1

に電話recreate()してくださいActivity。ただし、この方法では、アクティビティの再作成中に点滅する黒い画面が表示されます。

オプション2

finish();
startActivity(getIntent());

ここには「点滅する」黒い画面はありませんが、古いインスタンスと新しいインスタンスの間で、あまり快適ではない黒い背景の遷移が表示されます。私たちはもっとうまくやることができます。

オプション3

これを修正するには、次の呼び出しを追加しますoverridePendingTransition()

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

さようなら、黒い画面ですが、今回は、色付きの背景に、何らかの遷移(フェードアニメーション)がまだ表示されています。これは、新しいインスタンスが作成されて完全に表示される前に、アクティビティの現在のインスタンスを終了しているためです。中間の色は、windowBackgroundテーマ属性の値です。

オプション4

startActivity(getIntent());
finish();

finish() after startActivity()を呼び出すと、アクティビティ間のデフォルトの遷移が使用されます。多くの場合、小さなスライドインアニメーションが使用されます。しかし、移行はまだ見えています。

オプション5

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

私には、何も起こらなかったかのように、目に見える遷移なしでアクティビティを再開するので、これは最良のソリューションです。

たとえば、アプリでシステムの言語とは無関係に表示言語を変更する方法を公開する場合に役立ちます。この場合、ユーザーがアプリの言語を変更するたびに、移行せずにアクティビティを再開して、言語の切り替えが瞬時に見えるようにする必要があります。


1
オプション5の問題は、前のアクティビティがバックスタックに追加されることです。これを数回呼び出すと、ユーザーは実際の前のページに戻るために何度もクリックし直す必要があります。
Ollie

23

アクティビティを再開する必要がある場合は、次のコードを使用します。それはお勧めできませんが。

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

1
非常にクリーンでエレガントなソリューション。SDK 11以前のデバイスで問題なく動作します。
IgorGanapolsky 2013

super.recreate()メソッドに問題がありましたが、これはLollipopで

6

11より前のAPIでは、recreate()を使用できません。私はこのように解決しました:

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

そして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

のgingerbread実装を探した後recreate、次のコード(gingerbread用)を使用したいと思います。

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

これらのコードの場合、それはより高い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にはrequestRelaunchActivityがありませんが、差分から、これを見つけました:

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

ので、scheduleRelaunchActivity代わりに使用できると思いますrequestRelaunchActivity

そして、私は反射を使用してそれらを書きました:

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

}

Xposedフレームワークのバックポーティングにこれらのコードを使用しています。


素晴らしい作品です!エミュレータでテストしましたが、このアプローチはBuild.VERSION_CODES.ECLAIR_MR1(v7)と下位互換性があります。古いバージョンでも動作する可能性があります。
Tim Cooke

3

recreate() アクティビティを再作成する場所からメソッドを呼び出します。このメソッドは、で現在のアクティビティのインスタンスを破棄しonDestroy()、でアクティビティを再作成しonCreate()ます。


1

これが問題である場合は、アクティビティを埋めるビューを実行する別の方法を実装する必要があります。再実行onCreate()する代わりにonCreate()、いくつかの引数を指定してfillメソッドを呼び出すようにする必要があります。データが変更されると、fillingメソッドは別の引数で呼び出されます。


1

私がそれを解決した方法は、フラグメントを使用することです。これらは、サポートライブラリを使用してAPI 4まで下位互換性があります。

FrameLayoutを含む「ラッパー」レイアウトを作成します。

例:

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

次に、いつでも好きなときにFrameLayoutを置き換えることができるFragmentActivityを作成します。

例:

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

通常、アクティビティのonCreateを使用するのと同じように、フラグメント内で、onStartおよびonCreateViewを使用してインフレート/置換できます。

例:

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

また、状況によっては、のgetActivity().recreate();代わりに必要になる場合がありますrecreate()

たとえばrecreate()、アクティビティのクラス内で作成されたクラスで行う場合に使用する必要があります。


0

かつて、firebaseクラウドストレージを使用してデータベースファイルをアップロード、削除、再ダウンロードするテストアプリを作成しました。データベース内のデータを表示するには、次のコードが私が見つけた唯一のソリューションでした。この場合もどちらrecreate()finish()機能しませんでした。

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

0

データが変更されたときにフラグメントを更新する最良の方法を見つけました

「検索」ボタンがある場合は、ボタン内のARRAYリストを初期化する必要があります

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

onCreate()にパラメーターを渡したい場合は、追加で新しいインテントを作成し、それを使用してStartActivityを呼び出す必要があります。これは私がこの方法を使って行った簡単な例です。

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

不適切な命名規則、不適切な変数名、本当にわかりにくいコード... -1
Ahmed Adel Ismail

-4

あなたがあなたの見解をやり直そうとしているだけなら、私はまったく同じ問題を抱えていました。onResume機能にこれを入れてみてください:

mView = new AndroidPinballView(getApplication());

これも私の中onCreate()にあったので、これを私のためにonResume働いた:)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.