Програмно відновити / відтворити діяльність?


121

Після того як я внесу деякі зміни в свою базу даних, що передбачає суттєві зміни в моїх поглядах, я хотів би переробити, заново виконати onCreate.

Як це можливо?

Відповіді:


129

ОНОВЛЕННЯ : Android SDK 11 додав recreate()метод до заходів.


Я зробив це, просто використовуючи наміри, які розпочали діяльність. Визначте намір starterIntentу своєму класі та призначте його за onCreate()допомогою starterIntent = getIntent();. Потім, коли ви хочете відновити діяльність, зателефонуйтеfinish(); startActivity(starterIntent);

Це не дуже елегантне рішення, але це простий спосіб перезапустити свою діяльність і змусити її перезавантажити все.


8
насправді я думаю, що коли орієнтація пристрою змінює активізацію, oncreate викликається аналогічно .. тому я не проти цього робити. startActivity (getIntent ()); закінчити ();
Раджа

не розумно робити стартАктивність (getIntent ()), оскільки це буде просто шарувати діяльність на вершині діяльності. Потрібно припинити стару діяльність
Fallenreaper

8
@Fallenreaper Я запропонував зателефонувати finish()одразу після цього startActivity()саме з цієї причини ...
Стів Хейлі

7
Я досяг цього виклику першого finish();потімstartActivity(starterIntent);
Карло Родрігеса

3
Так що це? закінчити () і потім startActivity? Або навпаки?
Джефф Падгетт

92

Викличте метод відтворення діяльності.


19
Це нормально, якщо ваш додаток орієнтовано лише на рівень SDK рівня 11 і вище. Інакше я б пішов із підходом Стіва Хейлі.
TalkLittle

1
@FernandoEscher На жаль, це доступно лише на пристроях стільникового сота та вище.
ІгорГанапольський

2
куди зателефонувати у спосіб відтворення
SAndroidD

1
відтворити виклик методу перезавантажено чотири рази, закрийте додаток
SAndroidD

Раніше я використовував, 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()насправді не діє так само, як повністю відтворювати діяльність. Це еквівалентно обертанню пристрою, тому якщо у вас є якісь Fragmentелементи, setRetainInstance(true)вони не будуть відтворені; просто призупинився і відновився.

Тож наразі я не вірю у прийнятне рішення.


5
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMBзамість того, щоб використовувати11
S.Thiongane

4
11 - це річ, .HONEYCOMB не прав, тому що ваш код у SDK <11 не знаю, що є HONEYCOMB.
Tapa Зберегти

6
замінити startActivity(getIntent());finish();наfinish();startActivity(getIntent());
Ахмед Хамді

Ні, згідно з рекомендацією, ви повинні орієнтуватися на найвищий доступний sdk, тому сота буде доступна під час компіляції, а інта-константа несеться у вашій програмі. Я вважаю, що це звичайна модель використання sdk int, що перевищує мінімальний sdk int.
Хай Чжан

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
Дуже чисте і елегантне рішення. Відмінно працює на пристроях pre-sdk 11.
ІгорГанапольський

Були проблеми з методом super.recreate (), однак це працює Ок на Lollipop

6

для API до 11 ви не можете використовувати відтворити (). Я вирішив таким чином:

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 не має запитуRelaunchActivity, однак з розл. Я виявив таке:

             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()викликав ваш метод заповнення деяким аргументом. Коли дані змінюються, метод заповнення повинен викликати інший аргумент.


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>

Тоді ви робите 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();
    }
}

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 (новий View.OnClickListener () {

@Override public void onClick (Переглянути 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
Ахмед Адель Ісмаїл

-4

Якщо ви просто хочете знову переглянути свій погляд, у мене виникла точно така ж проблема. У onResumeфункції спробуйте поставити це:

mView = new AndroidPinballView(getApplication());

Це було і в моєму onCreate(), тому поклавши це на onResumeвідпрацьоване для мене :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.