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


194

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 uygulamam forcecloses olmadan iyi çalışır, ancak bazı kullanıcılar getActivity () yöntemi çağrıldığında parça sınıfında NullPointerException alır.

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 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?


Bir sorun çözdüm, ama çözüm değil. Nedenini bilmiyorum ama parça daha önceki aktivitelerine devam ediyor. Ve bu sadece son uygulamalar listesindeki son konumdaki uygulamamın, sistem uygulamamı yok ettiği anlaşılıyor.
Georgy Gobozov

1
Uygulamamı arka plan fragmetn'den devam ettirdiğimde onCreate / onResume yönteminden önce bir onResume çağrılmış. Hala canlı ve devam etmeye çalışan bir parça ayrılmış gibi görünüyor.
Georgy Gobozov

1
bu dizede firstTask.setTaskListener ((TaskListener) adaptor.getItem (0)); adaptor.getItem (0) eski parçayı döndür, adaptör parçaları doğru bir şekilde kaldırmayın
Georgy Gobozov

9
Bu arada büyük aktivite :) soru sordu, yorum bıraktı ve cevap verildi - hepsi tek bir kişi tarafından yapılır! Bunlar için +1.
Prizoff

Context (getActivity ()) öğesini onCreateView () içine kaydedin, çünkü bu görünüm arka plan durumunda yeniden oluşturulduğunda çağrılır.
sha

Yanıtlar:


123

Görünüşe göre sorunuma bir çözüm buldum. Burada ve burada çok iyi açıklamalar verilmiştir . İş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 parçası yöneticinize devam ettiğinizde bu parçanın örneği zaten vardır ve bunu parça yöneticisinden almanız ve bağdaştırıcıya iletmeniz gerekir.

GÜNCELLEME

Ayrıca, getActivity () çağrılmadan önce is Added öğesini kontrol etmek için parçalar kullanılırken 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çar, bir görevi iletir ve daha sonra önceki bir etkinliğe geri dönmek için hızlıca geri basarsak, görev bittiğinde, getActivity () yöntemini çağırarak onPostExecute () öğesindeki etkinliğe erişmeye çalışır. Etkinlik zaten ayrılmışsa ve bu kontrol yoksa:

if (isAdded()) 

uygulama çöküyor.


56
Bu her ne kadar sinir bozucu, isAdded()her erişim önce aramak zorunda ... kod çirkin yapar.
Ixx

25
Sahip arasındaki bir fark var gibi görünmüyor if(isAdded())yaif(getActivity() != null)
StackOverflowed

19

Tamam, bu sorunun gerçekten çözüldüğünü biliyorum ama bunun için çözümümü paylaşmaya karar verdim. Benim için soyut ebeveyn sınıfı oluşturdum 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;
        }
    }
}

Gördüğünüz gibi, bir dinleyici ekledim, bu yüzden Fragments Activitystandart yerine getActivity()almam gerektiğinde, aramam gerekecek

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

Mükemmel cevap! gerçek sorunu çözdüğü için doğru olan olarak işaretlenmelidir: Benim durumumda olsun ne olursa olsun görevimi tamamlamak gerekir çünkü getActivity () boş olmadığını kontrol etmek yeterli değildir. Bunu kullanıyorum ve mükemmel çalışıyor.
Hadas Kaminsky

18

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

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

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

Düzenlendi, onAttach(Activity)amortisman yapıldığı ve şimdi onAttach(Context)kullanıldığından


9
Parçalar her zaman üst etkinliğinin referansını tutar ve getActivity () yöntemiyle kullanılabilir olmasını sağlar, burada aynı referansı koruyoruz.
Pawan Maheshwari

8
Etkinliği etkinliklerle paylaşmak için fragmanınıza ihtiyacınız varsa Google bunu gerçekten önerir. developer.android.com/guide/components/fragments.html ("Etkinliğe etkinlik geri aramaları oluşturma"
konusuna bakın

6
etkinlik referansını geçersiz kılan onDetach yöntemini eklemek isteyebilirsiniz
gece yarısı

2
evet, etkinlik referansını geçersiz kılmak için onDetach yönteminde mActivity = null değerini başlatın.
Pawan Maheshwari

19
bunu asla yapma. tüm etkinliğinizi (ve bununla birlikte tüm düzen ağacını, çekilebilirler ve benzeri) sızdırıyorsunuz. Eğer getActivity()döner boş artık bir etkinlikte değildir çünkü, öyle. Bu kirli bir çözüm.
njzk2

10

Parçanın içindeki üst Etkinlikte başlatılıncaya kadar getActivity () gerektiren yöntemleri ç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();
}

Aslında, benzer bir sorunum vardı çünkü görevi parça yapıcısında başlıyordum. Çok teşekkürler.
Yüce Yunus

4

Bir süredir bu tür bir sorunla mücadele ediyorum ve bence güvenilir bir çözüm buldum.

Özellikle , kodunuzu referansları geri çekmek için yeterli zaman veren herhangi bir ağ davranışı ile uğraşıyorsanız, bir this.getActivity()için geri dönmeyeceğinden emin olmak oldukça zordur .nullFragmentActivity

Aşağıdaki çözümde, adında küçük bir yönetim sınıfı ilan ediyorum ActivityBuffer. Esasen, bu class, bir sahiplenmeye güvenilir bir referans sağlamak Activityve geçerli bir referans olduğunda Runnablegeçerli bir Activitybağlamda s yürütme sözü vermekle ilgilidir . RunnableLer derhal UI iş parçacığı üzerinde yürütülmesi için planlanan Contextbu kadar aksi ertelenir, kullanılabilir Contexthazırdır.

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

}

Uygulanması açısından, Pawan M tarafından yukarıda açıklanan davranışa denk gelecek şekilde yaşam döngüsü yöntemlerini uygulamaya dikkat etmeliyiz :

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, bir çağrı için güvenilmez Fragmentolduğunuzu uzatan herhangi bir alanda , sadece bir çağrı yapın ve görev için bir ilan edin!BaseFragmentgetActivity()this.getActivityBuffer().safely(...)ActivityBuffer.IRunnable

Daha void run(final Activity pActivity)sonra içeriğinizin UI İş Parçacığı boyunca yürütülmesi garanti edilir.

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

This.getActivityBuffer (). Safe (...) yöntemini kullanmaya bir örnek ekleyebilir misiniz?
fahad_sust

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

Verdiğiniz çözüm hakkında biraz daha açıklama ekleyerek cevabınızı biraz daha açıklayabilir misiniz?
abarisone

1

Bunun eski bir soru olduğunu biliyorum ama cevabımı başkaları tarafından çözülmediği için cevap vermem gerektiğini düşünüyorum.

her şeyden önce: i fragmentTransactions kullanarak dinamik olarak parçaları ekliyordu. İkincisi: parçalarım AsyncTasks (sunucudaki DB sorguları) kullanılarak değiştirildi. Üçüncüsü: benim parçam aktivite başlangıcında başlatılmadı Dördüncü: Ben parça değişkenini almak için "oluştur veya yükle" özel bir parça örnekleme kullandım. Dördüncüsü: Oryantasyon değişikliği nedeniyle aktivite yeniden yaratıldı

Sorun, "sorgu cevap nedeniyle parçası kaldırmak" istedi, ama parça hemen önce yanlış oluşturuldu. Muhtemelen daha sonra yapılacak "taahhüt" nedeniyle, parça henüz kaldırılma zamanı henüz eklenmedi bilmiyorum. Bu nedenle getActivity () null döndürüyordu.

Çözüm: 1) Yeni bir tane oluşturmadan önce parçanın ilk örneğini doğru bir şekilde bulmaya çalıştığımı kontrol etmek zorunda kaldım. 3) "Kaldır" dan hemen önce "yeniden oluşturma veya eski parçayı alma" yerine, parçayı doğrudan aktivite başlangıcına koydum. Parça değişkenini kaldırmadan önce "yüklemek" (veya örneklemek) yerine etkinlik başlangıcında örneklemek, getActivity sorunlarını önledi.


0

Kotlin'de getActivity () null koşulunu işlemek için bu yolu deneyebilirsiniz.

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

        //your code here

   }

Etkinliğin boş olup olmadığını kontrol eder ve boş değilse iç kodu yürütün.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.