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

在我对数据库做了一些修改(涉及视图的重大更改)之后,我想重新绘制、重新执行 onCreate。

这怎么可能?

163926 次浏览

如果这是您的问题,您可能应该实现另一种方法来填充您的活动。与其重新运行 onCreate(),不如让它使用一些参数来调用您的填充方法。当数据发生更改时,应该使用另一个参数调用填充方法。

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


通过简单地重用开始活动的意图,我已经做到了这一点。在类中定义一个意图 starterIntent,并使用 starterIntent = getIntent();onCreate()中分配它。然后,当您想要重新启动活动时,调用 finish(); startActivity(starterIntent);

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

如果您想要向 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);
}

调用活动的 再创造方法。

在这里组合一些答案,您可以使用如下内容。

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)他们将不会被重新创建,只是暂停和恢复。

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

当我需要重新启动一个活动时,我使用以下代码。

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

我解决这个问题的方法是使用 碎片。通过使用支持库,它们在 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。

例如:

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

在片段中,您可以使用 onStart 和 onCreateView,就像通常使用活动的 onCreate 一样。

例如:

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

在11之前,你不能使用 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


}

在寻找了用于 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没有 request RelaunchActivity,但是,从 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);
}


}

我用这些代码来支持公开的框架。

如果你只是想重建你的风景,我也有同样的问题。在 onResume函数中,试着这样写:

mView = new AndroidPinballView(getApplication());

这也是在我的 onCreate(),所以把这个放在 onResume为我工作:)

选择一

用你的 Activity呼叫 recreate()。 但是,此方法会导致在活动重新创建期间出现闪烁的黑屏。

选择二

finish();
startActivity(getIntent());

这里没有“闪烁”的黑屏,但是您将看到新旧实例之间的过渡,黑色背景并不那么令人愉快。我们可以做得更好。

选择三

为了解决这个问题,我们可以向 overridePendingTransition()添加一个调用:

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

再见黑屏,但在我的情况下,我仍然看到某种过渡(渐变动画) ,在彩色背景上这一次。这是因为您正在完成活动 之前的当前实例,新实例已经创建并完全可见,中间的颜色是 windowBackground主题属性的值。

选择四

startActivity(getIntent());
finish();

调用 finish() 之后 startActivity()将使用活动之间的默认转换,通常带有一点幻灯片动画。但这种转变仍是可见的。

选择五

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

对我来说,这是最好的解决方案,因为它在没有任何可见转换的情况下重新启动活动,就像什么都没有发生一样。

例如,如果在应用程序中,你公开了一种独立于系统语言更改显示语言的方法,那么这种方法可能会很有用。在这种情况下,无论何时用户更改了应用程序的语言,您都可能希望在没有转换的情况下重新启动您的活动,使语言切换看起来是即时的。

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

我曾经做过一个测试应用程序,它可以上传、删除,然后使用 Firebase 云存储重新下载数据库文件。 要在数据库中显示数据,我找到的唯一解决方案是以下代码。在这种情况下,recreate()finish()都不起作用。

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

我找到了在数据变化时刷新你的碎片的最佳方法

如果你有一个按钮“搜索”,你必须在按钮内初始化你的数组列表

SetOnClickListener (新的 View.OnClickListener (){

@ 重写 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 );


}

另外,根据您的情况,您可能需要 getActivity().recreate();而不仅仅是 recreate()

例如,如果在活动类内创建的类中执行 recreate(),则应该使用它。

如果您想使用 recreate并且目标 Android 版本低于11,那么使用 平台支持 API来使用 ActivityCompat.recreate(...)