Activity/Fragment/Viewの状態保存/データ復旧

6399 ワード

Activity/Fragmentのライフサイクルについて


まず余談ですが、金曜日のテストでバグを出して、次のFragmentにジャンプして、前のfragmentに戻って、前のtextviewに対する操作がなくなりました.
そこで私はまたViewのlifecircleを見てみると、Activityとはやはり違います.彼はbackstackから前のfragmentを取り出してonCreateViewを歩きますが、inflateviewとbutterknifer bind viewはonCreateViewでやっています(これは正しいです.onCreateViewはviewに戻ります.公式ドキュメントではこのviewでこのfragmentのroot layoutを返します.onCreateでは、コンポーネントを初期化するために必要です).つまりviewは再構築されているので、view状態はすべて初期状態になっています.
最終的に私はこの業務問題を解決しました:onCreateViewのupdateの現在のfragmentのviewの方法に判断を加えて、uidataの中で相応のデータがあれば、textviewを更新します.
ActivityとFragmentのライフサイクルの違いを感じています.
  • FragmentはonCreateView/onDestroyViewのペアが増えたのも、FragmentがActivityよりもViewの展示をしているからかもしれません.
  • FragmentにはonAttachとonDetachが多く、Activityへの接続を識別します.
  • start/stop(見える)、resume/pause(フロントにある)の2組が共有されており、activityには特殊なonRestartがあります.

  • また、onAttach(Activity activity)がapi 23以上でdeprecatedされていることに気づき、パラメータが変わりました:
    /* 
    * onAttach(Context) is not called on pre API 23 versions of Android and onAttach(Activity) is deprecated 
    * Use onAttachToContext instead 
    */  
       @TargetApi(23)  
       @Override  
       public void onAttach(Context context) {  
           super.onAttach(context);  
           onAttachToContext(context);  
       } 
    

    この新しい方法を上書きすると、gradleのtarget apiが23未満の場合、onAttachは呼び出されません(先週外部SDKにアクセスしたとき、彼らのsoはtargetApi 23以上をサポートしていなかったため、私たちのappはtargetApi 22に変更されました.これがリスクです).正しい方法はtargetApiをアップグレードするか、2つのonAttachを同時に書くことです.

    Fragmentの状態保存について


    私たちのグループのAppではUIdataの案を使っていますが、巧みだと思います.FragmentのonAttach()メソッド(また、onCreate()メソッドでもgetActivityをmActivityに付与)でmActivityのリカバリとmUIDataのリカバリを行いました:
        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            this.mActivity = (CPActivity) activity;
            this.mUIData = mActivity.mUIData;
        }
    

    これにより、getActivityによるonDetachによる空のポインタを回避できます.onDetachの後、getActivityはnullですから.しかしFragmentオブジェクトはGCによって回収されていないためmActivityは回収されていない.
    また、ViewPager+Fragmentのようなデザインでは、viewpagerのデフォルトで3ページ目に滑ると、1ページ目のFragmentがonDestroyViewになり、このときに1ページ目に戻ってくると、多くのものが再構築されます.このような状況は状況によって異なり、リアルタイム性の高いappであれば、このようにするのは間違いない.リアルタイム性の低いappであれば、onCreateViewでネットワークデータが空であるか、rootViewが空であるか、空でなければネットワークを要求せず、リソース消費を避けることができます.

    Activityの状態保存


    私たちのActivityにはpublicのmUIdataがあり、onCreate()、onRestoreInstanceState()、onRestoreInstanceState()の場合、savedInstanceStateから保存/取り出します.
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            //  
            if (savedInstanceState == null) {
    //initUIData , 
                mUIData = initUIData();
            } else {
    // , set classLoader, , classLoader        
                savedInstanceState.setClassLoader(getClass().getClassLoader());
                mUIData = (UIData) savedInstanceState.getSerializable(UIDATA);
            }
            super.onCreate(savedInstanceState);
    
            imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        }
    

    ActivityのFragment間のデータ通信


    私はこの文章を見て、Handler、放送/EventBus、インタフェース、setArgumentなどの方法について言及しました.その中の最後のsetArgument方式はFragmentのデザイナーたちの初志で、私も前に簡単に分析したことがありますが、その利点はActivityの再構築時にデータを復元できることです.そのため、Fragmentは単例の使用をお勧めしません.それに比べて、私たちのグループのUIdata案はいいと思います.

    Viewの状態保存


    Activityだけでなく、ViewというクラスにもonSaveInstanceStateonRestoreInstanceState(android.os.Parcelable)というstateを保存する方法があります.複雑なview、例えばユーザーが埋め尽くしたフォームについて、ユーザーが画面を回転したら、データがなくなってはいけません.
    Viewには、Base class for derived classes that want to save and restore their own onSaveInstancesState()と解釈されるクラスがあります.私たちはカスタマイズしてViewのstateを保存します.
    私たちのJDRViewでは、保存とリカバリ時にuidataに値を割り当てるために継承されています.parcelにuidataを追加してリカバリする必要があります.
        public static class SavedState extends BaseSavedState {
            /**
             *  
             */
            private UIData mUIData;
    
            public SavedState(Parcelable superState) {
                super(superState);
            }
    
            private SavedState(Parcel in) {
                super(in);
                mUIData = (UIData) in.readSerializable();
            }
    
            @Override
            public void writeToParcel(Parcel out, int flags) {
                super.writeToParcel(out, flags);
                out.writeSerializable(mUIData);
            }
    
            public static final Creator CREATOR = new Creator() {
    
                public SavedState createFromParcel(Parcel in) {
                    return new SavedState(in);
                }
    
                public SavedState[] newArray(int size) {
                    return new SavedState[size];
                }
            };
        }
    

    JDRViewはどうやってデータを復元したのか思い出しました.JDRViewのコンストラクション関数JDRView()->init(){mUIdata=initUIIData();initUI();onRestoreSavedInstaceでupdateUI()を呼び出します.
    Finance Viewを例にとると、
        @Override
        public UIData initUIData() {
            InvestmentListData investmentListData = mStorageUtil.get(InvestmentListData.class);
            if (investmentListData == null || investmentListData.fundCardInfo == null) {
                investmentListData = new InvestmentListData();
                investmentListData.fundCardInfo = createDefaultData();
            }
            return investmentListData;
        }
    

    私たちはmStorageUtilを使っています.SerializableのデータをJsonに変換して保存したSharedPreferencesに使います.コンストラクション関数の初期化とonRestoreSavedInstanceでは、mUIdataに値が割り当てられ、initUi/updateUiになります.ディスクからキャッシュデータをリカバリし、ビューを再構築したときにデータをリカバリするために使用されます.
    MainSequenceFragment:
        protected void updateCardListUI(CardSequenceList cardSequenceList) {
            if (cardSequenceList == null) {
                return;
            }
            List mCardList = cardSequenceList.tabStructureList;
            if (needDraw(mCardList)) {
                mCardView = CardViewManager.generateCardView(mActivity, mCardList);
                addCardViewLayout();
            }
            for (int i = 0; i < mCardView.size(); i++) {
                View cardView = mCardView.get(i);
                if (cardView instanceof JDRView) {
                    //loadData cardId server ,onSuccess updateUI。
                    ((JDRView) cardView).loadData(null);
                }
            }
        }
    

    cardIdはキャッシュ時に役立つ以外に、何の役に立ちますか.昔はページ名が伝わっていたようですが、今はcardIdに変更されました.
    ref: http://www.jianshu.com/p/662c46cd3b5f http://blog.csdn.net/u012702547/article/details/47151001 http://www.bubuko.com/infodetail-828889.html