getActivity (), Fragman işlevinde null değerini döndürür


192

Ben böyle bir kamu yöntemi ile bir parçası (F1) var

public void asd() {
    if (getActivity() == null) {
        Log.d("yes","it is null");
    }
}

ve evet (Aktiviteden) dediğimde, bu boştur ...

FragmentTransaction transaction1 = getSupportFragmentManager().beginTransaction();
F1 f1 = new F1();
transaction1.replace(R.id.upperPart, f1);
transaction1.commit();
f1.asd();

Çok yanlış yaptığım bir şey olmalı, ama bunun ne olduğunu bilmiyorum


Bu gönderiye yapıştırdığınızda sadece bir hata olup olmadığından emin değilim, ancak sonra parantez gerekiyor getActivity(). Ayrıca, parçayı nasıl somutlaştırıyorsunuz? Layout.xml dosyasında var mı?
CaseyB

İkinci kod parçası nereye aittir? Faaliyetin oncreate () yöntemine? Ve zaten setContentView () öğesini çağırdınız mı?
Franziskus Karsunke

R.id.upperPar, mizanpajdaki bir öğedir, bu yüzden parçayla değiştirilmesi gerekiyordu, ancak bu benim sorunum değil. Özel parça yöntemlerinde getActivity () dediğimde neden boş olduğumu anlamıyorum, onActivityCreated yönteminde getActivity'nin gerçek olmayan bir etkinlik olduğunu
söyleyelim

o düzenleri içinde değil sorun, uygulama iyi çalışır ama getActivity için null adlı almak neden o konuların burada değil gerektiği gibi oluşturulur fragmanını içine Btw tüm unsurları?
Lukap

1
Bu yöntemi çağırmalısınız: f1.asd (); fragman sınıfınızda geçersiz kılınacak olan onActivityCreated yönteminde.
Namrata Bagerwal

Yanıtlar:


164

commit işlemi zamanlar, yani hemen gerçekleşmez, ancak ana iş parçacığının bir sonraki hazırlığında ana iş parçacığı üzerinde çalışmak üzere zamanlanır.

Bir

onAttach(Activity activity)

yöntemine Fragmentbir kırılma noktası koymak ve çağrınıza göre ne zaman çağrıldığını görmek asd(). asd()Çıkışlara çağrı yaptığınız yöntemden sonra çağrıldığını göreceksiniz . onAttachBurada çağrı Fragmentaktivitesine ve bu noktadan bağlandığı getActivity()boş olmayan bir döner (nb aynı zamanda orada onDetach()arama).


5
Sorununuzu nasıl çözeceğinizi anlamadım. GetActivity () yöntemim stille hazır değilse, FragmentActivity nesnesinin başvurusunu nasıl alabilirim?
CeccoCQ

2
@Vivek Neyi başarmak istediğinizi tam olarak bilmiyorum. Hemen bir iletişim kutusu görüntülemek için Fragment'a ihtiyacınız varsa, oluşturmada, örneğin kendi yöntemlerinde onCreateViewveya onActivityCreatedyöntemlerinde yapması gereken şeyi yapmasını sağlayın . Sorular gönderilirken neden asd () çağrılması gerektiğini soruyorum.
PJL

3
onAttach kullanımdan kaldırıldı
abbasalim

6
onAttach (Activity mActivity) amortismana tabi tutuluyor gibi görünüyor .. bunun için herhangi bir çözüm
ashish.n

4
API 24 tanıtıldıcommitNow()
Nicolas

92

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

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

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

34
MActivity = null onDetach () ayarlamalıyız?
Oliver Pearmain

5
@OliverPearmain, onDetach () içinde yapacaksanız, o zaman kar olmaz. OnDestory () içinde geçersiz kılmanız gerekir. Ayrıca WeakRefernce'de tutmak zorundasınız.
Kirill Popov

Ben her ikisini de etkisiz hale getiriyorum onDestroy()ve onDetach()çünkü onDestroy()çağrılacak garanti edilmez.
Muhammed Ali

8
Activityİçeri girmezsek sızıyor muyuz onDestroy()?
Muhammed Ali

3
Göre developer.android.com/intl/zh-tw/guide/components/... , onAttach ()) (onCreateView çağırmadan önce denir. Ama onCreateView () getActivity () çağırırken hala bir NullPointerException olsun. Bu nasıl olabilir?
Kimi Chiu

82

Bu getActivity(), parça kaldırıldıktan sonra bitmiş başka bir iş parçacığında aradığınızda oldu . Bir HTTP isteği tamamlandığında ( örneğin) tipik durum getActivity()(örneğin a için Toast) çağrılır onResponse.

Bundan kaçınmak için bir alan adı tanımlayabilir mActivityve bunun yerine kullanabilirsiniz getActivity(). Bu alan Fragment'ın onAttach () yönteminde şu şekilde başlatılabilir:

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

    if (context instanceof Activity){
        mActivity =(Activity) context;
    }
}

Projelerimde, genellikle bu özellikle tüm Fragmanlarım için bir temel sınıf tanımlarım:

public abstract class BaseFragment extends Fragment {

    protected FragmentActivity mActivity;

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

    if (context instanceof Activity){
        mActivity =(Activity) context;
    }
}
}

Mutlu kodlama,


19
mActivity = null ayarlayalım; onDetach () içinde mi?
Bharat Dodeja

thucnguyen, nasıl tek aktivite uygulaması için mActivity statik ilan etmek?
13'te iamthevoid

Hiç Activitybaşka bir iş parçacığından erişmek için bir neden görmüyorum . Zaten onunla hiçbir şey yapamazsın, bir Tost bile göstermezsin. Bu nedenle, önce işi ana iş parçacığına aktarmalı veya Etkinliği hiç kullanmamalısınız.
Dzmitry Lazerka

4
@BharatDodeja mActivity = null onDetach olarak ayarlamalıyız? Buldun mu?
SLearner

5
Bu, etkinliği iptal etmeden sızacak.
Darek Deoniziak

30

OnAttach'taki etkinliğe gönderme yapmayı öneren diğer cevaplar, gerçek soruna bir bandaj önermektedir. GetActivity null değerini döndürdüğünde, Parçanın Aktiviteye eklenmediği anlamına gelir. En yaygın olarak, Etkinlik rotasyon veya Etkinlik bittiği için ortadan kalktığında, ancak Parçanın hala bir çeşit geri arama dinleyicisi kayıtlı olduğunda gerçekleşir. Etkinlikle ilgili bir şey yapmanız gerekiyorsa ancak Etkinlik gittiğinde dinleyici çağrıldığında yapabileceğiniz pek bir şey yoktur. Kodunuzda kontrol etmelisinizgetActivity() != nullve eğer orada değilse hiçbir şey yapmayın. Gittiğiniz Faaliyete ilişkin bir referans tutarsanız, Faaliyetin çöp toplanmasını engelliyorsunuz demektir. Yapmaya çalışabileceğiniz kullanıcı arayüzü şeyleri kullanıcı tarafından görülmez. Geri arama dinleyicisinde UI ile ilgili olmayan bir şey için bir Bağlamın olmasını isteyebileceğiniz bazı durumlar hayal edebiliyorum, bu durumlarda muhtemelen Uygulama bağlamını almak daha mantıklıdır. onAttachHile büyük bir bellek sızıntısı olmamasının tek nedeni, normalde geri arama dinleyicisi yürüttükten sonra artık gerekli olmayacağı ve Parça, tüm Görünümleri ve Etkinlik bağlamıyla birlikte toplanan çöp olabileceğine dikkat edin. Eğer sensetRetainInstance(true) Etkinlik alanı da korunacağı için bellek sızıntısı olasılığı daha yüksektir, ancak döndürüldükten sonra geçerli etkinlik değil önceki Etkinlik olabilir.


1
Bu benim sorunum. Bir işlem yapan bir parçam var -> sonra bir reklam gösteriliyor -> ve sonra proess devam ediyor. Bazı cihazlarda reklamdan döndükten sonra (bir dinleyici aracılığıyla reklam etkinliklerine) getActivity () geçersizdir. Ama işi bitirmek için işin diğer kısmını yapmaya devam etmem gerekiyor. Yani buna bir çözüm yok mu?
Notbad

Tam olarak karşı karşıya olduğum şey bu. Bazı faturalandırma şeyleri yaptığım bir parçada bir Etkinlik arayüzü var. Ödeme yapıldıktan sonra, bir şey yapmak için arayüzü kullanmak istiyorum, ancak arayüz boştu.
Freddie

Bu, bu konuyla ilgili 100'lü SO sorusuna doğru genel cevap gibi görünüyor.
Manuel

En iyi cevap. SO'da Android için çok fazla bandajlı çözüm var.
maxbeaudoin

Eğer biraz işlem yapmak istersem, getActivity () kullanılabilir olduktan sonra nasıl çalışabilirim (eğer varsa).
Sreekanth Karumanaghat

17

Android API düzey 23'ten bu yana, onAttach (Etkinlik etkinliği) kullanımdan kaldırıldı. OnAttach (Bağlam bağlamı) kullanmanız gerekir. http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

Etkinlik bir bağlamdır, dolayısıyla bağlamın bir Etkinlik olup olmadığını kontrol edebilir ve gerekirse yayınlayabilirsiniz.

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

    Activity a;

    if (context instanceof Activity){
        a=(Activity) context;
    }

}

Nasıl kullanılır
ARR.s

10

PJL haklı. Onun önerisini kullandım ve ben de bunu yaptım:

  1. fragman için tanımlanmış global değişkenler:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. uygulanan

@Override
public void onAttach(Activity activity) {
  super.onAttach(activity);
  synchronized (attachingActivityLock) {
      syncVariable = true;
      attachingActivityLock.notifyAll();
  }
}

3. Ben ana iş parçacığı üzerinde çalışacak, ben adım 4 ile iş parçacığı bloke olur, çünkü iş parçacığı, getActivity (), çağırmak gerekir, iş parçasını tamamladı ve onAttach () asla çağrılamaz.

    Thread processImage = new Thread(new Runnable() {

        @Override
        public void run() {
            processImage();
        }
    });
    processImage.start();

4. getActivity () çağırmam gereken fonksiyonumda bunu kullanıyorum (getActivity () çağrısından önce)

    synchronized (attachingActivityLock) {
        while(!syncVariable){
            try {
                attachingActivityLock.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

Bazı kullanıcı arayüzü güncellemeleriniz varsa, bunları kullanıcı arayüzü iş parçacığında çalıştırmayı unutmayın. Yaptım böylece ImgeView güncelleştirmek gerekir:

image.post(new Runnable() {

    @Override
    public void run() {
        image.setImageBitmap(imageToShow);
    }
});

7

Commit () işleminden sonra geri çağrıların çağrılma sırası:

  1. Commit () yönteminden hemen sonra manuel olarak çağırdığınız yöntem
  2. onAttach ()
  3. onCreateView ()
  4. onActivityCreated ()

Bazı Views içeren bazı işler yapmam gerekiyordu, bu yüzden onAttach () benim için çalışmadı; çöktü. Bu yüzden, hemen sonra command () (1.), sonra onCreateView () (3.) içinde görünümü ele kodun diğer bölümü denilen bir yöntem içinde bazı params ayarlayan kodumu bir bölümünü taşıdı.


3

OkHttp kullanıyorum ve sadece bu sorunla karşılaştım.


İlk bölüm için @ thucnguyen doğru yoldaydı .

Bu, parça kaldırıldıktan sonra bitmiş başka bir iş parçacığında getActivity () öğesini çağırdığınızda oldu. Tipik durum, bir HTTP isteği tamamlandığında (örneğin onResponse'de) getActivity () (örneğin bir Tost için) öğesini çağırmaktır.

Bazı HTTP çağrıları , etkinlik kapatıldıktan sonra bile yürütülüyordu (çünkü bir HTTP isteğinin tamamlanması biraz zaman alabilir). Sonra, HttpCallbackbazı Fragment alanlarını güncellemeye çalıştım ve nulldenerken bir istisna aldım getActivity().

http.newCall(request).enqueue(new Callback(...
  onResponse(Call call, Response response) {
    ...
    getActivity().runOnUiThread(...) // <-- getActivity() was null when it had been destroyed already

IMO çözümü, parça artık hayatta olmadığında geri çağrıların oluşmasını önlemektir (ve bu sadece Okhttp ile değil).

Düzeltme: Önleme.

Eğer parça yaşam döngüsüne bir göz attıysanız (daha fazla bilgi burada ), onAttach(Context context)ve onDetach()yöntemler olduğunu fark edeceksiniz . Bunlar, Parça bir etkinliğe ait olduktan sonra ve sırasıyla durmadan hemen önce çağrılır.

Bu, onDetachyöntemde denetleyerek bu geri aramanın gerçekleşmesini engelleyebileceğimiz anlamına gelir .

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

    // Initialize HTTP we're going to use later.
    http = new OkHttpClient.Builder().build();
}

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

    // We don't want to receive any more information about the current HTTP calls after this point.
    // With Okhttp we can simply cancel the on-going ones (credits to https://github.com/square/okhttp/issues/2205#issuecomment-169363942).
    for (Call call : http.dispatcher().queuedCalls()) {
        call.cancel();
    }
    for (Call call : http.dispatcher().runningCalls()) {
        call.cancel();
    }
}

2

Bu işlevi nereye çağırıyorsunuz? Eğer yapıcısında çağırırsanız Fragment, geri döner null.

Sadece getActivity()yöntem onCreateView()yürütüldüğünde arayın .


1

Aşağıdaki gibi yapın. Bence sana yardımcı olacak.

private boolean isVisibleToUser = false;
private boolean isExecutedOnce = false;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_my, container, false);
    if (isVisibleToUser && !isExecutedOnce) {
        executeWithActivity(getActivity());
    }
    return root;
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    this.isVisibleToUser = isVisibleToUser;
    if (isVisibleToUser && getActivity()!=null) {
        isExecutedOnce =true;
        executeWithActivity(getActivity());
    }
}


private void executeWithActivity(Activity activity){
    //Do what you have to do when page is loaded with activity

}

1

Hala onAttach (Aktivite etkinliği) ile sorunu olanlar, Sadece Bağlam olarak değiştirildi -

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

Çoğu durumda bağlamı kaydetmek sizin için yeterli olacaktır - örneğin getResources () yapmak istiyorsanız bunu doğrudan bağlamdan yapabilirsiniz. Hala Faaliyetinizle ilgili bağlam yapmanız gerekiyorsa -

 @Override
public void onAttach(Context context) {
    super.onAttach(context);
    mActivity a; //Your activity class - will probably be a global var.
    if (context instanceof mActivity){
        a=(mActivity) context;
    }
}

Kullanıcı tarafından önerilen1868713.


0

OnAttach kullanarak veya her yere onAttach koymak istemiyorsanız, ana App sınıfına ApplicationContext döndüren bir yöntem koyabilirsiniz:

public class App {
    ...  
    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        context = this;
    }

    public static Context getContext() {
        return context;
    }
    ...
}

Bundan sonra, projenizin her yerinde bu şekilde yeniden kullanabilirsiniz:

App.getContext().getString(id)

Bunun sizin için işe yaramaması durumunda lütfen bize bildirin.


0

Başka bir iyi çözüm, Android'in MVVM mimarisiyle LiveData'sını kullanmak olabilir. ViewModel'inizde bir LiveData nesnesi tanımlarsınız ve parçanızda gözlemlersiniz ve LiveData değeri değiştirildiğinde, gözlemcinizi (bu örnekte parça) yalnızca parçanız aktif durumdaysa bilgilendirir, bu nedenle kullanıcı arayüzünüzün çalışmasını sağlar ve etkinliğe yalnızca parçanız etkin durumdayken erişir. Bu, LiveData ile birlikte gelen bir avantajdır

Tabii ki bu soru ilk sorulduğunda, LiveData yoktu. Bu cevabı burada bırakıyorum çünkü gördüğüm kadarıyla hala bu problem var ve birisi için yararlı olabilir.


0

OnActivityCreated () içindeki getActivity () yöntemini çağırı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.