1

FragmentActivity私は s である 2 つのタブを持っていますListFragment。それぞれListFragmentにコールバックがあります。

:気づいた場合、この質問のほとんどは、私自身の質問Callback after orientation change become nullから再ハッシュされていますが、これは回転とは関係がないため別の質問ですが、私の希望は私の実際の問題を理解することですコールバック。)

コールバックの例

http://developer.android.com/training/basics/fragments/communicating.htmlで説明されているように、コールバックはonAttach(...)メソッド内で関連付けられています。

OnTab1Listener mTab1Callback;

public interface OnTab1Listener {
    public void onTab1Update();
}

@Override
public void onAttach(Activity activity) {
    Log.d(TAG, "onAttach");
    super.onAttach(activity);

    try {
        mTab1Callback = (OnTab1Listener)activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement OnTab1Listener");
    }
}

後で、FragmentActivity正常に動作するこのコールバックで と通信します。

アプリケーションがしばらくの間バックグラウンドに送信された後、アプリケーションを前面に移動して、コールバックを使用する何かをトリガーすると、コールバックがnullになり、アプリがクラッシュします。バックグラウンドでシステムがガベージコレクションを実行すると、これが発生すると思います。

通常の実行中に正常に動作する呼び出しの例を次に示します。

public void toggleEnabled(View v) {
    Log.d(TAG, "toggleEnabled");

    // null pointer here
    mTab1Callback.onTab1Update();
}

質問

  1. ガベージ コレクションが行われている場合、 mTab1Callbackガベージ コレクションが行われ、復元されないのはなぜですか? 私の印象は、参照の一部がガベージ コレクションされている場合、onAttach(...)Activityを含め、ライフサイクルとFragmentライフサイクルが再起動するというものでした。
  2. これはどのように適切に修正されていますか?

サンプルアプリ

この動作を示すサンプル アプリケーションを作成しました。完全なソースコードはこちらhttp://www.2shared.com/file/kkgFejUq/broken_example.html

アプリのスタック トレースの例

FATAL EXCEPTION: main
java.lang.IllegalStateException: Could not execute method of the activity
at android.view.View$1.onClick(View.java:3591)
at android.view.View.performClick(View.java:4084)
at android.widget.CompoundButton.performClick(CompoundButton.java:100)
at android.view.View$PerformClick.run(View.java:16966)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at android.view.View$1.onClick(View.java:3586)
... 12 more
Caused by: java.lang.NullPointerException
at com.example.myapp.Tab2.toggleEnabled(Tab1.java:87)
at com.example.myapp.MainActivity.onClick(MainActivity.java:50)
... 15 more

ここにTab1.java:87があります

mTab1Callback.onTab1Update();

これは、アプリをバックグラウンドに移動させ、ゲームを少しプレイしたり、音楽を再生したりするなど、他のことをしばらく行ってフォアグラウンドに戻し、チェックボックスのいずれかをクリックすると、null ポインターになります。

必要に応じて、アプリのアイコンをランチャーに配置してそこから起動し、他のアプリをしばらく実行した後、フォアグラウンドに移動します。これはクラッシュするときです。

また、エミュレーターでクラッシュさせることはできません。実際のハードウェアのみです。電話がコンピューターに接続されている場合はクラッシュする可能性が低く、テスト中にプラグを抜いた場合は簡単にクラッシュするようです.

TabsAdapterはこれと関係がありますか?

また、これはファイルに含まれているSherlockActionBarを使用します。MyAppのライブラリとして使用する必要があります。

アップデート

getActivity()nullであることがわかり始めました。ローテーションまたはバックグラウンドで長期間実行した後、null である唯一の場所は、コールバックを起動するtoggleEnabled()関数内です。

これは、原因がもはや存在しないMainActivitya を参照していることを示しているようです。Fragment

スペースのコードを組み合わせると、これは基本的に、 in をクリックした後に toggleEnabled() 関数を参照する方法です。(完全なコードについては、添付の例を参照してください)CheckBoxTab1

public void onClick(View v) {
     Tab1 tab = (Tab1)mTabsAdapter.getItem(0);
     tab.toggleEnabled(v);
}

だから私はgetItem(...)TabsAdapterに従うために掘り下げました。ここにあるもの

プライベート ArrayList mFragments = new ArrayList(); // タブなどを設定するコード @Override public Fragment getItem(int position) { Fragment frag = mFragments.get(position); if (frag.getActivity() == null) Log.d(TAG, "getItem: Activity is null");

return mFragments.get(position);

}

回転前に何が起こるか、getActivity()null ではありません。回転すると、getActivity()nullになります。これは、私が信じてFragmentいる適切なものを再現していないことを示しているようです。Contextしかし、これは、この種のことに関する私の知識が終わるところです。

静的にすることはできmFragmentsますが、それは正しい解決策ではないように感じます。

Fragmentが適切に取り付けられていない理由を知っている人はいますActivityか?

4

2 に答える 2

1

フラグメントに適切にアタッチされたアクティビティがない理由を誰かが知っていますか?

それはViewPager仕組みだからです。最初に をセットアップするとViewPager(2 つのフラグメントのインスタンス化とともに)、getItem()メソッドが呼び出され、正しいFragment. 電話を回転させるActivityと、その中のすべてFragmentsが破壊され、再び作成されます。この時点で、 はそのViewPager状態を内部的に再作成しようとしFragments、 を前の状態と一致させ、独自の を構築しFragmentsます。Fragmentsメソッドを使用して追加した独自のaddTabメソッドは使用されません。これは、電話を回転させた後、getItem()メソッドが最初の に対して呼び出されないためFragmentsです。問題は、現在のコードでgetItemメソッドを呼び出してFragmentsViewPagerリストからを取得しますFragments。これらのフラグメントは に関連付けられていない単純なインスタンスであるActivityため、メソッドで設定されたコールバックonAttach()は ですnull

主な問題は、その位置の を取得するためgetItemに a を呼び出すことができないことです。これを行う方法については、stackoverflow で多くの質問があります。以下のメソッドを作成して、フラグメントリストに常に正しいフラグメントが含まれるようにしました (たとえば、設定したフラグメントをドロップすることにより)。現在のコードは同じままで、以下を追加するだけです:FragmentPagerAdapterFragmentViewPager

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment created = (Fragment) super
            .instantiateItem(container, position);
    if (position < mFragments.size()) {
        Fragment fromList = mFragments.get(position);           
        if (!created.equals(fromList)) {
            mFragments.set(position, created);
        }
    } else {
        int difference = position - mFragments.size();
        if (difference == 0) {
            mFragments.add(created);                
        } else {
            for (int i = 0; i < difference - 1; i++) {
                mFragments.add(null);
            }
            mFragments.add(created);
        }
    }
    return created;
}

TabsAdapterFragmentsViewPager 独自のフラグメントを優先して手動で作成されたものはすべて削除する必要があります。それが機能するかどうかを確認してください。

また:

  • 使用しないでくださいsetRetainInstance(true)
  • への参照を持つ静的フィールドを使用する場合は、Contextメモリ リークを避けるために注意してください (たとえば、MyObjectAdapter静的フィールドではなくインスタンス フィールドにする必要があります)。
于 2012-12-17T19:18:43.290 に答える
1

OnAttach は onCreate と同様の方法で呼び出されるように見えます。つまり、作成時に 1 回だけ呼び出されるため、バックグラウンドから再開すると、このメソッドはスキップされます。

于 2012-12-16T22:38:54.750 に答える