기계적 인조 인간. 단편 getActivity ()는 때때로 널을 리턴합니다.


194

개발자 콘솔 오류 보고서에서 때때로 NPE 문제가있는 보고서가 표시됩니다. 내 코드의 문제점을 이해하지 못합니다. 에뮬레이터에서 내 장치 응용 프로그램은 강제 종료없이 잘 작동하지만 getActivity () 메서드가 호출되면 일부 사용자가 조각 클래스에서 NullPointerException을 얻습니다.

활동

pulic class MyActivity extends FragmentActivity{

    private ViewPager pager; 
    private TitlePageIndicator indicator;
    private TabsAdapter adapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        pager = (ViewPager) findViewById(R.id.pager);
        indicator = (TitlePageIndicator) findViewById(R.id.indicator);
        adapter = new TabsAdapter(getSupportFragmentManager(), false);

        adapter.addFragment(new FirstFragment());
        adapter.addFragment(new SecondFragment());
        indicator.notifyDataSetChanged();
        adapter.notifyDataSetChanged();

        // push first task
        FirstTask firstTask = new FirstTask(MyActivity.this);
        // set first fragment as listener
        firstTask.setTaskListener((TaskListener) adapter.getItem(0));
        firstTask.execute();
    }

    indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener()  {
        @Override
        public void onPageSelected(int position) {
            Fragment currentFragment = adapter.getItem(position);
            ((Taskable) currentFragment).executeTask();
        }

        @Override
        public void onPageScrolled(int i, float v, int i1) {}

        @Override
        public void onPageScrollStateChanged(int i) {}
    });
}

AsyncTask 클래스

public class FirstTask extends AsyncTask{

    private TaskListener taskListener;

    ...

    @Override
    protected void onPostExecute(T result) {
        ... 
        taskListener.onTaskComplete(result);
    }   
}

프래그먼트 클래스

public class FirstFragment extends Fragment immplements Taskable, TaskListener{

    public FirstFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.first_view, container, false);
    }

    @Override
    public void executeTask() {
        FirstTask firstTask = new FirstTask(MyActivity.this);
        firstTask.setTaskListener(this);
        firstTask.execute();
    }

    @Override
    public void onTaskComplete(T result) {
        // NPE is here 
        Resources res = getActivity().getResources();
        ...
    }
}

응용 프로그램이 백그라운드에서 다시 시작되면이 오류가 발생할 수 있습니다. 이 경우 어떻게이 상황을 올바르게 처리해야합니까?


문제를 알아 냈지만 해결책은 아닙니다. 이유는 모르겠지만 조각이 이전 활동을 재개합니다. 그리고 이것은 최근에 앱 목록에서 마지막 위치의 앱이 시스템에서 내 애플리케이션을 파괴하는 것처럼 보일 때만 발생합니다.
게오르기 고보 조프

1
백그라운드 조각에서 응용 프로그램을 다시 시작하면 onCreate / onResume 메소드 활동 전에 호출 된 onResume을 작성합니다. 분리 된 일부 조각이 여전히 살아 있고 재개하려고합니다.
게오르기 고보 조프

1
이 문자열에서 firstTask.setTaskListener ((TaskListener) adapter.getItem (0)); adapter.getItem (0)은 오래된 조각을 반환, 어댑터는 조각을 올바르게 제거하지 않습니다
Georgy Gobozov

9
그런데 훌륭한 활동 :) 질문, 의견 남음 및 답변-모두 한 사람이 수행합니다! +1
Prizoff

Context (getActivity ())를 onCreateView ()에 저장하십시오. 이것은 백그라운드 케이스에서 뷰를 다시 만들 때 호출되므로 호출됩니다.
sha

답변:


123

내 문제에 대한 해결책을 찾은 것 같습니다. 여기여기에 아주 좋은 설명이 있습니다 . 내 예는 다음과 같습니다.

pulic class MyActivity extends FragmentActivity{

private ViewPager pager; 
private TitlePageIndicator indicator;
private TabsAdapter adapter;
private Bundle savedInstanceState;

 @Override
public void onCreate(Bundle savedInstanceState) {

    .... 
    this.savedInstanceState = savedInstanceState;
    pager = (ViewPager) findViewById(R.id.pager);;
    indicator = (TitlePageIndicator) findViewById(R.id.indicator);
    adapter = new TabsAdapter(getSupportFragmentManager(), false);

    if (savedInstanceState == null){    
        adapter.addFragment(new FirstFragment());
        adapter.addFragment(new SecondFragment());
    }else{
        Integer  count  = savedInstanceState.getInt("tabsCount");
        String[] titles = savedInstanceState.getStringArray("titles");
        for (int i = 0; i < count; i++){
            adapter.addFragment(getFragment(i), titles[i]);
        }
    }


    indicator.notifyDataSetChanged();
    adapter.notifyDataSetChanged();

    // push first task
    FirstTask firstTask = new FirstTask(MyActivity.this);
    // set first fragment as listener
    firstTask.setTaskListener((TaskListener) getFragment(0));
    firstTask.execute();

}

private Fragment getFragment(int position){
     return savedInstanceState == null ? adapter.getItem(position) : getSupportFragmentManager().findFragmentByTag(getFragmentTag(position));
}

private String getFragmentTag(int position) {
    return "android:switcher:" + R.id.pager + ":" + position;
}

 @Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("tabsCount",      adapter.getCount());
    outState.putStringArray("titles", adapter.getTitles().toArray(new String[0]));
}

 indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            Fragment currentFragment = adapter.getItem(position);
            ((Taskable) currentFragment).executeTask();
        }

        @Override
        public void onPageScrolled(int i, float v, int i1) {}

        @Override
        public void onPageScrollStateChanged(int i) {}
 });

이 코드의 주요 아이디어는 애플리케이션을 정상적으로 실행하는 동안 새 단편을 작성하여 어댑터에 전달한다는 것입니다. 응용 프로그램 프래그먼트 관리자를 재개 할 때이 프래그먼트의 인스턴스가 이미 있으며 프래그먼트 관리자에서 가져 와서 어댑터로 전달해야합니다.

최신 정보

또한 getActivity ()가 호출되기 전에 프래그먼트를 사용하여 iPart를 확인하는 것이 좋습니다. 이것은 프래그먼트가 활동에서 분리 될 때 널 포인터 예외를 피하는 데 도움이됩니다. 예를 들어, 활동에는 비동기 작업을 푸시하는 조각이 포함될 수 있습니다. 작업이 완료되면 onTaskComplete 리스너가 호출됩니다.

@Override
public void onTaskComplete(List<Feed> result) {

    progress.setVisibility(View.GONE);
    progress.setIndeterminate(false);
    list.setVisibility(View.VISIBLE);

    if (isAdded()) {

        adapter = new FeedAdapter(getActivity(), R.layout.feed_item, result);
        list.setAdapter(adapter);
        adapter.notifyDataSetChanged();
    }

}

프래그먼트를 열고 작업을 푸시 한 다음 신속하게 뒤로 눌러 이전 활동으로 돌아 가면 작업이 완료되면 getActivity () 메소드를 호출하여 onPostExecute ()의 활동에 액세스하려고 시도합니다. 활동이 이미 분리되어 있고이 점검이없는 경우 :

if (isAdded()) 

그런 다음 응용 프로그램이 충돌합니다.


56
그러나 isAdded()각 액세스 전에 호출 해야하는 것은 성가시다 .
Ixx

25
가진 사이에 큰 차이가있을 것 같지 않습니다 if(isAdded())또는if(getActivity() != null)
StackOverflowed

19

좋아, 나는이 질문이 실제로 해결되었다는 것을 알고 있지만 이것에 대한 해결책을 공유하기로 결정했다. 내 추상 부모 클래스를 만들었습니다 Fragment.

public abstract class ABaseFragment extends Fragment{

    protected IActivityEnabledListener aeListener;

    protected interface IActivityEnabledListener{
        void onActivityEnabled(FragmentActivity activity);
    }

    protected void getAvailableActivity(IActivityEnabledListener listener){
        if (getActivity() == null){
            aeListener = listener;

        } else {
            listener.onActivityEnabled(getActivity());
        }
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        if (aeListener != null){
            aeListener.onActivityEnabled((FragmentActivity) activity);
            aeListener = null;
        }
    }

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

        if (aeListener != null){
            aeListener.onActivityEnabled((FragmentActivity) context);
            aeListener = null;
        }
    }
}

보시다시피, 나는 리스너를 추가 했으므로 Fragments Activitystandard 대신에 필요할 때마다 getActivity()전화해야합니다.

 getAvailableActivity(new IActivityEnabledListener() {
        @Override
        public void onActivityEnabled(FragmentActivity activity) {
            // Do manipulations with your activity
        }
    });

좋은 답변입니다! 실제 문제를 해결하기 때문에 올바른 것으로 표시해야합니다. 제 경우에는 작업에 관계없이 getActivity ()가 null이 아닌지 확인하는 것으로 충분하지 않습니다. 이것을 사용하고 있으며 완벽하게 작동합니다.
Hadas Kaminsky

18

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

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

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

onAttach(Activity)감가 상각되어 현재 onAttach(Context)사용 중이므로 수정되었습니다.


9
프래그먼트는 항상 부모 활동의 참조를 유지하고 getActivity () 메소드를 사용 가능하게합니다. 여기서는 동일한 참조를 유지합니다.
Pawan Maheshwari

8
활동과 이벤트를 공유하기 위해 프래그먼트가 필요한 경우 실제로 이것을 권장합니다. developer.android.com/guide/components/fragments.html ( "활동에 대한 이벤트 콜백 생성"을 찾으십시오)
Vering

6
onDetach 메소드를 추가하여 액티비티 참조를 무효화합니다
자정

2
예, onDetach 메소드에서 mActivity = null을 초기화하여 해당 활동 참조를 무효화하십시오.
Pawan Maheshwari

19
절대 그렇게하지 마십시오. 당신은 당신의 완전한 활동 (그리고 그것으로 전체 레이아웃 트리, 드로어 블 등)을 유출하고 있습니다. 경우 getActivity()반환 null로 더 이상 활동하지 않을 수 있습니다 때문입니다. 이것은 더러운 해결 방법입니다.
njzk2

10

부모 활동에서 onStart가 될 때까지 Fragment 내에서 getActivity ()가 필요한 메소드를 호출하지 마십시오.

private MyFragment myFragment;


public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);

    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    myFragment = new MyFragment();

    ft.add(android.R.id.content, youtubeListFragment).commit();

    //Other init calls
    //...
}


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

    //Call your Fragment functions that uses getActivity()
    myFragment.onPageSelected();
}

실제로 조각 생성자에서 작업을 시작했기 때문에 비슷한 문제가 발생했습니다. 고마워
Supreme Dolphin

4

나는 이런 종류의 문제 를 잠시 동안 싸우고 있었고, 나는 신뢰할 수있는 해결책을 생각해 냈다고 생각합니다.

그것은 그 확실히 알고 꽤 어려운 this.getActivity()반환하지 않을 nullA에 대한 Fragment당신의 코드를 철회 할 수있는 충분한 시간 제공 네트워크 동작의 모든 종류의 상대하고 특히, Activity참조.

아래 솔루션에서 나는이라는 작은 관리 클래스를 선언합니다 ActivityBuffer. 본질적으로 이것은 class소유에 대한 신뢰할 수있는 참조를 유지 하고 유효한 참조가있을 때마다 유효한 컨텍스트 내 Activity에서 실행을 약속 합니다. 의는 즉시 경우 UI 스레드에서 실행 예정이다 그 때까지 그렇지 않으면 실행이 지연되고 있습니다 준비가되어 있습니다.RunnableActivityRunnableContextContext

/** A class which maintains a list of transactions to occur when Context becomes available. */
public final class ActivityBuffer {

    /** A class which defines operations to execute once there's an available Context. */
    public interface IRunnable {
        /** Executes when there's an available Context. Ideally, will it operate immediately. */
        void run(final Activity pActivity);
    }

    /* Member Variables. */
    private       Activity        mActivity;
    private final List<IRunnable> mRunnables;

    /** Constructor. */
    public ActivityBuffer() {
        // Initialize Member Variables.
        this.mActivity  = null;
        this.mRunnables = new ArrayList<IRunnable>();
    }

    /** Executes the Runnable if there's an available Context. Otherwise, defers execution until it becomes available. */
    public final void safely(final IRunnable pRunnable) {
        // Synchronize along the current instance.
        synchronized(this) {
            // Do we have a context available?
            if(this.isContextAvailable()) {
                // Fetch the Activity.
                final Activity lActivity = this.getActivity();
                // Execute the Runnable along the Activity.
                lActivity.runOnUiThread(new Runnable() { @Override public final void run() { pRunnable.run(lActivity); } });
            }
            else {
                // Buffer the Runnable so that it's ready to receive a valid reference.
                this.getRunnables().add(pRunnable);
            }
        }
    }

    /** Called to inform the ActivityBuffer that there's an available Activity reference. */
    public final void onContextGained(final Activity pActivity) {
        // Synchronize along ourself.
        synchronized(this) {
            // Update the Activity reference.
            this.setActivity(pActivity);
            // Are there any Runnables awaiting execution?
            if(!this.getRunnables().isEmpty()) {
                // Iterate the Runnables.
                for(final IRunnable lRunnable : this.getRunnables()) {
                    // Execute the Runnable on the UI Thread.
                    pActivity.runOnUiThread(new Runnable() { @Override public final void run() {
                        // Execute the Runnable.
                        lRunnable.run(pActivity);
                    } });
                }
                // Empty the Runnables.
                this.getRunnables().clear();
            }
        }
    }

    /** Called to inform the ActivityBuffer that the Context has been lost. */
    public final void onContextLost() {
        // Synchronize along ourself.
        synchronized(this) {
            // Remove the Context reference.
            this.setActivity(null);
        }
    }

    /** Defines whether there's a safe Context available for the ActivityBuffer. */
    public final boolean isContextAvailable() {
        // Synchronize upon ourself.
        synchronized(this) {
            // Return the state of the Activity reference.
            return (this.getActivity() != null);
        }
    }

    /* Getters and Setters. */
    private final void setActivity(final Activity pActivity) {
        this.mActivity = pActivity;
    }

    private final Activity getActivity() {
        return this.mActivity;
    }

    private final List<IRunnable> getRunnables() {
        return this.mRunnables;
    }

}

구현 측면에서 Pawan M의 위에서 설명한 동작과 일치하도록 수명주기 방법을 적용하도록주의를 기울여야합니다 .

public class BaseFragment extends Fragment {

    /* Member Variables. */
    private ActivityBuffer mActivityBuffer;

    public BaseFragment() {
        // Implement the Parent.
        super();
        // Allocate the ActivityBuffer.
        this.mActivityBuffer = new ActivityBuffer();
    }

    @Override
    public final void onAttach(final Context pContext) {
        // Handle as usual.
        super.onAttach(pContext);
        // Is the Context an Activity?
        if(pContext instanceof Activity) {
            // Cast Accordingly.
            final Activity lActivity = (Activity)pContext;
            // Inform the ActivityBuffer.
            this.getActivityBuffer().onContextGained(lActivity);
        }
    }

    @Deprecated @Override
    public final void onAttach(final Activity pActivity) {
        // Handle as usual.
        super.onAttach(pActivity);
        // Inform the ActivityBuffer.
        this.getActivityBuffer().onContextGained(pActivity);
    }

    @Override
    public final void onDetach() {
        // Handle as usual.
        super.onDetach();
        // Inform the ActivityBuffer.
        this.getActivityBuffer().onContextLost();
    }

    /* Getters. */
    public final ActivityBuffer getActivityBuffer() {
        return this.mActivityBuffer;
    }

}

마지막으로 에 대한 호출에 대해 신뢰할 수없는 Fragment확장 영역 내 에서 간단하게 전화를 걸어 작업을 선언 하십시오!BaseFragmentgetActivity()this.getActivityBuffer().safely(...)ActivityBuffer.IRunnable

그러면 콘텐츠가 void run(final Activity pActivity)UI 스레드를 따라 실행됩니다.

ActivityBuffer다음과 같이 사용할 수 있습니다 :

this.getActivityBuffer().safely(
  new ActivityBuffer.IRunnable() {
    @Override public final void run(final Activity pActivity) {
       // Do something with guaranteed Context.
    }
  }
);

this.getActivityBuffer (). safely (...) 메소드 사용 예제를 추가 할 수 있습니까?
fahad_sust

3
@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    // run the code making use of getActivity() from here
}

제공하는 솔루션에 대한 설명을 조금 더 추가하여 답변을 더 자세히 설명해 주시겠습니까?
abarisone 2016 년

1

나는 이것이 오래된 질문이라는 것을 알고 있지만 내 문제는 다른 사람들에 의해 해결되지 않았기 때문에 그것에 대한 대답을 제공해야한다고 생각합니다.

우선 : 나는 fragmentTransactions를 사용하여 조각을 동적으로 추가하고있었습니다. 둘째 : 내 조각은 AsyncTasks (서버의 DB 쿼리)를 사용하여 수정되었습니다. 세 번째 : 활동 시작시 내 조각이 인스턴스화되지 않았습니다. 네 번째 : 조각 변수를 가져 오기 위해 사용자 정의 조각 인스턴스화를 사용하여 "생성 또는로드"했습니다. 넷째 : 방향 변경으로 인해 활동이 다시 작성되었습니다.

문제는 쿼리 응답으로 인해 조각을 "제거"하고 싶었지만 조각이 직전에 잘못 만들어졌습니다. 나중에 "커밋"으로 인해 조각을 제거 할 때 조각이 아직 추가되지 않은 이유를 모르겠습니다. 따라서 getActivity ()가 널을 리턴했습니다.

해결 방법 : 1) 새 인스턴스를 만들기 전에 조각의 첫 번째 인스턴스를 올바르게 찾으려고했는지 확인해야했습니다 .2) 방향 변경을 통해 유지하기 위해 해당 조각에 serRetainInstance (true)를 배치해야합니다 (백 스택 없음) 3) "제거하기"직전에 "오래된 조각을 재생성하거나 얻는 것"대신, 나는 활동을 시작할 때 조각을 직접 넣었다. 프래그먼트 변수를 제거하기 전에 "로드"(또는 인스턴스화)하는 대신 활동 시작시이를 초기화하면 getActivity 문제가 발생하지 않습니다.


0

Kotlin에서는 getActivity () null 조건을 처리하기 위해이 방법을 시도 할 수 있습니다.

   activity.let { // activity == getActivity() in java

        //your code here

   }

활동이 null인지 확인하고 null이 아닌 경우 내부 코드를 실행합니다.

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