Программно перезапустить / воссоздать активность?
После того, как я внесу некоторые изменения в свою базу данных, что повлечет за собой значительные изменения в моих представлениях, я хотел бы перерисовать, повторно выполнить onCreate.
Как это возможно?
17 ответов
Обновление: Android SDK 11 добавил метод активности () в действия.
Я сделал это, просто повторно использовав намерение, положившее начало деятельности. Определить намерение starterIntent
в вашем классе и назначить его в onCreate()
с помощью starterIntent = getIntent();
, Затем, когда вы хотите возобновить действие, позвоните finish(); startActivity(starterIntent);
Это не очень элегантное решение, но это простой способ перезапустить свою деятельность и заставить ее перезагрузить все.
Опция 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);
Для меня это лучшее решение, потому что оно перезапускает активность без видимого перехода, как будто ничего не произошло.
Это может быть полезно, если, например, в вашем приложении вы предлагаете способ изменить язык отображения независимо от языка системы. В этом случае всякий раз, когда пользователь меняет язык вашего приложения, вы, вероятно, захотите перезапустить свою деятельность без перехода, что сделает переключение языка мгновенным.
Комбинируя некоторые ответы здесь, вы можете использовать что-то вроде следующего.
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();
}
}
}
тестирование
Я проверил это немного, и есть некоторые проблемы:
- Если активность является самой низкой в стеке, вызов
startActivity(...); finish();
просто существует приложение и не перезапускает активность. super.recreate()
на самом деле не действует так же, как полное воссоздание деятельности. Это эквивалентно вращению устройства, так что если у вас есть какие-либоFragment
сsetRetainInstance(true)
они не будут воссозданы; просто остановился и возобновил.
Поэтому в настоящее время я не верю, что есть приемлемое решение.
Когда мне нужно перезапустить действие, я использую следующий код. Хотя это не рекомендуется.
Intent intent = getIntent();
finish();
startActivity(intent);
Для API до 11 вы не можете использовать restate(). Я решил таким образом:
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 не имеет 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.
Позвоните в recreate()
метод, из которого вы хотите воссоздать свою деятельность. Этот метод уничтожит текущий экземпляр Activity с помощьюonDestroy()
а затем воссоздайте активность с помощью onCreate()
.
В случае, если вы хотите использовать
recreate
и ориентируйтесь на версии Android ниже 11, используйте
ActivityCompat.recreate(...)
с помощью API поддержки платформы .
Если это ваша проблема, вам, вероятно, следует реализовать другой способ выполнения представления, заполняющего вашу активность. Вместо повторного запуска onCreate()
ты должен сделать это так onCreate()
вызывает ваш метод заполнения с некоторым аргументом. Когда данные изменяются, метод заполнения должен вызываться с другим аргументом.
Также в зависимости от вашей ситуации вам может понадобиться getActivity().recreate();
вместо просто recreate()
.
Например, вы должны использовать его, если вы делаете recreate()
в классе, созданном внутри класса активности.
Я решил это с помощью фрагментов. Они обратно совместимы до 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();
}
В надутом / заменяемом фрагменте вы можете использовать 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();
}
}
Однажды я сделал тестовое приложение, которое загружает, удаляет, а затем повторно загружает файл базы данных, используя облачное хранилище firebase. Для отображения данных в базе данных единственным решением, которое я нашел, был следующий код. Ни то, ни другоеrecreate()
ни finish()
работал в этом случае.
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
System.exit(0);
Работал!
intent.flags = Intent.FLAG_ACTIVITY_NO_ANIMATION
startActivity(intent)
overridePendingTransition( 0, 0)
finish()
Я нашел лучший способ обновить ваш фрагмент при изменении данных
если у вас есть кнопка "поиск", вы должны инициализировать свой список ARRAY внутри кнопки
mSearchBtn.setOnClickListener(новый 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 );
}
Если вы хотите передать параметр в onCreate(), вам нужно создать новое намерение с добавлением extra и вызвать с ним 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);
}
Если вы просто хотите пересмотреть свое мнение, у меня возникла та же проблема. в onResume
Функция попробуйте поставить это:
mView = new AndroidPinballView(getApplication());
Это было также в моем onCreate()
так что положив это в onResume
работал на меня:)