programing

안드로이드.fragment getActivity()가 null을 반환하는 경우가 있습니다.

powerit 2023. 9. 25. 23:09
반응형

안드로이드.fragment getActivity()가 null을 반환하는 경우가 있습니다.

개발자 콘솔 오류 보고서에서 NPE 문제가 있는 보고서가 가끔 나타납니다.저는 제 코드에 무엇이 문제인지 이해할 수 없습니다.에뮬레이터에서 장치 응용 프로그램이 강제로 닫히지 않고 잘 작동하지만 일부 사용자는 NullPointer를 얻습니다.getActivity() 메서드가 호출될 때 fragment 클래스에서 예외입니다.

활동

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) {}
    });
}

비동기 태스크 클래스

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()를 호출하기 전에 fragment를 사용하여 Added를 확인하는 것이 좋습니다.이렇게 하면 프래그먼트가 활동에서 분리될 때 널 포인터 예외를 피할 수 있습니다.예를 들어, 활동에는 비동기 작업을 푸시하는 프래그먼트가 포함될 수 있습니다.작업이 완료되면 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();
    }

}

프래그먼트를 열고 작업을 푸시한 다음 빠르게 뒤로 눌러 이전 작업으로 돌아갑니다. 작업이 완료되면 onPost의 작업에 액세스하려고 합니다.getActivity() 메서드를 호출하여 실행합니다.활동이 이미 분리되어 있는데 이 체크가 없는 경우:

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 Activityd 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)사용되고 있습니다.

상위 액티비티에서 시작할 때까지 getActivity()가 필요한 메서드를 Fragment 내에서 호출하지 마십시오.

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()않을 것입니다null당분간은Fragment . .Activity문헌

에서, 는 라고합니다.ActivityBuffer으로, class입니다에 할 수 참조를 을 다룹니다.Activity을 약속하고 .RunnableActivity유효한 참조가 있을 때마다 상황을 파악할 수 있습니다.Runnable 는 UI가 됩니다될 됩니다.Context할 수 있습니다. 그렇지 됩니다. 그렇지 않으면 실행이 지연됩니다.Context준비가 되었습니다.

/** 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;
    }

}

으로 내의 FragmentBaseFragment할 수 걸 됐어요getActivity(), 에 를 걸 수 있습니다.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를 사용하여 fragments를 동적으로 추가하고 있었습니다.둘째: 내 프래그먼트는 AsyncTasks(서버의 DB 쿼리)를 사용하여 수정되었습니다.셋째: 내 프래그먼트가 액티비티 시작 시 인스턴스화되지 않았습니다. 넷째: 프래그먼트 변수를 가져오기 위해 사용자 지정 프래그먼트 인스턴스화 "create or load it"을 사용했습니다.넷째: 방향전환으로 인해 활동이 재창조되었습니다.

문제는 질의응답 때문에 프래그먼트를 '제거'하고 싶었는데 바로 직전에 프래그먼트가 잘못 생성된 것입니다.나중에 "커밋"을 했기 때문에 제거할 시간이 되었을 때 프래그먼트가 아직 추가되지 않은 이유를 모르겠습니다.따라서 getActivity()가 null을 반환했습니다.

해결책 : 1)새 프래그먼트를 생성하기 전에 프래그먼트의 첫 번째 인스턴스를 올바르게 찾고 있는지 확인해야 했습니다 2)방향 변경을 통해 유지하기 위해 해당 프래그먼트에 serRetainInstance(true)를 넣어야 했습니다(백스택이 필요 없으므로 문제 없음) 3)"제거" 직전에 "재생하거나 오래된 조각을 가져오는" 대신에, 저는 그 조각을 직접 활동 시작에 넣었습니다.fragment 변수를 제거하기 전에 fragment 변수를 "로딩"(또는 인스턴스화)하는 대신 액티비티 시작 시에 인스턴스화하면 getActivity 문제가 방지됩니다.

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

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

        //your code here

   }

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

언급URL : https://stackoverflow.com/questions/11631408/android-fragment-getactivity-sometimes-returns-null

반응형