1. 程式人生 > >OSG動畫庫Animation解析(三)

OSG動畫庫Animation解析(三)

通過之前的兩篇文章,基本上對osgAnimation整個動畫的實現體系有了一個比較完整的介紹,本文主要介紹動畫中其他的一些內容,主要包括權、優先順序、以及動畫變換中的順序等內容。

1. 動畫的權重(weight)

假設對一輛勻速執行中的汽車施加兩股力量,單獨作用時,其中一股力量令汽車以60公里每小時向東北方向移動,另一股力量以100公里每小時向西北方向運動,如果希望汽車的方向修正為正北方向執行,需要向西北方向施加一個權重小於1.0的權重,讓它與東北方向的力量持平,這就是運動疊加和加權的意義。這個過程有點類似於向量的加法運算,遵循平行四邊形規則,權重類似於對向量乘上一個標量。

1.1 權重的設定

在osgAnimation中,和權重相關的類應該是:
1. AnimationManagerBase及其派生類
2. Animation
3. Channel
4. Target
這其中Channel和Target沒有設定權重的介面,但是在它們的內部需要使用權重這個引數參與運算,換言之它們權重是Animation設定給它們的。Animation可以通過設定函式void Animation::setWeight (float weight)設定動畫的權重,動畫管理類BasicAnimationManager使用void playAnimation (Animation* pAnimation, int priority = 0, float weight = 1.0);

指定動畫的權重。假設使用BasicAnimationManager來編寫動畫程式碼,那麼權重設定的傳遞關係是:

WeightDirection

也就是說playAnimation中指定的權重會設定給animation,animation遍歷它管理的所有Channels,並把這個權重設定給這些Channels【也就是說同一個Animation中所有頻道的權重都是一樣的】,這些Channels把權重設定給它管理的各自的Target物件,Target物件使用這個設定的權重進行運算,得到最終的結果。

1.2 單動畫單頻道

在之前編寫打RotateCallback的示例中,將一個頻道新增到了一個動畫之中,如果設定權重看是否會對結果產生影響。程式碼如下:

int main(int argc, char **argv)
{
    osgViewer::Viewer viewer;
    osg::Group* grp = new osg::Group;

    osg::MatrixTransform *mt = new osg::MatrixTransform;

    osgAnimation::UpdateMatrixTransform *umt1 = new osgAnimation::UpdateMatrixTransform;
    umt1->setName("move1UpdatedateCallback");
    umt1->getStackedTransforms().push_back(new osgAnimation::StackedTranslateElement("move1"));

    osgAnimation::Vec3LinearChannel *channel1 = new osgAnimation::Vec3LinearChannel();
    channel1->setTargetName("move1UpdatedateCallback");
    channel1->setName("move1");
    channel1->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(0, osg::Vec3(0, 0, 0)));
    channel1->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(2, osg::Vec3(5, 0, 0)));

    osgAnimation::Animation *anim1 = new osgAnimation::Animation();
    anim1->addChannel(channel1);

    osgAnimation::BasicAnimationManager *bam = new osgAnimation::BasicAnimationManager();
    bam->registerAnimation(anim1);
    bam->playAnimation(anim1, 10, 0.5);

    osg::Node* cowNode = osgDB::readNodeFile("cow.osg");
    grp->addChild(mt);
    mt->addChild(cowNode);
    grp->addUpdateCallback(bam);

    mt->addUpdateCallback(umt1);

    viewer.setSceneData(grp);
    viewer.setUpViewInWindow(300, 300, 800, 600);
    return (viewer.run());
}

使用playAnimation設定了當前的動畫權重是0.5, 優先順序是10(預設引數weight=0.5, priority=0),通過1.1中對權重的設定,可以最終得到
Channel的權重程式碼部分:

        virtual void update(double time, float weight, int priority)
        {
            // skip if weight == 0
            if (weight < 1e-4)
                return;
            typename SamplerType::UsingType value;
            _sampler->getValueAt(time, value);
            _target->update(weight, value, priority);
        }

如果設定了權重小於(0.00001,也就是近似等於0的情況),那麼動畫是不會起作用的。設定其他值,程式碼會進入到Target使用權重計算的程式碼中:

        void update(float weight, const T& val, int priority)
        {
            if (_weight || _priorityWeight)
            {
                if (_lastPriority != priority)
                {
                    _weight += _priorityWeight * (1.0 - _weight);
                    _priorityWeight = 0;
                    _lastPriority = priority;
                }

                _priorityWeight += weight;
                float t = (1.0 - _weight) * weight / _priorityWeight;
                lerp(t, _target, val);
            }
            else
            {
                _priorityWeight = weight;
                _lastPriority = priority;
                _target = val;
            }
        }

先看看Target類儲存的和優先順序以及權重相關的變數

    class  Target : public osg::Referenced
    {
    public:
        Target(): _weight(0), _priorityWeight(0), _lastPriority(0) {}
        virtual ~Target() {}
        void reset() { _weight = 0; _priorityWeight = 0; }
        int getCount() const { return referenceCount(); }
        float getWeight() const { return _weight; }
    protected:
        float _weight;
        float _priorityWeight;
        int _lastPriority;
    };

這三個變數_weight, _priorityWeight, _lastPriority的含義是:
_weight: 當前的已經計算的權重,如果把整個動畫管理器管理的動畫按照優先順序進行排序,那麼當優先順序進行切換時,_weight會記錄之前所有優先順序動畫所消耗的權重(如果把整個權重看作是1)
_priorityWeight:指的是遍歷某一個優先順序時,它下面所有頻道的權重之和。當優先順序切換時,這個值為重設為0,並重新開始計算。(這也是為什麼名字是priorityWeight,因為它只記錄某一priority下面的權重之和)
_lastPriority是上一次的優先順序(通過記錄上一次優先順序,可以知道什麼時候優先順序發生了切換)
這三個變數預設情況下都是0,通過reset函式的呼叫可以將_weight和_priorityWeight設定為0.
當單動畫單頻道的時候,BasicAnimationManager的更新程式碼如下:


void BasicAnimationManager::update (double time)
{
    ...
    for (TargetSet::iterator it = _targets.begin(); it != _targets.end(); ++it)
        (*it).get()->reset();
    for( AnimationLayers::reverse_iterator iterAnim = _animationsPlaying.rbegin(); iterAnim != _animationsPlaying.rend(); ++iterAnim )
    {
        // update all animation
        int priority = iterAnim->first;
        AnimationList& list = iterAnim->second;
        for (unsigned int i = 0; i < list.size(); i++)
        {
            list[i]->update(time, priority)
        }
    }
    ...
}

整個遍歷的迴圈其實只遍歷了一次,因為只存在一個優先順序為0的動畫,並且動畫中僅有一個Channel,這段迴圈只執行一次,注意在執行update之前,執行了target的reset操作,將target中的_weight和_priorityWeight設定為0,導致Target在執行update時,僅僅返回Channel中插值好的值,

            else
            {
                _priorityWeight = weight;
                _lastPriority = priority;
                _target = val;
            }

並沒有權重的參與計算。
也就是說單動畫單頻道的情況下:
1. 設定的權重並沒有什麼作用,但是不要設定為0(確切的說是不要小於1e-4)
2. 設定的優先順序並沒有什麼作用
3. Target僅僅幫助傳遞資料,傳遞Channel中Sampler計算的資料給UpdateMatrixTransform

1.2 單動畫多頻道

既然動畫Animation是用來管理眾多Channel,嘗試在Animation中新增多個Channel:

int main(int argc, char **argv)
{
    osgViewer::Viewer viewer;
    osg::Group* grp = new osg::Group;

    osg::MatrixTransform *mt = new osg::MatrixTransform;

    osgAnimation::UpdateMatrixTransform *umt1 = new osgAnimation::UpdateMatrixTransform;
    umt1->setName("move1UpdatedateCallback");
    umt1->getStackedTransforms().push_back(new osgAnimation::StackedTranslateElement("move1"));

    //Channel 1
    osgAnimation::Vec3LinearChannel *channel1 = new osgAnimation::Vec3LinearChannel();
    channel1->setTargetName("move1UpdatedateCallback");
    channel1->setName("move1");
    channel1->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(0, osg::Vec3(0, 0, 0)));
    channel1->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(2, osg::Vec3(5, 0, 0)));

    //Channel 2
    osgAnimation::Vec3LinearChannel *channel2 = new osgAnimation::Vec3LinearChannel();
    channel2->setTargetName("move1UpdatedateCallback");
    channel2->setName("move1");
    channel2->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(0, osg::Vec3(0, 0, 0)));
    channel2->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(2, osg::Vec3(0, 0, 5)));

    osgAnimation::Animation *anim1 = new osgAnimation::Animation();
    anim1->addChannel(channel1);
    anim1->addChannel(channel2);

    osgAnimation::BasicAnimationManager *bam = new osgAnimation::BasicAnimationManager();
    bam->registerAnimation(anim1);
    bam->playAnimation(anim1);

    osg::Node* cowNode = osgDB::readNodeFile("cow.osg");
    grp->addChild(mt);
    mt->addChild(cowNode);
    grp->addUpdateCallback(bam);

    mt->addUpdateCallback(umt1);

    viewer.setSceneData(grp);
    viewer.setUpViewInWindow(300, 300, 800, 800);
    return (viewer.run());
}

執行修改程式,發現模型在兩個頻道的共同作用下(Channel1向右移動,Channel2向上移動)向螢幕右上方向運動。
由於同一個動畫的權重是一樣的,也就是說該動畫下所有的頻道的權重也都是一樣的。UpdateMatrixTransform只包含一個StackedTranslateElement,這個StackedTranslateElement被兩個頻道都連線,因此產生的Target也是二者共用的。從Target使用權重計算的程式碼,可以知道:
1. Target的_weight和_priorityWeight在每一次遍歷的過程中會首先被設定為0, 如果所有的頻道的優先順序都沒有變化,那麼Target中的_weight永遠將是0,下面這段程式碼將永遠不會被執行(1.2中單動畫由於所有的動畫頻道下的優先順序是一樣的,因此下面這段程式碼不會被執行)

                if (_lastPriority != priority)
                {
                    // change in priority
                    // add to weight with the same previous priority cumulated weight
                    _weight += _priorityWeight * (1.0 - _weight);
                    _priorityWeight = 0;
                    _lastPriority = priority;
                }
  1. 遍歷的第一個頻道肯定會執行else中的語句,也就是(第一次迴圈必定會進入else分支)
            else
            {
                _priorityWeight = weight;
                _lastPriority = priority;
                _target = val;
  1. 對於本例中的情況,Target的update程式碼會被執行兩次,由於_weight = 0,實際程式碼如下:
                _priorityWeight += weight;
                float t =  weight / _priorityWeight;
                lerp(t, _target, val);

lerp是一個線性的差值,lerp的這一行是
_target = _target * (1-t) + t * val;
由於一個Animation中所有的weight都是相等的,也就是這個t=0.5,假設一個Animation中有多個Channel,那麼t依次等於 1/2, 1/3, 1/4, … 知道 1/Channel個數。

如果權重相同,那麼一段動畫的最終取值應該是:
最終值 = 1/N * Channel1取值 + 1/N Channel2取值 + … + 1/N ChannelN取值。
使用這段程式碼可以得到這個結論。

這段程式碼的計算推導過程簡單說一下:

假設一個動畫Animation中有n個Channel,由於這些Channel的權重和優先順序都是一樣的,假設它們的權值都是w,並且每個Channel由它的Sampler取樣器計算出來的結果是v, 最終的計算的結果(由target儲存)是value,那麼程式碼的計算過程如下:

第一次迭代: value = v1 (_priorityWeight = w)

第二次迭代: 由於_priorityWeight = _priorityWeight + w, 也就是_priorityWeight = 2w,那麼有: value = ( 1 - w / 2 * w )v1 + v2 w = 1/2*(v1+v2)

第三次迭代: 同樣 value = (1 - w / 3*w)(0.5(v1+v2))+ 1/3 cv3 = 1/3*(v1+v2+v3)
……

第n次迭代: value = [1(1/n)](1/n1)(v1+v2+...+vn1)+(1/n)vn = 1/n * (v1+v2+…+v_n)

也就是說當一個動畫中有多個頻道時,有以下結論:
1. 所有頻道的權重和優先順序都只能由Animation設定,並且權重都一樣,優先順序也一樣。
2. 權重不能設定為0(具體來說是不能小於1e-4),否則update函式直接退出。除此之外,權重設定成任何其他數都是一樣的效果
3. 最後得到的結果是所有頻道的平均值

2. 動畫的優先順序(Priority)

osgAnimation的動畫管理器BasicAnimationManager中儲存著動畫的陣列,它是用一個map來進行儲存的,這個map的鍵值是動畫的優先順序,預設情況下新增進去的Animation的優先順序是0,在使用void playAnimation (Animation* pAnimation, int priority = 0, float weight = 1.0);進行播放時設定優先順序。按照之前的討論,優先順序只有在存在多個動畫的時候才有意義。

2.1 多動畫單頻道

多動畫單頻道指的是管理器BasicAnimationManager中新增很多個Animation動畫,每一個動畫中只包含一個Channel,修改程式碼:

int main(int argc, char **argv)
{
    osgViewer::Viewer viewer;
    osg::Group* grp = new osg::Group;

    osg::MatrixTransform *mt = new osg::MatrixTransform;

    osgAnimation::UpdateMatrixTransform *umt1 = new osgAnimation::UpdateMatrixTransform;
    umt1->setName("move1UpdatedateCallback");
    umt1->getStackedTransforms().push_back(new osgAnimation::StackedTranslateElement("move1"));


    //第一個動畫
    osgAnimation::Vec3LinearChannel *channel1 = new osgAnimation::Vec3LinearChannel();
    channel1->setTargetName("move1UpdatedateCallback");
    channel1->setName("move1");
    channel1->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(0, osg::Vec3(0, 0, 0)));
    channel1->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(2, osg::Vec3(5, 0, 0)));
    osgAnimation::Animation *anim1 = new osgAnimation::Animation();
    anim1->addChannel(channel1);

    //第二個動畫
    osgAnimation::Vec3LinearChannel *channel2 = new osgAnimation::Vec3LinearChannel();
    channel2->setTargetName("move1UpdatedateCallback");
    channel2->setName("move1");
    channel2->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(0, osg::Vec3(0, 0, 0)));
    channel2->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(2, osg::Vec3(0, 0, 5)));
    osgAnimation::Animation *anim2 = new osgAnimation::Animation();
    anim2->addChannel(channel2);

    //第三個動畫
    //第二個動畫
    osgAnimation::Vec3LinearChannel *channel3 = new osgAnimation::Vec3LinearChannel();
    channel3->setTargetName("move1UpdatedateCallback");
    channel3->setName("move1");
    channel3->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(0, osg::Vec3(0, 0, 0)));
    channel3->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(2, osg::Vec3(0, 5, 0)));
    osgAnimation::Animation *anim3 = new osgAnimation::Animation();
    anim2->addChannel(channel3);

    osgAnimation::BasicAnimationManager *bam = new osgAnimation::BasicAnimationManager();
    bam->registerAnimation(anim1);
    bam->registerAnimation(anim2);
    bam->registerAnimation(anim3);
    bam->playAnimation(anim1, 1, 1);
    bam->playAnimation(anim2, 1, 1);
    bam->playAnimation(anim3, 1, 1);

    osg::Node* cowNode = osgDB::readNodeFile("cow.osg");
    grp->addChild(mt);
    mt->addChild(cowNode);
    grp->addUpdateCallback(bam);

    mt->addUpdateCallback(umt1);

    viewer.setSceneData(grp);
    viewer.setUpViewInWindow(300, 300, 800, 600);
    return (viewer.run());
}

有以下幾種情況:
1. 當所有Animation的優先順序一樣,權重也一樣的時候,和單動畫多頻道的結果是一樣的(所有的動畫平均起作用)
2. 當Animation的優先順序一樣,但是權重不一樣的時候,按照權重起作用,也就是最終的結果是 : value = w1 * v1 + w2 * v2 (這裡的權重w1、w2,都是它們設定的權除以它們所有的權的和),也就是說這時候的權可以隨便設定,不要求所有的權重之和是1
3. 當Animation的優先順序不一樣,但是權重一樣的時候
4. 當Animation的優先順序不一樣,權重也不一樣的時候
第3和第4兩種情況其實是一樣的處理過程,演算法基本上是這樣的:

優先順序高的動畫值先計算,在先計算的時候有優先權,它的權重起的作用更大,假設有n的動畫,一次按優先順序從高到底排列是 A1, A2,A3,A4… An,假設A1的動畫的權重是W1,A2是W2… An是Wn,那麼A1優先順序最高,它的權重分的是整個權重是已1為參考的,如果A1的權重是1,那麼其他A2到An的動畫都不起作用。如果A1的權重不是1,而是一個小於1的值W1,那麼剩下的A2到An只能去分享(1-W1),它們的權重都是以(1-W1)來計算的,也就是分到的權重就會更小。

這個過程可以用下面一個場景來類比:假設很多人花錢買了一個西瓜分,現在大家安排怎麼分這個西瓜,西瓜怎麼分以家庭為單位,家庭裡面每個人分到的西瓜大小都一樣。每個家庭可以分一個百分數的大小,但是這個百分數是以剩餘部分來計算的。也就是說第一個分西瓜的家庭,他分的百分數是以整個西瓜大小來計算的(假設他分50%),那麼直接就切走一半的西瓜。剩下的人分的部分的百分數只能以剩下的半個西瓜來計算,假設第二家分的的也是50%,那麼事實上它只能分走1/4個西瓜,那麼以此類推,越是後分的家庭,分到的西瓜越小(雖然後面分的家庭可能的百分數也可能很大,但是給他分的基數小了(剩餘的西瓜大小)),也就是說優先順序越高的家庭得到的實際西瓜多,也就是貢獻越大。

上面這個類比已經很貼切了。這裡面每一個家庭實際上對應一個Animation,每個家庭成員對應一個Channel。我們現在討論的是多動畫單頻道,也就是相當於每一個家庭只有一個成員,多動畫多頻道就相當於每個家庭有多個成員而已,但是實際上不影響最後計算的結果。從上面的分析可以看出:

  1. 優先順序越高的動畫對最後的Target得到的最終插值結果的貢獻越大。
  2. 動畫的計算如果存在不同優先順序和權重的時候,設定的權重並不是簡單的線性關係
  3. 如果我們把優先順序最高的權設定為大於1,那麼會有一個很奇怪的效果,其他動畫的權重會在計算中出現負數,導致動畫的計算結果是反的。也就是說插值的結果是我們預期插值結果的相反數,最好避免這樣做,在設定權重的時候都設定在[0,1]之間。

2.2 多動畫多頻道

這種情況和多動畫單頻道類似,只是每個動畫多出來很多的Channel,但是這些Channel疊加起來的權重是和整個動畫的權值一樣。假設動畫的權值是 W,那麼所有它包含的Channel(N個)疊加的結果權值也是W。

w=1/Nw+1/Nw+...+1/Nw(N

整個過程在2.1中已經論述清楚了。

至此,整個osgAnimation中的動畫過程都解析完整了。接下來的文章會繼續討論osgAnimation中漸進動畫和骨骼動畫等內容。

相關推薦

OSG畫庫Animation解析

通過之前的兩篇文章,基本上對osgAnimation整個動畫的實現體系有了一個比較完整的介紹,本文主要介紹動畫中其他的一些內容,主要包括權、優先順序、以及動畫變換中的順序等內容。 1. 動畫的權重(weight) 假設對一輛勻速執行中的汽車施加兩股力量,

OSG畫庫Animation解析

1. 概述 OpenSceneGraph(OSG)庫在核心庫的基礎上寫了許多擴充套件,這其中就有一個庫專門用來處理場景中的動畫的庫osgAnimation,這一系列的文章主要介紹一下osgAnimation庫的原理和實現,記錄下來方便日後查閱。本文對應的OSG

OSG畫庫Animation解析

在前文中討論到osgAnimation中的Target物件應該和場景的更新回撥有一些關係,本文接著前面繼續討論。 在前面的程式碼中,使用自己編寫的更新回撥類RotateCallback,osgAnimation根據動畫的內容和方式的不同,提供了幾種動畫的更新回

dva源碼解析

numbers setup 保存數據 啟動應用 mode erp 訂閱 需要 oss 轉載 原文 https://blog.csdn.net/zhangrui_web/article/details/79651448 API 輸出文件 dva 默認輸出文件。 dva

yocto-sumo源碼解析oe-setup-builddir

r環境 direct director error local 源碼 bash 創建 變量 該腳本的主要功能就是創建構建目錄並準備一些配置文件,比如conf/local.conf,conf/bblayer.conf 1. 檢測BUILDDIR環境變量是否設置好(在本系列分享

Spring原始碼解析——元件註冊3

@Scope設定元件作用域 import com.ken.domain.Person; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Config

Android圖片載入框架最全解析,深入探究Glide的快取機制(筆記)

原文地址:Android圖片載入框架最全解析(三),深入探究Glide的快取機制 筆記: 1.Glide快取簡介 2.快取Key EngineKey 重寫了equals()和hashCode()方法,保證只有傳入EngineKey的所有引數都相同的情況下才認為是

java集合原始碼解析--List

今天給大家帶來有序集合的介面List,我想也應該是大家在工作中用的比較多的 先來看看介面的定義: public interface List<E> extends Collection<E>可以看出介面List直接繼承於介面Collection,並且一樣使用了

影象修復例項解析

本篇基於SIGGRAPH 2017 (ACM ToG)的 Globally and Locally Consistent Image Completion  (CE中加入Global+Local兩個判別器的改進),  proj:http://hi.cs.waseda.ac

深度學習論文翻譯解析:Detecting Text in Natural Image with Connectionist Text Proposal Network

論文標題:Detecting Text in Natural Image with Connectionist Text Proposal Network 論文作者:Zhi Tian , Weilin Huang, Tong He , Pan He , and Yu Qiao 論文原始碼的下載地址:htt

Spring原始碼解析:父子容器的概念

  相信大家現在在使用spring專案開發時可能不只是單單使用spring一個框架進行開發, 可能會用到現在主流的ssm,spring和springmvc一起使用。   而在一起使用的時候我就發現了一個問題,在web.xml配置spring容器初始化的時候存在一個問題。     一般我們在配置sprin

Spring源碼解析:父子容器的概念

意思 資源 圖片 方法 一起 listen 調用 rop getbean   相信大家現在在使用spring項目開發時可能不只是單單使用spring一個框架進行開發, 可能會用到現在主流的ssm,spring和springmvc一起使用。   而在一起使用的時候我就發現了一

Kubernetes彈性伸縮全場景解析 - HPA實踐手冊

前言 在上一篇文章中,給大家介紹和剖析了HPA的實現原理以及演進的思路與歷程。在本文中,我們會為大家講解如何使用HPA以及一些需要注意的細節。 autoscaling/v1實踐 v1的模板可能是大家平時見到最多的也是最簡單的,v1版本的HPA只支援一種指標 —— CPU。傳統意義上,彈性伸縮最少也會支

Java原始碼分析——java.util工具包解析——HashMap、TreeMap、LinkedHashMap、Hashtable類解析

    Map,中文名字對映,它儲存了鍵-值對的一對一的關係形式,並用雜湊值來作為存貯的索引依據,在查詢、插入以及刪除時的時間複雜度都為O(1),是一種在程式中用的最多的幾種資料結構。Java在java.util工具包中實現了Map介面,來作為各大

dubbo-php-framework的Protocol解析

我們接著前面的文章繼續分析,先從完整的接到請求後的處理過程開始。 //接收到完整的資料包後的處理過程 public function onOneRequest($client_id, $request) { if($this->parser->isHea

Redis5.0原始碼解析----------字典詳細

基於Redis5.0 在字典中, 一個鍵(key)可以和一個值(value)進行關聯(或者說將鍵對映為值), 這些關聯的鍵和值就被稱為鍵值對 字典中的每個鍵都是獨一無二的, 程式可以在字典中根據鍵查詢與之關聯的值, 或者通過鍵來更新值, 又或者根據鍵來刪除整個鍵值對

dubbo-php-framework的客戶端api解析

這篇我們分析生成Proxy後的處理流程,接著前面一篇文章,我們可以看到有p2p和register模式,這兩種模式最大的區別是服務地址資訊從哪裡獲取,而proxy的處理流程卻是一直的。 public static function newProxyInstance($ser

OKHttp 3.10原始碼解析:快取機制

本篇我們來講解OKhttp的快取處理,在網路請求中合理地利用本地快取能有效減少網路開銷,提高響應速度。HTTP報頭也定義了很多控制快取策略的域,我們先來認識一下HTTP的快取策略。 一.HTTP快取策略 HTTP快取有多種規則,根據是否需要向伺服器發起請求來分類,我們將其分為兩大類:強制

spring mvc引數解析 HandlerMethodArgumentResolver

前面分析到WebDataBinderFactory 建立,實際是建立一個ServletRequestDataBinderFactory例項。 解析引數的方法是在父類實現了ModelAttributeMethodProcessor 從圖中可以看出類的繼承關係。

基於django 開發的框架 jumpserver 原始碼解析

   基於 rest_framework 的 url 路由 跟 資料 跟 前端互動。   為了要讓 django 支援 RESTful 風格 的 介面 需要 用到 第三方元件,來 分析下rest_framework  對 request 做了什麼 ,又對