getActivity()がフラグメント関数でnullを返す


191

このようなパブリックメソッドのフラグメント(F1)があります

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

そして、私が(アクティビティから)それを呼び出すと、それはnullです...

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

それは私が非常に間違っていることであるに違いありませんが、それが何であるかはわかりません


この投稿に貼り付けたときにエラーがあったかどうかはわかりませんが、の後には括弧が必要getActivity()です。また、フラグメントをどのようにインスタンス化していますか?あなたのlayout.xmlにそれがありますか?
CaseyB

2番目のコードフラグメントはどこに属していますか?アクティビティのoncreate()メソッドに?そして、あなたはすでにsetContentView()を呼び出しましたか?
Franziskus Karsunke

R.id.upperParはレイアウトの要素なので、フラグメントで置き換えることになっていますが、それは私の問題ではありません。カスタムフラグメントメソッドでgetActivity()を呼び出すとnullになる理由がわかりません。たとえば、onActivityCreatedメソッドでgetActivityが実際のアクティビティがnullではない
Lukap

レイアウトにない問題、アプリはうまく機能しますが、なぜgetActivityに対してnullを取得するのですか?レンダリングされたフラグメントを含むすべての要素がここでは問題にならないようにレンダリングされます
Lukap

1
このメソッドを呼び出す必要があります:f1.asd(); フラグメントクラスでオーバーライドされるonActivityCreatedメソッド内。
Namrata Bagerwal 2017年

回答:


164

commit トランザクションをスケジュールします。つまり、すぐには発生しませんが、次にメインスレッドの準備ができたときに、メインスレッドでの作業としてスケジュールされます。

追加することをお勧めします

onAttach(Activity activity)

メソッドをFragment呼び出してブレークポイントを設定し、への呼び出しと比較していつ呼び出されるかを確認しますasd()asd()exit の呼び出しを行うメソッドの後に呼び出されることがわかります。onAttachここで、呼がFragment、その活性にし、この点から装着されているgetActivity()非ヌルが返される(NBもあるonDetach()コール)。


5
どうすれば問題を解決できるのかわかりませんでした。getActivity()がまだ準備ができていない場合、どのようにしてFragmentActivityオブジェクトの参照を取得できますか?
CeccoCQ 2011

2
@Vivekあなたが何を達成したいのか私にはよくわかりません。Fragmentでダイアログをすぐに表示する必要がある場合は、onCreateViewor onActivityCreatedメソッドなどで、作成時に必要なことを実行させます。質問の投稿でasd()を呼び出す必要がある理由を質問します。
PJL 2012年

3
onAttachは非推奨
abbasalim

6
onAttach(Activity mActivity)は減価償却されているようです。これに対する回避策
ashish.n '27

4
API 24が導入されましたcommitNow()
Nicolas

92

これを取り除くための最善の方法は、onAttachが呼び出されたときにアクティビティ参照を保持し、必要に応じてアクティビティ参照を使用することです。たとえば、

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

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

34
mActivity = null onDetach()を設定する必要がありますか?
Oliver Pearmain 2014

5
@OliverPearmain onDetach()で行う場合、利益はありません。onDestory()でそれを無効にする必要があります。さらに、それをWeakRefernceに保持する必要があります。
キリルポポフ2015

私は両方でそれを無効化していますonDestroy()し、onDetach()ためにonDestroy()呼び出されることが保証されていません。
Mohammed Ali

8
Activityそれを無効にしないと、リークしていonDestroy()ますか?
モハメッドアリ

2
developer.android.com/intl/zh-tw/guide/components/…によると、onCreateView()を呼び出す前にonAttach()が呼び出されます。しかし、onCreateView()でgetActivity()を呼び出している間も、NullPointerExceptionが発生します。どうしてそうなるのでしょうか?
Kimi Chiu

81

これgetActivity()は、フラグメントが削除された後に終了した別のスレッドを呼び出すと発生しました。典型的なケースは、HTTPリクエストが終了したときに(たとえば)を呼び出すことですgetActivity()(たとえば)。ToastonResponse

これを回避するには、フィールド名mActivityを定義して、の代わりに使用しますgetActivity()。このフィールドは、次のようにFragmentのonAttach()メソッドで初期化できます。

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

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

私のプロジェクトでは、通常、この機能を使用してすべてのフラグメントの基本クラスを定義しています。

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

ハッピーコーディング、


19
mActivity = nullを設定しますか?onDetach()?
Bharat Dodeja 2015

thucnguyen、単一のアクティビティアプリに対してmActivity staticを宣言してみませんか?
iamthevoid 2015年

Activity別のスレッドからアクセスする理由がまったくわかりません。とにかくそれで何かをすることはできず、トーストを見せることさえできません。したがって、最初にメインスレッドに作業を転送するか、アクティビティをまったく使用しないでください。
Dzmitry Lazerka

4
@BharatDodeja mActivity = null onDetachを設定する必要がありますか?わかった?
SLearner

5
これは、アクティビティをゼロにすることなくリークします。
Darek Deoniziak 2016

30

onAttachでのアクティビティへの参照を維持することを示唆する他の回答は、実際の問題に対する応急処置を示唆しているだけです。getActivityがnullを返す場合、フラグメントがアクティビティに関連付けられていないことを意味します。最も一般的には、これは、ローテーションまたはアクティビティが終了したためにアクティビティがなくなったが、フラグメントにまだ何らかの種類のコールバックリスナーが登録されている場合に発生します。アクティビティで何かをする必要があるが、アクティビティがなくなった場合にリスナーが呼び出されたとき、できることはあまりありません。あなたのコードではあなただけをチェックする必要がありますgetActivity() != nullそして、それがなければ、何もしません。なくなったアクティビティへの参照を保持している場合は、アクティビティがガベージコレクションされるのを防ぎます。実行しようとする可能性のあるUIの操作は、ユーザーには表示されません。コールバックリスナーで、UIに関連しない何かのコンテキストが必要になる状況を想像できます。そのような場合は、おそらくアプリケーションコンテキストを取得する方が理にかなっています。唯一の理由ですonAttachトリックが大きなメモリリークではないは、通常、コールバックリスナーの実行後は不要になり、フラグメント、そのすべてのビュー、およびアクティビティコンテキストと共にガベージコレクションできるためです。もし、あんたがsetRetainInstance(true) アクティビティフィールドも保持されるため、メモリリークの可能性が高くなりますが、ローテーション後は、現在のアクティビティではなく、前のアクティビティになる可能性があります。


1
これはまさに私の問題です。プロセスを実行するフラグメントがあります->次に広告が表示されます->そしてプロセスが続行します。一部のデバイスでは、広告から(リスナーを介して広告イベントに)戻った後、getActivity()はnullです。しかし、私は仕事を終えるために仕事の他の部分を続けなければなりません。これには解決策がないということですか?
Notbad

これがまさに私が直面していることです。一部の請求処理を実行するフラグメントにアクティビティインターフェイスがあります。支払いが完了した後、インターフェイスを使用して何かを実行したいのですが、インターフェイスがnullになっています。
フレディ

これは、このトピックに対する何百ものSOの質問に対する正しい一般的な回答のようです。
マヌエル、

ベストアンサー。Android on SOには、非常に多くの絆創膏ソリューションがあります。
maxbeaudoin

したがって、何らかの操作を実行したい場合は、getActivity()が使用可能になった後で実行できます(可能な場合)。
Sreekanth Karumanaghat

17

Android APIレベル23以降、onAttach(Activity activity)は非推奨になりました。onAttach(Context context)を使用する必要があります。http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

アクティビティはコンテキストであるため、単にコンテキストを確認できればアクティビティであり、必要に応じてキャストできます。

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

    Activity a;

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

}

使い方
ARR.s '26 / 07/26

10

PJLは正しいです。私は彼の提案を使用しており、これは私がやったことです:

  1. フラグメント用に定義されたグローバル変数:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. 実装された

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

3。メインスレッドで実行される場合、手順4でスレッドをブロックするため、getActivity()を呼び出す必要がある関数をスレッドでラップしました。onAttach()は呼び出されません。

    Thread processImage = new Thread(new Runnable() {

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

4。getActivity()を呼び出す必要がある関数では、これを使用します(getActivity()を呼び出す前に)

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

UIの更新がある場合は、UIスレッドで実行してください。ImgeViewを更新する必要があるので、次のようにしました。

image.post(new Runnable() {

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

7

commit()の後にコールバックが呼び出される順序:

  1. commit()の直後に手動で呼び出すメソッド
  2. onAttach()
  3. onCreateView()
  4. onActivityCreated()

ビューを含むいくつかの作業を行う必要があったため、onAttach()は機能しませんでした。クラッシュしました。そのため、commit()の直後に呼び出されるメソッド内にいくつかのパラメーターを設定していたコードの一部を移動し(1)、次にonCreateView()内のビューを処理するコードの別の部分を移動しました(3)。


3

私はOkHttpを使用していますが、この問題に直面しました。


最初の部分では、@ thucnguyenは正しい方向に進んでいました

これは、フラグメントが削除された後に終了した別のスレッドでgetActivity()を呼び出すと発生しました。典型的なケースは、HTTPリクエストが終了したときに(たとえば、onResponseで)getActivity()を呼び出すことです(Toastの場合など)。

アクティビティが閉じられた後でも、一部のHTTP呼び出しが実行されていました(HTTPリクエストが完了するまでに時間がかかるため)。次に、HttpCallbackいくつかのFragmentフィールドを更新nullしようとしgetActivity()たときに、をしようとしたときに例外が発生しました。

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

IMO解決策は、フラグメントが存在しなくなったときにコールバックが発生ないようにすることです(Okhttpだけではありません)。

修正:防止。

あなたが見ている場合はフラグメントのライフサイクル(詳細はこちらを)、あなたはそこだと気付くでしょうonAttach(Context context)し、onDetach()方法。これらは、Fragmentがアクティビティに属した後で、それぞれ停止する直前に呼び出されます。

つまり、onDetachメソッドでそれを制御することで、コールバックの発生を防ぐことができます。

@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

この関数をどこで呼び出しますか?のコンストラクタで呼び出すと、Fragmentが返されnullます。

getActivity()メソッドonCreateView()が実行されたときに呼び出すだけです。


1

以下のようにしてください。参考になると思います。

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

onAttach(Activityアクティビティ)にまだ問題がある人は、コンテキストに変わりました-

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

ほとんどの場合、コンテキストを保存するだけで十分です。たとえば、getResources()を実行する場合は、コンテキストから直接実行できます。コンテキストをアクティビティに追加する必要がある場合-

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

user1868713によって提案されたとおり。


0

onAttachを使用するか、onAttachをどこにも配置したくない場合は、メインのAppクラスにApplicationContextを返すメソッドを配置できます。

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

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

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

その後、次のように、プロジェクト全体のどこでも再利用できます。

App.getContext().getString(id)

これでうまくいかない場合はお知らせください。


0

別の良い解決策は、MVVMアーキテクチャでAndroidのLiveDataを使用することです。ViewModel内にLiveDataオブジェクトを定義し、それをフラグメントで観察します。LiveData値が変更されると、フラグメントがアクティブな状態である場合にのみオブザーバー(この場合はフラグメント)に通知します。そのため、次のことが保証されます。フラグメントがアクティブな状態の場合にのみ、UIを機能させ、アクティビティにアクセスします。これは、1つの利点です。 LiveDataに

もちろん、この質問が最初に出されたとき、LiveDataはありませんでした。私が見るように、この問題がまだあり、誰かに役立つかもしれないので、私はこの答えをここに残します。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.