เปิดใหม่โดยใช้โปรแกรม / สร้างกิจกรรมใหม่หรือไม่


121

หลังจากที่ฉันทำการเปลี่ยนแปลงบางอย่างในฐานข้อมูลของฉันซึ่งเกี่ยวข้องกับการเปลี่ยนแปลงที่สำคัญในมุมมองของฉันฉันต้องการวาดใหม่เรียกใช้งาน onCreate ใหม่

เป็นไปได้อย่างไร?

คำตอบ:


129

อัปเดต : Android SDK 11 เพิ่มrecreate()วิธีการในกิจกรรม


ฉันทำได้โดยเพียงแค่นำความตั้งใจที่เริ่มต้นกิจกรรมกลับมาใช้ใหม่ กำหนดความตั้งใจstarterIntentในชั้นเรียนของคุณและกำหนดไว้ในการใช้onCreate() starterIntent = getIntent();จากนั้นเมื่อคุณต้องการเริ่มกิจกรรมใหม่ให้โทรfinish(); startActivity(starterIntent);

ไม่ใช่วิธีการแก้ปัญหาที่สวยงาม แต่เป็นวิธีง่ายๆในการเริ่มต้นกิจกรรมใหม่และบังคับให้โหลดทุกอย่างซ้ำ


8
ที่จริงฉันคิดว่าเมื่อการวางแนวอุปกรณ์เปลี่ยนการสร้างกิจกรรมจะถูกเรียกในลักษณะเดียวกัน .. ดังนั้นฉันไม่รังเกียจที่จะทำเช่นนี้ startActivity (getIntent ()); เสร็จสิ้น();
ราชา

มันไม่ฉลาดที่จะทำ startActivity (getIntent ()) เนื่องจากจะทำเพียงแค่เลเยอร์กิจกรรมที่ด้านบนของกิจกรรม จำเป็นต้องยุติกิจกรรมเก่า
Fallenreaper

8
@ Fallenreaper ฉันแนะนำให้โทรfinish()ทันทีหลังจากstartActivity()เหตุผลนั้นอย่างแม่นยำ ...
Steve Haley

7
ผมประสบความสำเร็จมันเรียกแรกfinish();แล้วstartActivity(starterIntent);
คาร์โลRodríguez

3
มันคืออะไร? เสร็จสิ้น () แล้ว startActivity? หรือวิธีอื่น ๆ ?
Jeff Padgett

92

เรียกใช้เมธอดสร้างกิจกรรมใหม่


19
สิ่งนี้ใช้ได้หากแอปของคุณกำหนดเป้าหมายเฉพาะ SDK ระดับ 11 ขึ้นไป ไม่งั้นฉันจะไปตามแนวทางของสตีฟเฮลีย์
TalkLittle

1
@FernandoEscher น่าเสียดายที่มีเฉพาะในอุปกรณ์ Honeycomb ขึ้นไป
IgorGanapolsky

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()ไม่ได้ดำเนินการในลักษณะเดียวกับการสร้างกิจกรรมใหม่ทั้งหมด มันจะเทียบเท่ากับการหมุนอุปกรณ์ดังนั้นหากคุณมีFragments กับsetRetainInstance(true)พวกเขาจะไม่ถูกสร้างขึ้น; หยุดชั่วคราวและดำเนินการต่อ

ตอนนี้ฉันไม่เชื่อว่ามีวิธีแก้ปัญหาที่ยอมรับได้


5
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMBแทนที่จะใช้11
S.Thiongane

4
11 คือ rigth, .HONEYCOMB ไม่ถูกต้องเนื่องจากรหัสของคุณใน SDK <11 ไม่ทราบว่า wtat คือ HONEYCOMB
Tapa Save

6
เปลี่ยนstartActivity(getIntent());finish();ไปfinish();startActivity(getIntent());
อาเหม็ด Hamdy

ไม่ตามคำแนะนำคุณควรกำหนดเป้าหมาย 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 คือการเพิ่มกิจกรรมก่อนหน้านี้ไปที่แบ็คสแต็ค เรียกสิ่งนี้หลาย ๆ ครั้งและผู้ใช้ของคุณต้องคลิกย้อนกลับหลายครั้งเพื่อไปยังหน้าก่อนหน้าจริง
Ollie

23

เมื่อฉันต้องการเริ่มกิจกรรมใหม่ฉันใช้รหัสต่อไปนี้ แม้ว่าจะไม่แนะนำ

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

1
โซลูชันที่สะอาดและสวยงามมาก ใช้งานได้ดีกับอุปกรณ์ pre-sdk 11
IgorGanapolsky

มีปัญหากับวิธี 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 ไม่มี 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);
             }

ดังนั้นฉันคิดว่าฉันสามารถใช้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 framework


อลังการงานสร้าง! ฉันทดสอบในโปรแกรมจำลองและวิธีนี้เข้ากันได้กับBuild.VERSION_CODES.ECLAIR_MR1(v7) อาจใช้ได้กับเวอร์ชันเก่าเช่นกัน
Tim Cooke

3

เรียกใช้recreate() วิธีการจากจุดที่คุณต้องการสร้างกิจกรรมของคุณขึ้นมาใหม่ วิธีการนี้จะทำลายเช่นปัจจุบันกิจกรรมกับกิจกรรมนั้นสร้างด้วยonDestroy()onCreate()


1

หากนี่เป็นปัญหาของคุณคุณควรใช้วิธีอื่นในการดูข้อมูลในกิจกรรมของคุณ แทนที่จะเรียกใช้อีกครั้งonCreate()คุณควรonCreate()เรียกใช้วิธีการเติมด้วยอาร์กิวเมนต์ เมื่อข้อมูลมีการเปลี่ยนแปลงวิธีการเติมควรถูกเรียกด้วยอาร์กิวเมนต์อื่น


1

วิธีที่ฉันแก้ไขคือใช้Fragmentsเศษสิ่งเหล่านี้เข้ากันได้ย้อนหลังจนถึง API 4 โดยใช้ไลบรารีการสนับสนุน

คุณสร้างเค้าโครง "wrapper" โดยมี 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();
  }

ใน Fragment คุณขยาย / แทนที่คุณสามารถใช้ onStart และ onCreateView ได้เหมือนกับที่คุณ Normaly จะใช้ 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

ฉันค้นพบวิธีที่ดีที่สุดในการรีเฟรช Fragment ของคุณเมื่อข้อมูลมีการเปลี่ยนแปลง

หากคุณมีปุ่ม "ค้นหา" คุณต้องเริ่มต้นรายการ ARRAY ของคุณภายในปุ่ม

mSearchBtn.setOnClickListener (View OnClickListener ใหม่ () {

@Override โมฆะสาธารณะ 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 () คุณต้องสร้างจุดประสงค์ใหม่ด้วยการเพิ่มพิเศษและเรียก 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
Ahmed Adel Ismail

-4

หากคุณต้องการทำมุมมองของคุณใหม่ฉันพบปัญหาเดียวกันแน่นอน ในonResumeฟังก์ชั่นให้ลองใส่สิ่งนี้:

mView = new AndroidPinballView(getApplication());

สิ่งนี้ก็อยู่ในตัวฉันonCreate()เช่นกันดังนั้นการใส่สิ่งนี้ในการonResumeทำงานสำหรับฉัน :)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.