java.lang.IllegalStateException: Đoạn không được đính kèm với Activity


148

Tôi hiếm khi gặp lỗi này trong khi thực hiện cuộc gọi API.

java.lang.IllegalStateException: Fragment  not attached to Activity

Tôi đã thử đặt mã bên trong isAdded()phương thức để kiểm tra xem đoạn hiện có được thêm vào hoạt động của nó hay không nhưng tôi vẫn hiếm khi gặp lỗi này. Tôi không hiểu tại sao tôi vẫn nhận được lỗi này. Làm thế nào tôi có thể ngăn chặn nó?

Nó hiển thị lỗi trên dòng-

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

Dưới đây là cuộc gọi api mẫu mà tôi đang thực hiện.

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 (). getResource (). getString (R.opes.camera_id));
Ashwin H

Câu trả lời:


203

Lỗi này xảy ra do ảnh hưởng kết hợp của hai yếu tố:

  • Yêu cầu HTTP, khi hoàn thành, sẽ gọi một trong hai onResponse()hoặc onError()(hoạt động trên luồng chính) mà không biết liệu Activityvẫn còn ở phía trước hay không. Nếu Activitykhông còn (người dùng điều hướng ở nơi khác), getActivity()trả về null.
  • Volley Responseđược thể hiện như một lớp bên trong vô danh, nó hoàn toàn giữ một tham chiếu mạnh mẽ đến lớp bên ngoài Activity. Điều này dẫn đến rò rỉ bộ nhớ cổ điển.

Để giải quyết vấn đề này, bạn nên luôn luôn làm:

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

    // etc ...

}

và cũng, sử dụng isAdded()trong onError()phương pháp là tốt:

@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
Khi sử dụng các yêu cầu và AsyncTasks của Volley từ bên trong Activity, không có cách nào dễ dàng để tránh NPE '. Luôn có cơ hội người dùng có thể điều hướng khỏi dòng điện Activitytrong khi một trong các luồng đang làm gì đó trong nền, và sau đó khi luồng hoàn thành và onPostExecute()hoặc onResponse()được gọi, không có Activity. Tất cả những gì bạn có thể làm là thực hiện kiểm tra các tham chiếu null tại các điểm khác nhau trong mã của bạn và điều đó không chống đạn :)
YS

2
Kiểm tra khỉ android (adb shell khỉ) thực sự rất tốt trong việc khắc phục lỗi này nếu bạn chưa tính đến nó theo kiểu chung / toàn cầu.
Groovee60

5
isAdded () là đủ, boolean công khai cuối cùng làAdded () {return mActivity! = null && mAdded; }
lannyf

2
@ruselli: Kiểm tra addedcờ boolean và xem Activitytrường hợp hiện tại có nullhay không.
YS

1
@gauravjain Tránh thực hiện các yêu cầu không đồng bộ (như các cuộc gọi HTTP) trực tiếp từ Fragment. Làm điều đó từ Hoạt động và nó sẽ ổn thôi. Ngoài ra, xóa các tham chiếu Fragment từ FragmentManager, đó là một cách thực hành tốt và là cách tốt nhất để tránh rò rỉ bộ nhớ.
YS

56

Vòng đời mảnh vỡ rất phức tạp và đầy lỗi, hãy thử thêm:

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

2
Tôi nên đặt nó ở đâu?
Vaclovas Rekašius Jr.

1
@ VaclovasRekašiusJr. Có vẻ như khá nhiều nơi bạn muốn truy cập Hoạt động từ bên trong đoạn. Vui vẻ!
TylerJames

2
Tôi nên làm gì nếu hoạt động == null. để duy trì ứng dụng của tôi @Miroslav
pavel

Nhìn vào isAdded () bạn có thể thấy "hoạt động! = Null" không dư thừa
BertKing

@BertKing return mHost != null && mAdded;- Đó là những gì bên trong phương thức Fragment.isAdded (). Tôi nghĩ rằng mhost là một Hoạt động nếu bạn theo dõi nó, nhưng có vẻ như mhost nằm trong FragmentActivity. Vì vậy, có lẽ, bạn đúng. Bất kỳ bổ sung?
Johnny Five

14

Tôi đã tìm thấy Phương thức rất đơn giản Giải pháp isAdded () là một trong những phương thức phân đoạn để xác định rằng đoạn hiện tại này có được gắn với Hoạt động của nó hay không.

chúng ta có thể sử dụng nó như mọi nơi trong lớp phân mảnh như:

if(isAdded())
{

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

}

12

Ngoại lệ: java.lang.IllegalStateException: Fragment

DeadlineListFragment {ad2ef970} không được đính kèm với Activity

Thể loại: Vòng đời

Mô tả : Khi thực hiện thao tác tốn thời gian trong luồng nền (ví dụ: AsyncTask), một đoạn mới đã được tạo trong thời gian đó và được tách ra khỏi Hoạt động trước khi luồng nền kết thúc. Mã trong luồng UI (ví dụ: onPostExecute) gọi một mảnh vỡ tách ra, ném ngoại lệ như vậy.

Giải pháp khắc phục:

  1. Hủy bỏ chủ đề nền khi tạm dừng hoặc dừng Fragment

  2. Sử dụng isAdded () để kiểm tra xem đoạn đó có được đính kèm hay không và sau đó lấy getResource () từ hoạt động.


11

Tôi có thể bị trễ nhưng có thể giúp được ai đó ..... Giải pháp tốt nhất cho việc này là tạo một cá thể lớp ứng dụng toàn cầu và gọi nó trong đoạn cụ thể mà hoạt động của bạn không được đính kèm

như dưới đây

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

Đây là lớp ứng dụng

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
Có, phương pháp này cũng được sử dụng rộng rãi.
CoolMind 7/12/2016

1
Đây không phải là lựa chọn khôn ngoan. FragmentContext và ApplicationContext có các kiểu khác nhau. Bối cảnh mảnh có thể có chủ đề tối, phong cách tùy chỉnh, Locale, v.v. sẽ kéo màu sắc, chuỗi tài nguyên từ các tệp khác nhau. Trong khi ApplicationContext có thể không kéo tài nguyên chính xác. Nếu bạn không có Ngữ cảnh, thì bạn không nên cố gắng kết xuất tài nguyên đó.
Jemshit Iskenderov

2

Lỗi này có thể xảy ra nếu bạn đang tạo một đoạn mà bằng cách nào đó không thể khởi tạo được:

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
  }
}

Trong trường hợp của tôi, tôi đã gặp điều này khi tôi cố gắng sử dụng:

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

2

Trong sử dụng Fragment isAdded() Nó sẽ trả về true nếu đoạn hiện được gắn vào Activity.

Nếu bạn muốn kiểm tra bên trong Hoạt động

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

Hy vọng nó sẽ giúp được ai đó


1

Tôi đã áp dụng cách tiếp cận sau đây để xử lý vấn đề này. Tạo một lớp mới hoạt động như một trình bao bọc cho các phương thức hoạt động như thế này

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

}

Bây giờ bất cứ nơi nào tôi cần truy cập tài nguyên từ các đoạn hoặc hoạt động, thay vì gọi trực tiếp phương thức, tôi sử dụng lớp này. Trong trường hợp hoạt động contextkhông phải là nullnó trả về giá trị của tài sản và trong trường hợp contextlà null, nó sẽ chuyển một giá trị mặc định (cũng được chỉ định bởi người gọi hàm).

Quan trọng Đây không phải là một giải pháp, đây là một cách hiệu quả để bạn có thể xử lý sự cố này một cách duyên dáng. Bạn sẽ muốn thêm một số nhật ký trong trường hợp bạn nhận được cá thể hoạt động là null và cố gắng sửa nó, nếu có thể.


0

điều này xảy ra khi đoạn không có ngữ cảnh, do đó phương thức getActivity () trả về null. kiểm tra xem bạn có sử dụng ngữ cảnh trước khi bạn nhận được không hoặc nếu Hoạt động không còn tồn tại nữa. sử dụng bối cảnh trong Fragment.onCreate và sau phản ứng api thường xảy ra sự cố này


0

Đôi khi ngoại lệ này là do lỗi trong triển khai thư viện hỗ trợ. Gần đây tôi đã phải hạ cấp từ 26.1.0 xuống 25.4.0 để thoát khỏi nó.


Không, tôi không, nhưng có lẽ tôi nên tạo một cái.
Bord81

0

Vấn đề này xảy ra bất cứ khi nào bạn gọi một bối cảnh không có sẵn hoặc null khi bạn gọi nó. Đây có thể là một tình huống khi bạn đang gọi ngữ cảnh của luồng hoạt động chính trên một luồng nền hoặc ngữ cảnh của luồng nền trên luồng hoạt động chính.

Chẳng hạn, tôi đã cập nhật chuỗi tùy chọn chia sẻ của mình như sau.

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

Và được gọi là finish () ngay sau nó. Bây giờ những gì nó làm là khi cam kết chạy trên luồng chính và dừng mọi cam kết Async khác nếu đến khi nó kết thúc. Vì vậy, bối cảnh của nó là sống động cho đến khi viết xong. Do đó bối cảnh trước đó là trực tiếp, gây ra lỗi xảy ra.

Vì vậy, hãy chắc chắn để kiểm tra lại mã của bạn nếu có một số mã có vấn đề bối cảnh này.


Làm thế nào bạn quản lý để khắc phục vấn đề này? Tôi đang gọi nó trong một chuỗi không đồng bộ và bây giờ tôi đang gặp vấn đề này.
Simon

Chỉ cần đảm bảo rằng thao tác ghi đã kết thúc, sau đó chỉ có bối cảnh bị hủy không trước khi hoàn thành thao tác ghi.
Prashant Paliwal
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.