Android 以编程方式重新启动/重新创建活动?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2486934/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Programmatically relaunch/recreate an activity?
提问by Pentium10
After I do some change in my database, that involves significant change in my views, I would like to redraw, re-execute onCreate.
在我对数据库进行一些更改后,这涉及我的视图的重大更改,我想重绘,重新执行 onCreate。
How is that possible?
这怎么可能?
回答by Steve Haley
UPDATE: Android SDK 11 added a recreate()
method to activities.
更新:Android SDK 11recreate()
为活动添加了一个方法。
I've done that by simply reusing the intent that started the activity. Define an intent starterIntent
in your class and assign it in onCreate()
using starterIntent = getIntent();
. Then when you want to restart the activity, call finish(); startActivity(starterIntent);
我通过简单地重用启动活动的意图来做到这一点。starterIntent
在您的班级中定义一个意图并onCreate()
使用starterIntent = getIntent();
. 然后当你想重新启动活动时,调用finish(); startActivity(starterIntent);
It isn't a very elegant solution, but it's a simple way to restart your activity and force it to reload everything.
这不是一个非常优雅的解决方案,但它是一种简单的方法来重新启动您的活动并强制它重新加载所有内容。
回答by Timmmm
Combining some answers here you can use something like the following.
在这里结合一些答案,您可以使用以下内容。
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();
}
}
}
Testing
测试
I tested it a bit, and there are some problems:
我测试了一下,有一些问题:
- If the activity is the lowest one on the stack, calling
startActivity(...); finish();
just exist the app and doesn'trestart the activity. super.recreate()
doesn't actually act the same way as totally recreating the activity. It is equivalent to rotating the device so if you have anyFragment
s withsetRetainInstance(true)
they won't be recreated; merely paused and resumed.
- 如果活动是堆栈中的最低活动,则调用
startActivity(...); finish();
仅存在应用程序并且不会重新启动活动。 super.recreate()
实际上与完全重新创建活动的方式不同。它相当于旋转设备,因此如果您有任何Fragment
s,setRetainInstance(true)
它们将不会被重新创建;只是暂停并继续。
So currently I don't believe there is an acceptable solution.
所以目前我不相信有一个可以接受的解决方案。
回答by flawyte
Option 1
选项1
Call recreate()
on your Activity
.
However this method causes a flashing black screen to appear during the activity re-creation.
致电recreate()
您的Activity
. 但是,此方法会导致在重新创建活动期间出现闪烁的黑屏。
Option 2
选项 2
finish();
startActivity(getIntent());
No "flashing" black screen here, but you'll see a transition between the old and the new instances with a not-so-pleasant black background. We can do better.
这里没有“闪烁”的黑屏,但您会看到旧实例和新实例之间的过渡,黑色背景不太令人愉快。我们可以做得更好。
Option 3
选项 3
To fix this, we can add a call to overridePendingTransition()
:
为了解决这个问题,我们可以添加一个调用overridePendingTransition()
:
finish();
startActivity(getIntent());
overridePendingTransition(0, 0);
Good bye black screen, but in my case I still see some kind of transition (a fade animation), on a colored background this time. That's because you're finishing the current instance of your activity beforethe new one is created and becomes fully visible, and the in-between color is the value of the windowBackground
theme attribute.
再见黑屏,但就我而言,这次我仍然在彩色背景上看到某种过渡(淡入淡出动画)。那是因为您在创建新活动并变得完全可见之前完成了活动的当前实例,中间颜色是windowBackground
主题属性的值。
Option 4
选项 4
startActivity(getIntent());
finish();
Calling finish()
afterstartActivity()
will use the default transition between activities, often with a little slide-in animation. But the transition is still visible.
调用finish()
afterstartActivity()
将使用活动之间的默认过渡,通常带有一点滑入动画。但过渡仍然可见。
Option 5
选项 5
startActivity(getIntent());
finish();
overridePendingTransition(0, 0);
To me, this is the best solution because it restarts the activity without any visible transition, like if nothing happened.
对我来说,这是最好的解决方案,因为它会在没有任何可见过渡的情况下重新启动 Activity,就像什么也没发生一样。
It could be useful if, for example, in your app you expose a way to change the display language independently of the system's language. In this case, whenever the user changes your app's language you'll probably want to restart your activity without transition, making the language switch look instantaneous.
例如,如果在您的应用程序中公开了一种独立于系统语言更改显示语言的方法,这可能会很有用。在这种情况下,每当用户更改您的应用程序的语言时,您可能希望在没有转换的情况下重新启动您的 Activity,使语言切换看起来是即时的。
回答by Ayush Goyal
When I need to restart an activity, I use following code. Though it is not recommended.
当我需要重新启动活动时,我使用以下代码。虽然不推荐。
Intent intent = getIntent();
finish();
startActivity(intent);
回答by Francesco Ditrani
for API before 11 you cannot use recreate(). I solved in this way:
对于 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();
and in onCreate..
并在 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
}
回答by liudongmiao
After looking for the gingerbread implement for recreate
, I'd like to use following codes (for gingerbread):
在为 寻找姜饼工具后recreate
,我想使用以下代码(姜饼):
activity.mMainThread.mAppThread.scheduleRelaunchActivity(activity.mToken, null, null, 0, false, null);
For these codes, it's from the implementation in higher api.
对于这些代码,它来自更高版本的 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 has no requestRelaunchActivity, however, from the diff, i found this:
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);
}
So I think I could use scheduleRelaunchActivity
instead of requestRelaunchActivity
.
所以我想我可以使用scheduleRelaunchActivity
代替requestRelaunchActivity
.
And I have written them using reflect:
我已经使用反射编写了它们:
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);
}
}
I'm using these codes for the back-porting of xposed framework.
我将这些代码用于 xposed 框架的向后移植。
回答by neo
Call the recreate()
method from where you want to recreate your activity . This method will destroy current instance of Activity with onDestroy()
and then recreate activity with onCreate()
.
recreate()
从您要重新创建活动的位置调用该方法。此方法将销毁 Activity 的当前实例,onDestroy()
然后使用 重新创建活动onCreate()
。
回答by Spikey
The way I resolved it is by using Fragments. These are backwards compatible until API 4 by using the support library.
我解决它的方法是使用Fragments。通过使用支持库,这些在 API 4 之前是向后兼容的。
You make a "wrapper" layout with a FrameLayout in it.
您使用 FrameLayout 制作一个“包装器”布局。
Example:
例子:
<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>
Then you make a FragmentActivity in wich you can replace the FrameLayout any time you want.
然后你创建一个 FragmentActivity ,你可以随时替换 FrameLayout 。
Example:
例子:
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();
}
In the Fragment you inflate/replace you can use the onStart and onCreateView like you normaly would use the onCreate of an activity.
在您膨胀/替换的 Fragment 中,您可以像平常使用活动的 onCreate 一样使用 onStart 和 onCreateView。
Example:
例子:
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();
}
}
回答by MrSnowflake
If this is your problem, you should probably implement another way to do the view filling in your Activity. Instead of re running onCreate()
you should make it so onCreate()
calls your filling method with some argument. When the data changes, the filling method should get called with another argument.
如果这是您的问题,您可能应该实现另一种方法来在您的活动中进行视图填充。而不是重新运行,onCreate()
你应该让它onCreate()
用一些参数调用你的填充方法。当数据发生变化时,填充方法应该用另一个参数调用。