1. 程式人生 > >Martin.Mu `s Special Column

Martin.Mu `s Special Column

前言

最近在研究Glide的時候,發現Glide的with方法的主要目的是想將圖片的加載於傳入的Activity/Fragment的生命週期進行繫結,發現他的思路很巧妙,是通過新增Fragment的方法進行圖片下載的管理,在這裡我將Glide中使用的一些方法和我們常用的Fragment的Api進行學習記錄,針對他的replace,attach,detach,add,show,hide方法做了生命週期的驗證,以及一些相關的原始碼片段解析,希望對各位也有幫助。

準備工作

專案程式碼

結構圖

在這裡插入圖片描述 這張類結構圖是我看原始碼的時候畫出來的,方便我對原始碼的理解,畫得不好的地方歡迎指出。

由於版本的原始碼會經常改寫,目前8.0的原始碼版本中,Fragment的原始碼已經有改動,本人看了下,編寫過程中沒有6.0便於理解,所以該專案的原始碼解析我們就指定一個Android 6.0的原始碼來進行分析。

常用方法解析

Replace用例和解析

 public class ReplaceFragmentsActivity extends AppCompatActivity {

    Fragment currentFragment;
    Fragment1 fragment1 = new Fragment1();
    Fragment2 fragment2 = new Fragment2();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(
savedInstanceState); setContentView(R.layout.activity_main); RadioGroup radioGroup = (RadioGroup) findViewById(R.id.bottom); radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group,
int checkedId) { switch (checkedId) { case R.id.bottom_radio1: ToastUtil.showToast(getApplicationContext(),"radio1"); currentFragment = fragment1; break; case R.id.bottom_radio2: ToastUtil.showToast(getApplicationContext(),"radio2"); currentFragment = fragment2; break; } getSupportFragmentManager().beginTransaction() .replace(R.id.fl_main_content, currentFragment) .commit(); } }); } }

可以看見他的replace方法很簡單一行程式碼替換就行,下面是他的生命週期結果圖: fragment_replace 看到上面的結果,我們得出以下結論: 新Fragment作為替換目標繫結到當前Activity時,原繫結的Fragment被銷燬,新的Fragment建立後被繫結到當前Activity。

原始碼解析

BackStackRecord繼承FragmentTransaction類,FragmentTransaction裡面定義了很多Fragment管理的API,我們來看下原始碼:

/**
 * @hide Entry of an operation on the fragment back stack.
 */
final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry, Runnable {
        
   static final class Op {
        Op next;
        Op prev;
        int cmd;
        Fragment fragment;
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;
        ArrayList<Fragment> removed;
    }
...

	public FragmentTransaction replace(int containerViewId, Fragment fragment) {
        return replace(containerViewId, fragment, null);
    }

    public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
        if (containerViewId == 0) {
            throw new IllegalArgumentException("Must use non-zero containerViewId");
        }

        doAddOp(containerViewId, fragment, tag, OP_REPLACE);
        return this;
    }
    
	Fragment f = op.fragment;
	int containerId = f.mContainerId;
	if (mManager.mAdded != null) {
	    for (int i=0; i<mManager.mAdded.size(); i++) {
	        Fragment old = mManager.mAdded.get(i);
	        if (FragmentManagerImpl.DEBUG) Log.v(TAG,
	                "OP_REPLACE: adding=" + f + " old=" + old);
	        if (old.mContainerId == containerId) {
	            if (old == f) {
	                op.fragment = f = null;
	            } else {
	                if (op.removed == null) {
	                    op.removed = new ArrayList<Fragment>();
	                }
	                op.removed.add(old);
	                old.mNextAnim = exitAnim;
	                if (mAddToBackStack) {
	                    old.mBackStackNesting += 1;
	                    if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
	                            + old + " to " + old.mBackStackNesting);
	                }
	                mManager.removeFragment(old, transition, transitionStyle);
	            }
	        }
	    }
	}
	
	private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
        fragment.mFragmentManager = mManager;

        if (tag != null) {
            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
                throw new IllegalStateException("Can't change tag of fragment "
                        + fragment + ": was " + fragment.mTag
                        + " now " + tag);
            }
            fragment.mTag = tag;
        }

        if (containerViewId != 0) {
            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
                throw new IllegalStateException("Can't change container ID of fragment "
                        + fragment + ": was " + fragment.mFragmentId
                        + " now " + containerViewId);
            }
            fragment.mContainerId = fragment.mFragmentId = containerViewId;
        }

        Op op = new Op();
        op.cmd = opcmd;
        op.fragment = fragment;
        addOp(op);
    }
    
    void addOp(Op op) {
        if (mHead == null) {
            mHead = mTail = op;
        } else {
            op.prev = mTail;
            mTail.next = op;
            mTail = op;
        }
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
        mNumOp++;
    }
    
...
}

可以看見我們的Op是一個內部類,結構的話是一個雙向連結串列,雙向連結串列的特性就是方便查詢,可以看見我們的Fragment的管理核心就是雙向連結串列,資料就是fragment,使用了命令模式int cmd來進行相關的操作,因為類實現了Runable方法,由於程式碼邏輯較長,一些邏輯程式碼我就不貼了,就是講op加入到連結串列中,然後遍歷加入的op呼叫當前類的加入到佇列執行我們的任務,核心的方法就是run,現在我們來看看run方法:

public void run() {
        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);

        if (mAddToBackStack) {
            if (mIndex < 0) {
                throw new IllegalStateException("addToBackStack() called after commit()");
            }
        }

        bumpBackStackNesting(1);

        TransitionState state = null;
        SparseArray<Fragment> firstOutFragments = null;
        SparseArray<Fragment> lastInFragments = null;
        if (SUPPORTS_TRANSITIONS) {
            firstOutFragments = new SparseArray<Fragment>();
            lastInFragments = new SparseArray<Fragment>();

            calculateFragments(firstOutFragments, lastInFragments);

            state = beginTransition(firstOutFragments, lastInFragments, false);
        }

        int transitionStyle = state != null ? 0 : mTransitionStyle;
        int transition = state != null ? 0 : mTransition;
        Op op = mHead;
        while (op != null) {
            int enterAnim = state != null ? 0 : op.enterAnim;
            int exitAnim = state != null ? 0 : op.exitAnim;
            switch (op.cmd) {
                case OP_ADD: {
                    Fragment f = op.fragment;
                    f.mNextAnim = enterAnim;
                    mManager.addFragment(f, false);
                } break;
                case OP_REPLACE: {
                    Fragment f = op.fragment;
                    int containerId = f.mContainerId;
                    if (mManager.mAdded != null) {
                        for (int i=0; i<mManager.mAdded.size(); i++) {
                            Fragment old = mManager.mAdded.get(i);
                            if (FragmentManagerImpl.DEBUG) Log.v(TAG,
                                    "OP_REPLACE: adding=" + f + " old=" + old);
                            if (old.mContainerId == containerId) {
                                if (old == f) {
                                    op.fragment = f = null;
                                } else {
                                    if (op.removed == null) {
                                        op.removed = new ArrayList<Fragment>();
                                    }
                                    op.removed.add(old);
                                    old.mNextAnim = exitAnim;
                                    if (mAddToBackStack) {
                                        old.mBackStackNesting += 1;
                                        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
                                                + old + " to " + old.mBackStackNesting);
                                    }
                                    mManager.removeFragment(old, transition, transitionStyle);
                                }
                            }
                        }
                    }
                    if (f != null) {
                        f.mNextAnim = enterAnim;
                        mManager.addFragment(f, false);
                    }
                } break;
                default: {
                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                }
            }

            op = op.next;
        }

        mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);

        if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }
    }
}

final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {
...
public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
        final boolean inactive = !fragment.isInBackStack();
        if (!fragment.mDetached || inactive) {
            if (mAdded != null) {
                mAdded.remove(fragment);
            }
            if (fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            fragment.mAdded = false;
            fragment.mRemoving = true;
            moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
                    transition, transitionStyle, false);
        }
    }
    
     void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
        // Fragments that are not currently added will sit in the onCreate() state.
        if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
            newState = Fragment.CREATED;
        }
        if (f.mRemoving && newState > f.mState) {
            // While removing a fragment, we can't change it to a higher state.
            newState = f.mState;
        }
        // Defer start if requested; don't allow it to move to STARTED or higher
        // if it's not already started.
        if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
            newState = Fragment.STOPPED;
        }
        if (f.mState < newState) {
            // For fragments that are created from a layout, when restoring from
            // state we don't want to allow them to be created until they are
            // being reloaded from the layout.
            if (f.mFromLayout && !f.mInLayout) {
                return;
            }  
            if (f.mAnimatingAway != null) {
                // The fragment is currently being animated...  but!  Now we
                // want to move our state back up.  Give up on waiting for the
                // animation, move to whatever the final state should be once
                // the animation is done, and then we can proceed from there.
                f.mAnimatingAway = null;
                moveToState(f, f.mStateAfterAnimating, 0, 0, true);
            }
            switch (f.mState) {
                case Fragment.INITIALIZING:
                    if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
                    if (f.mSavedFragmentState != null) {
                        f.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
                        f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
                                FragmentManagerImpl.VIEW_STATE_TAG);
                        f.mTarget = getFragment(f.mSavedFragmentState,
                                FragmentManagerImpl.TARGET_STATE_TAG);
                        if (f.mTarget != null) {
                            f.mTargetRequestCode = f.mSavedFragmentState.getInt(
                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
                        }
                        f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
                                FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
                        if (!f.mUserVisibleHint) {
                            f.mDeferStart = true;
                            if (newState > Fragment.STOPPED) {
                                newState = Fragment.STOPPED;
                            }
                        }
                    }
                    f.mHost = mHost;
                    f.mParentFragment = mParent;
                    f.mFragmentManager = mParent != null
                            ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
                    f.mCalled = false;
                    f.onAttach(mHost.getContext());
                    if (!f.mCalled) {
                        throw new SuperNotCalledException("Fragment " + f
                                + " did not call through to super.onAttach()");
                    }
                    if (f.mParentFragment == null) {
                        mHost.onAttachFragment(f);
                    }

                    if (!f.mRetaining) {
                        f.performCreate(f.mSavedFragmentState);
                    }
                    f.mRetaining = false;
                    if (f.mFromLayout) {
                        // For fragments that are part of the content view
                        // layout, we need to instantiate the view immediately
                        // and the inflater will take care of adding it.
                        f.mView = f.performCreateView(f.getLayoutInflater(
                                f.mSavedFragmentState), null, f.mSavedFragmentState);
                        if (f.mView != null) {
                            f.mInnerView = f.mView;
                            if (Build.VERSION.SDK_INT >= 11) {
                                ViewCompat.setSaveFromParentEnabled(f.mView, false);
                            } else {
                                f.mView = NoSaveStateFrameLayout.wrap(f.mView);
                            }
                            if (f
            
           

相關推薦

Martin.Mu `s Special Column

前言 最近在研究Glide的時候,發現Glide的with方法的主要目的是想將圖片的加載於傳入的Activity/Fragment的生命週期進行繫結,發現他的思路很巧妙,是通過新增Fragment的方法進行圖片下載的管理,在這裡我將Glide中使用的一些方法和我

SinPing's Special column

前述 本來這邊博文是在上月釋出的,由於儲存圖片的七牛雲圖床域名過期,在申請域名和備案過程中耗了時間。後面的博文依然每月更新一篇。 1、介紹 執行引擎說白點就是執行程式碼,在瞭解虛擬機器如何執行程式碼之前,來看看方法執行的過程,如下圖執行簡單的類所示: 這裡涉及

Jackery's Special Column

        影象檢測是影象分割,影象識別的基礎,也是不可缺少的關鍵。在視覺計算理論框架中,抽取二維影象的邊緣、角點、紋理等基本特徵,是整個框架的第一步;本文章對Harris角點演算法做了比較詳細的理論介紹以及相關實現。 Part One:角點型別介紹 在現實世界

Tyrion Lannister's Special column

c:\program files (x86)\microsoft sdks\windows\v7.0a\include\ws2def.h(91): warning C4005: “AF_IPX”: 巨集重定義 1>          c:\program files (x86)\microsoft sd

【Jackery's Special Column 】Today is difficult,tomorrow is more difficult,but the day after tomorrow is beautiful,Keep Fight !

Today is difficult,tomorrow is more difficult,but the day after tomorrow is beautiful,Keep Fight !...

Vinco's special column

基於視訊的未來網購     雙十一“光棍節”即將到來,大家不會忘記"淘寶網2012光棍節創下一天交易總額191億的紀錄 驚動外媒"的報道。 有網購經歷的小夥伴們都知道,目前國內的網購網站如淘寶京東等均是基於平面文字圖片的網頁展示自己的產品。    這是一個基於WEB的應由於基於國內流行的一些C/S架構下

檢視檢視報錯ERROR 1356 (HY000): View 'bdi_gj1121.y' references invalid table(s) or column(s) or function

問題說明: 業務說匯入的新資料庫有幾個檢視查不了,報錯: mysql> select count(*) from bdi_gj1121.beaf_company;ERROR 1356 (HY000): View 'bdi_gj1121.beaf_company' references

Bonnie Learning Summary --- Special column

 做專案的時候我們肯定接觸過很多jar包,那麼jar包是什麼呢?筆者瞭解到jar是java archive file 的簡寫。從名字就可以知道,它的應用與Java息息相關。下面就詳細介紹如何自己生成jar包,這樣我們管理我們自己的程式碼(尤其是一些比較重要而且不會或者很少有修改的程式碼)。  安裝好JDK之

Martin Fowler's Bliki 中文版

原文:SpecificationByExample(譯註1)    敏捷        2006年6月16日            Bliki 索引譯註1:以例為規,“例”即測試涉及到的例子,這裡主要是指驗收測試(Acceptance Testing);“規”即軟體規

Martin Liu's Blog--Focus on Oracle

上面詳細演示瞭如何進入linux的修復模式,其實很多情況下,linux無法啟動時,都可以通過這個方式登入系統進行修復和更改操作。 下面是恢復/etc/fstab檔案的詳細過程: 首先檢視一下系統分割槽情況,如下所示: sh-3.1# fdisk -l Disk /dev/sda: 42.9 GB, 42949

關於在使用sparksql寫程序是報錯以及解決方案:org.apache.spark.sql.AnalysisException: Duplicate column(s): "name" found, cannot save to file.

文件加載 mod 但是 路徑 win 錯誤 寫入 技術分享 over 說明:   spark --version : 2.2.0   我有兩個json文件,分別是emp和dept: emp內容如下: {"name": "zhangsan", "age": 26, "dep

Dissecting the Disruptor: What’s so special about a ring buffer?

Recently we open sourced the LMAX Disruptor, the key to what makes our exchange so fast.  Why did we open source it?  Well, we’ve realised that co

Ask HN: Employer Merged with a Special Purpose Acquisition Company. What's Next?

As mentioned in the title, my employer just merged with a Special Purpose Acquisition Company (SPAC)[0]. These types of mergers seem like they're new so I

mybatis錯誤:Operand should contain 1 column(s)

執行mybatis時報了org.apache.ibatis.exceptions.PersistenceException: ### Error querying database.  Cause: java.sql.SQLException: Operand should

mybatis報錯:Cause: java.sql.SQLException: Operand should contain 1 column(s)\n;

mybatis配置: <select id="queryDubboConfig" parameterType="map" resultMap="DubboConfigDO">

SQL查詢欄位新增括號報錯:Operand should contain 1 column(s)

select vid_id, vid_name, vid_leadrole, vid_desc, vid_director, vid_date,  vid_area, vid_pic, vid_score, vid_info, vinfo.cid, vid_serialsta

[解決]scrapy操作mysql class 'pymysql.err.InternalError'(1241, 'Operand should contain 1 column(s)

1241, ‘Operand should contain 1 column 我是在吧資料插入到MySQL的時候出現這個錯誤的,插入的語句,程式碼都是對的 還是報錯,所以願意只能是在某個資料出了問題

錯誤:Operand should contain 1 column(s)

錯誤 MyBatis Cause: java.sql.SQLException: Operand should contain 1 column(s) 原因 xxmapper.xml 元素個數不匹配(認

Welcome to tikeyc's column

蘋果靜止熱更新,可惜我的是企業APP...(當然有些熱更新已經可以通過蘋果稽核了,比如JSPatch) 最近公司要新增熱修復BUG,其實早之前本人就有簡單實現過,剛好契合公司需求,在此總結一下iOS熱更新實現方式 這個是我根據JSPatch寫的一個Demo:https://github.com/tike

spring-data-jpa使用聯合主鍵後出現operand should contain 1 column(s)

最近做專案遇到個比較坑的地方,今天有空記錄下來,希望可以為遇到同樣情況的朋友提供一個方法。   因為業務邏輯上的問題,專案寫到前幾天突然發現要重新設計資料庫某個表的主鍵。(在團隊同學的建議下),採用了之前一直沒機會接觸過的聯合主鍵。   專案用的是mySql,資料表修改成