java.lang.IllegalStateException: แฟรกเมนต์ไม่ได้แนบกับกิจกรรม


148

ฉันไม่ค่อยได้รับข้อผิดพลาดนี้ขณะทำการเรียก API

java.lang.IllegalStateException: Fragment  not attached to Activity

ฉันลองวางโค้ดด้านในisAdded()เพื่อตรวจสอบว่ามีการเพิ่มแฟรกเมนต์ให้กับกิจกรรมของมันหรือไม่ แต่ฉันก็ยังไม่ค่อยได้รับข้อผิดพลาดนี้ ฉันไม่เข้าใจว่าทำไมฉันยังได้รับข้อผิดพลาดนี้ ฉันจะป้องกันได้อย่างไร

ข้อผิดพลาดในการแสดงของมันในบรรทัด -

cameraInfo.setId(getResources().getString(R.string.camera_id));

ด้านล่างนี้คือตัวอย่างการเรียก api ที่ฉันกำลังทำ

SAPI.getInfo(getActivity(),
                new APIResponseListener() {
                    @Override
                    public void onResponse(Object response) {


                        cameraInfo = new SInfo();
                        if(isAdded()) {
                            cameraInfo.setId(getResources().getString(R.string.camera_id));
                            cameraInfo.setName(getResources().getString(R.string.camera_name));
                            cameraInfo.setColor(getResources().getString(R.string.camera_color));
                            cameraInfo.setEnabled(true);
                        }


                    }

                    @Override
                    public void onError(VolleyError error) {
                        mProgressDialog.setVisibility(View.GONE);
                        if (error instanceof NoConnectionError) {
                            String errormsg = getResources().getString(R.string.no_internet_error_msg);
                            Toast.makeText(getActivity(), errormsg, Toast.LENGTH_LONG).show();
                        }
                    }
                });

cameraInfo.setId (.. getActivity () getResources () getString (R.string.camera_id));
Ashwin H

คำตอบ:


203

ข้อผิดพลาดนี้เกิดขึ้นเนื่องจากผลรวมของสองปัจจัย:

  • คำร้องขอ HTTP เมื่อเสร็จสิ้นจะเรียกใช้อย่างใดอย่างหนึ่งonResponse()หรือonError()(ซึ่งทำงานกับเธรดหลัก) โดยไม่ทราบว่าActivityยังอยู่ในเบื้องหน้าหรือไม่ ถ้า the Activityหายไป (ผู้ใช้สำรวจที่อื่น), getActivity()ส่งคืน null
  • วอลเลย์Responseจะแสดงเป็นระดับชั้นที่ไม่ระบุชื่อซึ่งโดยปริยายถือที่แข็งแกร่งในการอ้างอิงด้านนอกActivityชั้นเรียน ส่งผลให้เกิดการรั่วไหลของหน่วยความจำแบบคลาสสิก

ในการแก้ปัญหานี้คุณควรทำดังนี้

Activity activity = getActivity();
if(activity != null){

    // etc ...

}

และใช้isAdded()ในonError()วิธีการเช่นกัน:

@Override
public void onError(VolleyError error) {

    Activity activity = getActivity(); 
    if(activity != null && isAdded())
        mProgressDialog.setVisibility(View.GONE);
        if (error instanceof NoConnectionError) {
           String errormsg = getResources().getString(R.string.no_internet_error_msg);
           Toast.makeText(activity, errormsg, Toast.LENGTH_LONG).show();
        }
    }
}

2
เมื่อใช้คำขอ Volley และAsyncTasks จากภายในActivityไม่มีวิธีที่จะป้องกันความผิดพลาดในการหลีกเลี่ยง NPE ' ยังมีโอกาสที่ผู้ใช้อาจออกจากปัจจุบันActivityในขณะที่หนึ่งในหัวข้อที่จะทำบางสิ่งบางอย่างในพื้นหลังและจากนั้นเมื่อเสร็จสิ้นด้ายonPostExecute()หรือจะเรียกว่าไม่มีonResponse() Activityสิ่งที่คุณทำได้คือทำการตรวจสอบการอ้างอิงแบบ null ที่จุดต่าง ๆ ในรหัสของคุณและนั่นไม่ใช่ bulletproof :)
YS

2
การทดสอบลิง android (adb shell monkey) นั้นดีมากในการขจัดข้อผิดพลาดนี้ถ้าคุณยังไม่ได้คิดในแบบสามัญ / สากล
Groovee60

5
isAdded () เพียงพอแล้วบูลีนสาธารณะสุดท้าย isAdded () {return mActivity! = null && mAdded; }
lannyf

2
@ruselli: ตรวจสอบการaddedตั้งค่าสถานะบูลีนและดูว่าActivityอินสแตนซ์ปัจจุบันเป็นnullหรือไม่
YS

1
@gauravjain หลีกเลี่ยงการร้องขอแบบอะซิงโครนัส (เช่นการโทร HTTP) โดยตรงจาก Fragments ทำจากกิจกรรมและควรจะดี นอกจากนี้ยังมีการอ้างอิงที่ชัดเจนของ FragmentManager ซึ่งเป็นวิธีปฏิบัติที่ดีและเป็นวิธีที่ดีที่สุดในการหลีกเลี่ยงการรั่วไหลของหน่วยความจำ
YS

56

วงจรชีวิตของแฟรกเมนต์มีความซับซ้อนและเต็มไปด้วยข้อบกพร่องลองเพิ่ม:

Activity activity = getActivity(); 
if (isAdded() && activity != null) {
...
}

2
ฉันควรใส่ไว้ที่ไหน?
Vaclovas Rekašius Jr.

1
@ VaclovasRekašiusJr ดูเหมือนว่าสวยมากทุกที่ที่คุณต้องการเข้าถึงกิจกรรมจากในส่วน สนุก!
TylerJames

2
ฉันควรทำอย่างไรถ้ากิจกรรม == ว่าง เพื่อให้แอปพลิเคชั่นของฉันยังมีชีวิตอยู่ @Miroslav
pavel

ดูที่ isAdded (), คุณอาจพบว่า "activity! = null" ไม่ซ้ำซ้อน
BertKing

@BertKing return mHost != null && mAdded;- นั่นคือสิ่งที่อยู่ภายใน fragment.isAdded () วิธีการ ฉันคิดว่า mHost เป็นกิจกรรมถ้าคุณติดตามมัน แต่ดูเหมือนว่า mHost นั้นอยู่ภายใน FragmentActivity ดังนั้นบางทีคุณพูดถูก เพิ่มเติมใด ๆ
Johnny Five

14

ฉันพบโซลูชันที่ง่ายมากถูก เพิ่ม ()ซึ่งเป็นหนึ่งในวิธีการแยกส่วนเพื่อระบุว่าชิ้นส่วนปัจจุบันนี้เชื่อมต่อกับกิจกรรมหรือไม่

เราสามารถใช้สิ่งนี้ได้ทุกที่ในคลาสแฟรกเมนต์เช่น:

if(isAdded())
{

// using this method, we can do whatever we want which will prevent   **java.lang.IllegalStateException: Fragment not attached to Activity** exception.

}

12

ข้อยกเว้น: java.lang.IllegalStateException: แฟรกเมนต์

DeadlineListFragment {ad2ef970} ไม่ได้เชื่อมต่อกับกิจกรรม

หมวดหมู่:วงจรชีวิต

คำอธิบาย : เมื่อทำการดำเนินการที่ต้องใช้เวลาในเธรดพื้นหลัง (เช่น AsyncTask) Fragment ใหม่ถูกสร้างขึ้นในระหว่างนี้และถูกแยกออกจากกิจกรรมก่อนที่เธรดพื้นหลังจะเสร็จสิ้น โค้ดในเธรด UI (เช่น onPostExecute) จะเรียกใช้งานบน Fragment ที่แยกออกมาซึ่งจะทำการยกเว้นให้

แก้ไขปัญหา:

  1. ยกเลิกเธรดพื้นหลังเมื่อหยุดชั่วคราวหรือหยุดการแยกส่วน

  2. ใช้ isAdded () เพื่อตรวจสอบว่ามีการแนบแฟรกเมนต์และจากนั้นรับ getResources () จากกิจกรรม


11

ฉันอาจจะสาย แต่อาจช่วยใครบางคน ..... วิธีที่ดีที่สุดสำหรับสิ่งนี้คือการสร้างอินสแตนซ์คลาสแอปพลิเคชันระดับโลกและเรียกมันในส่วนเฉพาะที่กิจกรรมของคุณไม่ได้ถูกแนบ

ดังเช่นด้านล่าง

icon = MyApplication.getInstance().getString(R.string.weather_thunder);

นี่คือคลาสแอปพลิเคชัน

public class MyApplication extends Application {

    private static MyApplication mInstance;
    private RequestQueue mRequestQueue;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    public static synchronized MyApplication getInstance() {
        return mInstance;
    }
}

1
ใช่วิธีนี้ยังใช้กันอย่างแพร่หลาย
CoolMind

1
นี่ไม่ใช่ตัวเลือกที่ฉลาด FragmentContext และ ApplicationContext มีสไตล์แตกต่างกัน บริบทของแฟรกเมนต์อาจมีธีมสีเข้มสไตล์ที่กำหนดเองสถานที่ ฯลฯ ซึ่งจะดึงสีทรัพยากรสตริงจากไฟล์ต่างๆ ในขณะที่ ApplicationContext อาจดึงทรัพยากรที่ไม่ถูกต้อง หากคุณไม่มีบริบทคุณไม่ควรพยายามแสดงผลทรัพยากรนั้น
Jemshit Iskenderov

2

ข้อผิดพลาดนี้สามารถเกิดขึ้นได้หากคุณสร้างอินสแตนซ์ชิ้นส่วนที่ไม่สามารถสร้างอินสแตนซ์ได้:

Fragment myFragment = MyFragment.NewInstance();


public classs MyFragment extends Fragment {
  public void onCreate() {
   // Some error here, or anywhere inside the class is preventing it from being instantiated
  }
}

ในกรณีของฉันฉันพบสิ่งนี้เมื่อฉันพยายามใช้:

private String loading = getString(R.string.loading);

2

ในการใช้งานส่วน isAdded() มันจะกลับมาจริงถ้าชิ้นส่วนที่แนบมากับกิจกรรมในปัจจุบัน

หากคุณต้องการตรวจสอบภายในกิจกรรม

 Fragment fragment = new MyFragment();
   if(fragment.getActivity()!=null)
      { // your code here}
      else{
       //do something
       }

หวังว่ามันจะช่วยใครซักคน


1

ฉันใช้แนวทางต่อไปนี้เพื่อจัดการปัญหานี้ สร้างคลาสใหม่ซึ่งทำหน้าที่เป็น wrapper สำหรับวิธีกิจกรรมเช่นนี้

public class ContextWrapper {
    public static String getString(Activity activity, int resourceId, String defaultValue) {
        if (activity != null) {
            return activity.getString(resourceId);
        } else {
            return defaultValue;
        }
    }

    //similar methods like getDrawable(), getResources() etc

}

ตอนนี้ที่ใดก็ตามที่ฉันต้องการเข้าถึงทรัพยากรจากชิ้นส่วนหรือกิจกรรมแทนที่จะเรียกวิธีการโดยตรงฉันใช้คลาสนี้ ในกรณีที่กิจกรรมcontextไม่ใช่กิจกรรมนั้นnullจะส่งคืนค่าของสินทรัพย์และในกรณีที่contextเป็นโมฆะมันจะส่งผ่านค่าเริ่มต้น (ซึ่งผู้โทรของฟังก์ชันจะระบุด้วย)

สิ่งสำคัญนี่ไม่ใช่วิธีแก้ปัญหานี่เป็นวิธีที่มีประสิทธิภาพที่คุณสามารถจัดการกับข้อผิดพลาดนี้ได้อย่างสง่างาม คุณต้องการเพิ่มบันทึกในบางกรณีที่คุณได้รับอินสแตนซ์ของกิจกรรมเป็นโมฆะและพยายามแก้ไขปัญหานั้นถ้าเป็นไปได้


0

สิ่งนี้เกิดขึ้นเมื่อแฟรกเมนต์ไม่มีบริบทดังนั้นเมธอด getActivity () จะส่งคืนค่าว่าง ตรวจสอบว่าคุณใช้บริบทก่อนรับหรือไม่หรือไม่มีกิจกรรมอีกต่อไป ใช้บริบทใน fragment.onCreate และหลังจากการตอบสนอง API มักกรณีปัญหานี้


0

บางครั้งข้อยกเว้นนี้เกิดจากข้อบกพร่องในการใช้งานไลบรารีสนับสนุน เมื่อเร็ว ๆ นี้ฉันต้องลดระดับจาก 26.1.0 เป็น 25.4.0 เพื่อกำจัดมัน


ไม่ฉันทำไม่ได้ แต่บางทีฉันควรสร้างใหม่
Bord81

0

ปัญหานี้เกิดขึ้นทุกครั้งที่คุณเรียกบริบทที่ไม่พร้อมใช้งานหรือเป็นโมฆะเมื่อคุณเรียกใช้ นี่อาจเป็นสถานการณ์เมื่อคุณกำลังเรียกบริบทของเธรดกิจกรรมหลักบนเธรดพื้นหลังหรือบริบทของเธรดพื้นหลังบนเธรดกิจกรรมหลัก

ตัวอย่างเช่นฉันอัปเดตสตริงค่ากำหนดที่แชร์ของฉันดังนี้

editor.putString("penname",penNameEditeText.getText().toString());
editor.commit();
finish();

และเรียกว่าเสร็จสิ้น () ทันทีหลังจากนั้น ทีนี้สิ่งที่มันทำคือการที่คอมมิชชันรันบนเธรดหลักและหยุดการทำงานของ Async อื่น ๆ ถ้ามันมาจนกว่าจะเสร็จสิ้น ดังนั้นบริบทของมันยังมีชีวิตอยู่จนกว่าการเขียนจะเสร็จสมบูรณ์ ดังนั้นบริบทก่อนหน้านี้เป็นจริงทำให้เกิดข้อผิดพลาดเกิดขึ้น

ดังนั้นตรวจสอบให้แน่ใจว่าได้ตรวจสอบรหัสของคุณอีกครั้งหากมีรหัสที่มีปัญหาด้านบริบทนี้


คุณจัดการเพื่อแก้ไขปัญหานี้ได้อย่างไร ฉันกำลังเรียกมันภายในเธรด async และฉันพบปัญหานี้แล้ว
Simon

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