以编程方式重新启动/重新创建活动?


121

在数据库中进行了一些更改(涉及到视图的重大更改)之后,我想重绘并重新执行onCreate。

那怎么可能?

Answers:


129

更新:Android SDK 11 recreate()为活动添加了一种方法。


我已经通过简单地重用开始活动的意图来做到这一点。starterIntent在课程中定义一个意图,并onCreate()使用进行分配starterIntent = getIntent();。然后,当您要重新启动活动时,请致电finish(); startActivity(starterIntent);

这不是一个非常优雅的解决方案,但是它是重新启动活动并强制其重新加载所有内容的简单方法。


8
实际上,我认为当设备方向改变时,活动的oncreate也会以类似的方式调用..所以我不介意这样做。startActivity(getIntent()); 完();
拉贾

进行startActivity(getIntent())并不明智,因为它只会将活动放在活动之上。需要结束旧活动
Fallenreaper 2012年

8
@Fallenreaper我建议出于这个原因finish()立即致电startActivity()...
Steve Haley

7
我实现它首先调用finish();然后startActivity(starterIntent);
卡罗·罗德里格斯

3
那是什么呢?finish()然后startActivity?还是相反?
Jeff Padgett

92

调用活动的recreate方法。


19
如果您的应用仅定位到SDK级别11和更高级别,则可以。否则我会选择史蒂夫·海利的方法。
TalkLittle 2011年

1
@FernandoEscher不幸的是,这仅在Honeycomb设备及更高版本上可用。
IgorGanapolsky

2
在哪里调用重新创建方法
SAndroidD 2014年

1
重新创建方法调用,完全关闭该应用程序
SAndroidD 2014年

我曾经利用过,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()实际上与完全重新创建活动的行为不同。这等效于旋转设备,因此,如果有任何FragmentsetRetainInstance(true)它们将不会被重新创建。只是停下来又恢复。

所以目前我不相信有一个可以接受的解决方案。


5
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB而不是使用11
S.Thiongane 2014年

4
11是严格的,.HONEYCOMB不正确,因为您的SDK <11中的代码不知道wtat是HONEYCOMB。
Tapa保存

6
替换startActivity(getIntent());finish();finish();startActivity(getIntent());
艾哈迈德·哈姆迪(Ahmed Hamdy)2014年

不,根据建议,您应该以最高可用的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() 之后 调用startActivity()将使用活动之间的默认过渡,通常带有一些幻灯片动画。但是过渡仍然可见。

选项5

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

对我来说,这是最好的解决方案,因为它无需任何可见的过渡即可重启活动,就像什么也没发生一样。

例如,如果在您的应用中您公开了一种独立于系统语言来更改显示语言的方法,则可能会很有用。在这种情况下,每当用户更改应用程序的语言时,您可能都希望在不进行过渡的情况下重新启动活动,从而使语言切换看起来是瞬间的。


1
选项5的一个问题是,它将先前的活动添加到了Backstack。多次调用此操作,您的用户必须单击很多次才能返回到实际的上一页。
奥利

23

需要重新启动活动时,请使用以下代码。虽然不推荐。

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

1
非常干净和优雅的解决方案。在pred-sdk 11设备上运行良好。
IgorGanapolsky

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

在找到的姜饼工具之后recreate,我想使用以下代码(用于姜饼):

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,但是,从diff中,我发现了这一点:

             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)。它也可能适用于旧版本。
蒂姆·库克

3

recreate() 从要重新创建活动的位置调用该方法。此方法将使用销毁当前的Activity实例,onDestroy()然后使用重建活动onCreate()


1

如果这是您的问题,则可能应该实现另一种方法来填写“活动”中的视图。而不是重新运行,onCreate()您应该这样做,以便onCreate()使用一些参数调用您的填充方法。数据更改时,应使用另一个参数调用fill方法。


1

我解决它的方法是使用Fragments。在使用支持库的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>

然后,您可以创建一个FragmentActivity,可以随时替换FrameLayout。

例:

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

我发现了在数据更改时刷新片段的最佳方法

如果您有按钮“搜索”,则必须在按钮内初始化阵列列表

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(),则必须添加一个额外的参数来创建一个新的Intent,并使用它来调用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
艾哈迈德·阿德尔·伊斯梅尔

-4

如果您只是想重新考虑您的观点,那么我也有同样的问题。在onResume函数中尝试将其放入:

mView = new AndroidPinballView(getApplication());

这也是在我身上onCreate(),所以把它放在onResume对我有用的地方:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.