web-gelistirme-sc.com

Android. GetActivity () parçası bazen null döndürür

Geliştirici konsolu hata raporlarında bazen NPE sorunu olan raporları görüyorum. Kodumda neyin yanlış olduğunu anlamıyorum. Öykünücüde ve aygıt uygulamamda haciz olmadan iyi çalışıyor, ancak bazı kullanıcılar getActivity () yöntemi çağrıldığında fragman sınıfında NullPointerException alıyor.

Aktivite

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 sınıfı

public class FirstTask extends AsyncTask{

    private TaskListener taskListener;

    ...

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

Parça sınıfı

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();
        ...
    }
}

Belki de bu hata, uygulamalar arka plandan devam ettiğinde ortaya çıkar. Bu durumda bu durumu nasıl düzgün bir şekilde ele almalıyım?

185
Georgy Gobozov

Sorunum için bir çözüm bulduğum anlaşılıyor. Çok iyi açıklamalar verilmiştir burada ve burada . İşte benim örnek:

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

Bu koddaki ana fikir, uygulamanızı normal şekilde çalıştırırken, yeni parçalar oluşturup bunları adaptöre geçirmenizdir. Uygulama bölümünüze döndüğünüzde, fragman yöneticisi zaten bu fragmanın örneğine sahip ve onu fragman yöneticisinden alıp adaptöre geçirmeniz gerekiyor.

GÜNCELLEME

Ayrıca, getActivity () çağrılmadan önce Eklenenleri kontrol etmek için fragmanların kullanılması iyi bir uygulamadır. Bu, parça etkinlikten ayrıldığında boş bir işaretçi istisnasını önlemeye yardımcı olur. Örneğin, bir etkinlik, zaman uyumsuz bir görevi zorlayan bir parça içerebilir. Görev tamamlandığında, onTaskComplete dinleyicisi çağrılır.

@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();
    }

}

Parçayı açarsak, Bir görevi itin ve önceki bir etkinliğe geri dönmek için hızlıca geri basın, görev tamamlandığında, onAcostExecute () içindeki etkinliğe getActivity () yöntemini çağırarak erişmeye çalışır. Etkinlik zaten ayrılmışsa ve bu kontrol orada değilse:

if (isAdded()) 

sonra uygulama çöker.

121
Georgy Gobozov

Tamam, bu sorunun çözüldüğünü biliyorum ama bunun için çözümümü paylaşmaya karar verdim. Fragment için soyut ebeveyn sınıfı oluşturdum:

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

Gördüğünüz gibi, bir dinleyici ekledim, ne zaman ihtiyacım olursa, standart getActivity() yerine FragmentsActivity almalıyım.

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

Bundan kurtulmanın en iyi yolu, onAttach çağrıldığında aktivite referansını tutmak ve örneğin, örneğin.

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

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

onAttach(Activity) öğütüldüğü ve şimdi onAttach(Context) kullanıldığından beri düzenlendi

17
Pawan Maheshwari

Fragment içinde getActivity () gerektiren yöntemleri üst etkinlikte başlamadan önce çağırmayın.

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();
}
9
bvmobileapps

Ben bu tür problemlerle mücadele bir süredir ve güvenilir bir çözüm buldum.

this.getActivity()'nin null için Fragment döndürmeyeceğinden emin olmak oldukça zordur, özellikle de kodunuza Activity referanslarını geri çekmek için yeterli zaman veren herhangi bir ağ davranışıyla uğraşıyorsanız.

Aşağıdaki çözümde, ActivityBuffer adında küçük bir yönetim sınıfı ilan ediyorum. Temel olarak, bu class, sahip olunan Activity için güvenilir bir referans sağlama ve geçerli bir başvuru olduğunda geçerli bir Runnable bağlamında Activitys yürütme sözü verdi. Runnables, eğer Context mevcutsa derhal UI Konusunda yürütülecek şekilde zamanlanır, aksi takdirde Context hazır olana kadar yürütme ertelenir.

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

}
_

Uygulaması açısından, yukarıda belirtilen davranışla çakışacak yaşam döngüsü yöntemlerini uygulamaya özen göstermeliyiz 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;
    }

}
_

Son olarak, Fragment içinde BaseFragment alanını getActivity() çağrısına güvenilmez olduğunuz herhangi bir alanda this.getActivityBuffer().safely(...) çağrısı yapın ve görev için bir _ActivityBuffer.IRunnable_ ilan edin!

void run(final Activity pActivity) içeriğinin daha sonra UI Thread'ı boyunca yürütülmesi garanti edilir.

ActivityBuffer daha sonra aşağıdaki gibi kullanılabilir:

_this.getActivityBuffer().safely(
  new ActivityBuffer.IRunnable() {
    @Override public final void run(final Activity pActivity) {
       // Do something with guaranteed Context.
    }
  }
);
_
4
Mapsy
@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    // run the code making use of getActivity() from here
}
3
dheeran

Bunun eski bir soru olduğunu biliyorum ama cevabımı başkalarına veremediğim için cevabımı vermem gerektiğini düşünüyorum.

her şeyden önce: fragmentTransactions kullanarak dinamik olarak parçalar ekliyordum. İkincisi: fragmanlarım AsyncTasks (bir sunucudaki DB sorguları) kullanılarak değiştirildi. Üçüncüsü: fragmanım faaliyet başlangıcında başlatılmadı Dördüncü: fragman değişkenini elde etmek için özel bir fragman başlatması "oluştur veya yükle" kullandım. Dördüncü: oryantasyon değişikliği nedeniyle aktivite yeniden yaratıldı

Sorun, sorgunun cevabı nedeniyle parçayı "kaldırmak" istemiştim, ancak parça daha önce yanlış yaratılmıştı. Neden, daha sonra yapılacak "taahhüt" nedeniyle büyük olasılıkla, henüz kaldırılma zamanı geldiğinde fragman eklenmedi. Bu nedenle getActivity () öğesi null döndürüyordu.

Çözüm: 1) Yeni bir tane oluşturmadan önce parçanın ilk örneğini doğru bulmaya çalıştığımı kontrol etmek zorunda kaldım 2) Oryantasyon değişikliğini devam ettirmek için bu parçanın üzerine serRetainInstance (true) koymak zorunda kaldım (geri dönüş olmadan) bu nedenle gerekli değil, sorun değil) 3) “Eski parçayı yeniden oluşturmak veya eski haline getirmek” yerine, “çıkarmadan” hemen önce, parçayı doğrudan faaliyet başlangıcına koydum. Aktivite sırasında başlatmak, parça değişkenini kaldırmadan önce "yüklemek" (veya başlatmak) yerine getActivity sorunlarını önledi.

1
Feuby