getActivity ()는 Fragment 함수에서 null을 반환합니다.


192

나는 이와 같은 공개 방법을 가진 조각 (F1)을 가지고있다.

public void asd() {
    if (getActivity() == null) {
        Log.d("yes","it is null");
    }
}

그리고 네 (활동에서) 호출하면 null입니다 ...

FragmentTransaction transaction1 = getSupportFragmentManager().beginTransaction();
F1 f1 = new F1();
transaction1.replace(R.id.upperPart, f1);
transaction1.commit();
f1.asd();

내가 잘못하고있는 것이 틀림없지 만 그게 뭔지 모르겠다


이 게시물에 붙여 넣을 때 오류가 있는지 확실하지 않지만 뒤에 괄호가 필요합니다 getActivity(). 또한 조각을 어떻게 인스턴스화합니까? layout.xml에 있습니까?
CaseyB

두 번째 코드 조각은 어디에 속합니까? 활동의 oncreate () 메소드에? 그리고 이미 setContentView ()를 호출 했습니까?
Franziskus Karsunke

R.id.upperPar는 레이아웃의 요소이므로 조각으로 대체되어야하지만 내 문제는 아닙니다. 나는 사용자 정의 조각 방법에서 getActivity ()를 호출 할 때 내가 널 (null)을받을 이유는 onActivityCreated 방법 getActivity의 말은하지 null의 실제 활동하게, 이해가 안 돼요
Lukap

이 레이아웃에없는 문제는 응용 프로그램이 잘 작동하지만 나는 getActivity에 null을받을 이유가이 문제 여기 안와 같은 렌더링되는 단편을 포함 BTW 모든 요소?
Lukap

1
이 메소드를 호출해야합니다. f1.asd (); 조각 클래스에서 재정의되는 onActivityCreated 메서드에서
Namrata Bagerwal

답변:


164

commit 트랜잭션을 예약합니다. 즉, 즉시 발생하지 않지만 다음에 메인 스레드가 준비 될 때 메인 스레드에서 작업으로 예약됩니다.

추가하는 것이 좋습니다

onAttach(Activity activity)

메소드를 사용하여 Fragment중단 점을 설정하고 호출과 관련하여 호출 시점을 확인합니다 asd(). asd()엑시트를 호출하는 메소드 이후에 호출되는 것을 볼 수 있습니다 . onAttach어디에 호는 Fragment그 활동이 시점에서 부착되는 getActivity()비 - 널을 반환 (NB도있다 onDetach()호).


5
문제를 해결할 수있는 방법을 이해하지 못했습니다. getActivity ()가 아직 준비되지 않은 경우 FragmentActivity 객체의 참조를 어떻게 얻을 수 있습니까?
CeccoCQ

2
@Vivek 나는 당신이 무엇을 성취하고 싶은지 잘 모르겠습니다. 대화 상자를 바로 표시하기 위해 프래그먼트가 필요한 경우, 예를 들어 onCreateView또는 onActivityCreated메소드 에서 작성시 필요한 작업을 수행하도록하십시오 . 질문 게시에서 asd ()를 호출 해야하는 이유에 대해 질문합니다.
PJL

3
onAttach 더 이상 사용되지 않음
abbasalim

6
onAttach (Activity mActivity)가 감가 상각되는 것 같습니다. 이것에 대한 모든 해결 방법
ashish.n

4
API 24 도입commitNow()
Nicolas

92

이를 제거하는 가장 좋은 방법은 onAttach가 호출 될 때 활동 참조를 유지하고 필요한 경우 활동 참조를 사용하는 것입니다.

@Override
public void onAttach(Context context) {
    super.onAttach(activity);
    mContext = context;
}

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}

34
mActivity = null onDetach ()를 설정해야합니까?
Oliver Pearmain

5
@OliverPearmain onDetach ()에서 수행하면 이익이 없습니다. onDestory ()에서이를 무효화해야합니다. 또한 WeakRefernce에 보관해야합니다.
Kirill Popov

나는 모두를 무효로하고 onDestroy()하고 onDetach()있기 때문에 onDestroy()호출 할 수 보장 할 수 없습니다.
Mohammed Ali

8
Activity우리가 무효화하지 않으면 누출 되고 onDestroy()있습니까?
Mohammed Ali

3
developer.android.com/intl/zh-tw/guide/components/… 에 따르면 onAttach ()는 onCreateView ()를 호출하기 전에 호출됩니다. 그러나 onCreateView ()에서 getActivity ()를 호출하는 동안 여전히 NullPointerException이 발생합니다. 어떻게 그런 일이 일어날 수 있습니까?
Kimi Chiu

82

getActivity()조각이 제거 된 후 완료된 다른 스레드 를 호출 할 때 발생했습니다 . 일반적인 경우는 HTTP 요청이 끝났을 때 getActivity()(예 :)를 호출하는 것 입니다.ToastonResponse 입니다.

이를 피하기 위해 필드 이름을 정의하고 mActivity대신 사용할 수 getActivity()있습니다. 이 필드는 Fragment의 onAttach () 메소드에서 다음과 같이 초기화 할 수 있습니다.

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (context instanceof Activity){
        mActivity =(Activity) context;
    }
}

내 프로젝트에서는 일반적으로이 기능을 사용하여 모든 Fragments에 대한 기본 클래스를 정의합니다.

public abstract class BaseFragment extends Fragment {

    protected FragmentActivity mActivity;

    @Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (context instanceof Activity){
        mActivity =(Activity) context;
    }
}
}

행복한 코딩,


19
우리는 mActivity = null을 설정할 것입니다; onDetach ()에서?
Bharat Dodeja

thucnguyen, 단일 활동 앱에 대해 mActivity를 정적으로 선언하는 방법은 무엇입니까?
iamthevoid

Activity다른 스레드에서 액세스해야 할 이유가 전혀 없습니다 . 어쨌든 토스트를 보여 주지도 않고 아무것도 할 수 없습니다. 따라서 작업을 먼저 메인 스레드로 전송하거나 활동을 전혀 사용하지 않아야합니다.
Dzmitry Lazerka

4
@BharatDodeja 우리는 mActivity = null onDetach로 설정해야합니까? 알았어?
SLearner 2016 년

5
이것은 활동을 무효화하지 않고 누출됩니다.
Darek Deoniziak

30

onAttach의 활동을 참조하도록 제안하는 다른 답변은 실제 문제에 대한 반창고를 제안하는 것입니다. getActivity가 null을 반환하면 조각이 활동에 첨부되지 않았 음을 의미합니다. 회전 또는 액티비티가 완료되어 액티비티가 사라졌지 만 프래그먼트에 여전히 콜백 리스너가 등록되어있는 경우 가장 일반적으로 발생합니다. 활동으로 무언가를해야하지만 활동이 없어지면 리스너가 호출되면 할 수있는 일이 많지 않습니다. 코드에서 확인해야합니다.getActivity() != null그리고 그것이 없다면 아무것도하지 마십시오. 사라진 활동에 대한 참조를 유지하면 활동이 가비지 수집되지 않습니다. 사용자가 시도하는 UI 항목은 사용자에게 표시되지 않습니다. 콜백 리스너에서 UI와 관련이없는 것에 대한 컨텍스트를 원할 수있는 상황을 상상할 수 있습니다.이 경우 응용 프로그램 컨텍스트를 얻는 것이 더 합리적입니다. onAttach트릭이 큰 메모리 누수가 아닌 유일한 이유 는 일반적으로 콜백 리스너가 실행 된 후에는 더 이상 필요하지 않으며 Fragment, 모든 뷰 및 Activity 컨텍스트와 함께 가비지 수집 될 수 있기 때문입니다. 만약 너라면setRetainInstance(true) 활동 필드도 유지되지만 회전 후에는 현재 활동이 아닌 이전 활동이 될 수 있기 때문에 메모리 누수가 발생할 가능성이 더 큽니다.


1
이것은 정확히 내 문제입니다. 프로세스를 수행하는 조각이 있습니다-> 광고가 표시됩니다-> 그런 다음 예언은 계속됩니다. 광고에서 리스너를 통해 광고에서 돌아온 후 일부 장치에서 getActivity ()는 null입니다. 그러나 작업을 마치려면 작업의 다른 부분을 계속해야합니다. 이것에 대한 해결책이 없다는 것을 의미합니까?
Notbad

이것은 내가 직면하고있는 것입니다. 결제 작업을 수행하는 조각에 Activity 인터페이스가 있습니다. 지불이 끝나면 인터페이스를 사용하여 무언가를하고 싶지만 인터페이스가 null입니다.
Freddie

이것은이 주제에 대한 수백 가지의 SO 질문에 대한 일반적인 대답 인 것 같습니다.
Manuel

가장 좋은 답변입니다. SO에는 Android를위한 많은 반창고 솔루션이 있습니다.
maxbeaudoin

따라서 일부 작업을 수행하려는 경우 getActivity ()가 사용 가능한 후에 어떻게 실행할 수 있습니까?
Sreekanth Karumanaghat

17

Android API 레벨 23부터 onAttach (Activity activity)가 더 이상 사용되지 않습니다. onAttach (Context context)를 사용해야합니다. http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

활동은 컨텍스트이므로 컨텍스트가 활동인지 간단히 확인하고 필요한 경우 캐스트하십시오.

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    Activity a;

    if (context instanceof Activity){
        a=(Activity) context;
    }

}

사용 방법
ARR.s

10

PJL이 옳습니다. 나는 그의 제안을 사용했고 이것이 내가 한 일이다.

  1. 조각에 대해 정의 된 전역 변수 :

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. 구현

@Override
public void onAttach(Activity activity) {
  super.onAttach(activity);
  synchronized (attachingActivityLock) {
      syncVariable = true;
      attachingActivityLock.notifyAll();
  }
}

삼 . 스레드에서 getActivity ()를 호출 해야하는 함수를 래핑했습니다. 메인 스레드에서 실행되면 4 단계로 스레드를 차단하고 onAttach ()는 호출되지 않기 때문입니다.

    Thread processImage = new Thread(new Runnable() {

        @Override
        public void run() {
            processImage();
        }
    });
    processImage.start();

4. getActivity ()를 호출 해야하는 함수에서 getActivity ()를 호출하기 전에 이것을 사용합니다.

    synchronized (attachingActivityLock) {
        while(!syncVariable){
            try {
                attachingActivityLock.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

UI 업데이트가있는 경우 UI 스레드에서 실행해야합니다. ImgeView를 업데이트해야합니다.

image.post(new Runnable() {

    @Override
    public void run() {
        image.setImageBitmap(imageToShow);
    }
});

7

commit () 후에 콜백이 호출되는 순서 :

  1. commit () 직후에 수동으로 호출하는 메소드
  2. onAttach ()
  3. onCreateView ()
  4. onActivityCreated ()

일부 뷰와 관련된 작업을 수행해야했기 때문에 onAttach ()가 작동하지 않았습니다. 추락했다. 그래서 내 코드의 일부를 commit () (1.) 바로 다음에 호출 된 메소드 내에서 일부 매개 변수를 설정 한 다음 onCreateView () (3.) 내부의 뷰를 처리하는 코드의 일부를 이동했습니다.


3

OkHttp를 사용하고 있으며이 문제에 직면했습니다.


첫 번째 부분에서 @thucnguyen은 올바른 길을 가고 있었다 .

조각이 제거 된 후 완료된 다른 스레드에서 getActivity ()를 호출 할 때 발생했습니다. 일반적인 경우는 HTTP 요청이 완료되면 (예 : onResponse에서) getActivity () (예 : 토스트)를 호출하는 것입니다.

활동이 종료 된 후에도 일부 HTTP 호출이 실행 되었습니다 (HTTP 요청이 완료되기까지 시간이 걸릴 수 있음). 그런 다음 HttpCallback일부 Fragment 필드를 업데이트하려고했지만 시도 할 null때 예외가 발생했습니다 getActivity().

http.newCall(request).enqueue(new Callback(...
  onResponse(Call call, Response response) {
    ...
    getActivity().runOnUiThread(...) // <-- getActivity() was null when it had been destroyed already

IMO 솔루션은 프래그먼트가 더 이상 살아 있지 않을 때 콜백이 발생하지 않도록하는 것입니다 (Okhttp뿐만 아니라).

수정 : 예방.

프래그먼트 라이프 사이클 (자세한 정보는 여기 )을 살펴보면 방법 onAttach(Context context)onDetach()방법 이 있음을 알 수 있습니다. 이것들은 Fragment가 활동에 속하고 각각 중지되기 직전에 호출됩니다.

즉, onDetach메소드 에서 콜백을 제어하여 콜백이 발생하지 않도록 할 수 있습니다 .

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    // Initialize HTTP we're going to use later.
    http = new OkHttpClient.Builder().build();
}

@Override
public void onDetach() {
    super.onDetach();

    // We don't want to receive any more information about the current HTTP calls after this point.
    // With Okhttp we can simply cancel the on-going ones (credits to https://github.com/square/okhttp/issues/2205#issuecomment-169363942).
    for (Call call : http.dispatcher().queuedCalls()) {
        call.cancel();
    }
    for (Call call : http.dispatcher().runningCalls()) {
        call.cancel();
    }
}

2

이 함수를 어디에서 호출합니까? 의 생성자에서 호출하면을 Fragment반환 null합니다.

getActivity()메소드 onCreateView()가 실행될 때 호출 하십시오 .


1

다음과 같이하십시오. 도움이 될 것 같습니다.

private boolean isVisibleToUser = false;
private boolean isExecutedOnce = false;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_my, container, false);
    if (isVisibleToUser && !isExecutedOnce) {
        executeWithActivity(getActivity());
    }
    return root;
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    this.isVisibleToUser = isVisibleToUser;
    if (isVisibleToUser && getActivity()!=null) {
        isExecutedOnce =true;
        executeWithActivity(getActivity());
    }
}


private void executeWithActivity(Activity activity){
    //Do what you have to do when page is loaded with activity

}

1

여전히 onAttach (Activity activity)에 문제가있는 사람들은 방금 컨텍스트로 변경되었습니다.

    @Override
public void onAttach(Context context) {
    super.onAttach(context);
    this.context = context;
}

대부분의 경우 컨텍스트를 저장하면 충분합니다. 예를 들어 getResources ()를 수행하려는 경우 컨텍스트에서 바로 수행 할 수 있습니다. 여전히 활동에 컨텍스트를 작성 해야하는 경우-

 @Override
public void onAttach(Context context) {
    super.onAttach(context);
    mActivity a; //Your activity class - will probably be a global var.
    if (context instanceof mActivity){
        a=(mActivity) context;
    }
}

user1868713에서 제안한대로.


0

onAttach를 사용하거나 어디서나 onAttach를 넣고 싶지 않은 경우 기본 App 클래스에 ApplicationContext를 반환하는 메서드를 넣을 수 있습니다.

public class App {
    ...  
    private static Context context;

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

    public static Context getContext() {
        return context;
    }
    ...
}

그런 다음 프로젝트의 모든 위치에서 다음과 같이 재사용 할 수 있습니다.

App.getContext().getString(id)

이것이 효과가없는 경우 알려주십시오.


0

또 다른 좋은 해결책은 MVVM 아키텍처와 함께 Android의 LiveData를 사용하는 것입니다. ViewModel 내부에 LiveData 객체를 정의하고 프래그먼트에서 관찰하고 LiveData 값이 변경되면 프래그먼트가 활성 상태 인 경우에만 관찰자 (이 경우 프래그먼트)에게 알릴 것이므로 보장됩니다. 프래그먼트가 활성 상태 일 때만 UI가 작동하고 액티비티에 액세스합니다. 이것은 LiveData 와 함께 제공되는 장점 중 하나입니다

물론이 질문을 처음 받았을 때 LiveData는 없었습니다. 내가 본 것처럼 여전히이 문제가 있으며 누군가에게 도움이 될 수 있기 때문에이 답변을 여기에 남겨두고 있습니다.


0

onActivityCreated () 내에서 getActivity () 메소드를 호출하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.