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

개발자 콘솔 오류 보고서에서 때때로 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();
        ...
    }
}

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



답변

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

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()) 

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


답변

좋아, 나는이 질문이 실제로 해결되었다는 것을 알고 있지만 이것에 대한 해결책을 공유하기로 결정했다. 내 추상 부모 클래스를 만들었습니다 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
        }
    });


답변

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

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

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

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


답변

부모 활동에서 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();
}


답변

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

그것은 그 확실히 알고 꽤 어려운 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.
    }
  }
);


답변

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


답변

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

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

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

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