Android 6.0 IMS流程——IMS开机初始化

一、IMS开机初始化

这里写图片描述
(如果图片看不清的话,可以右键选择在查看图片,或者把图片另存到自己电脑再查看。)


http://blog.csdn.net/linyongan


1.1 监控IMS Service

PhoneApp进程是在系统开机时启动的,Phone进程初始化的时候(步骤1~6),在创建GSMPhone或者CDMAPhone之后,会执行监控IMS Service的流程,也就是流程图上的步骤7,在PhoneFactory.Java的makeDefaultPhone()方法中:

<code class="hljs java has-numbering">    <span class="hljs-javadoc">/**
     * FIXME replace this with some other way of making these
     * instances
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">makeDefaultPhone</span>(Context context) {
        ......
        <span class="hljs-comment">//先根据phoneType创建GSMPhone或者CDMAPhone</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < numPhones; i++) {
            PhoneBase phone = <span class="hljs-keyword">null</span>;
            <span class="hljs-keyword">int</span> phoneType = TelephonyManager.getPhoneType(networkModes[i]);
            <span class="hljs-keyword">if</span> (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
                phone = TelephonyPluginDelegate.getInstance().makeGSMPhone(context,
                                sCommandsInterfaces[i], sPhoneNotifier, i);
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
                phone = TelephonyPluginDelegate.getInstance().makeCDMALTEPhone(context,
                                sCommandsInterfaces[i], sPhoneNotifier, i);
            }
            Rlog.i(LOG_TAG, <span class="hljs-string">"Creating Phone with type = "</span> + phoneType + <span class="hljs-string">" sub = "</span> + i);

            sProxyPhones[i] = TelephonyPluginDelegate.getInstance().makePhoneProxy(phone);
        }
        ......
        <span class="hljs-comment">//在makeDefaultPhone()方法的最后,等所有要初始化的操作</span>
        <span class="hljs-comment">//(如创建RIL,Default Phone等)都执行完之后。</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < numPhones; i++) {
            <span class="hljs-comment">//开始执行监控IMS Service的流程</span>
            sProxyPhones[i].startMonitoringImsService();
        }
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li></ul>

接着来到PhoneProxy.java的startMonitoringImsService()方法中,可以看到在监控IMS service之前必须先创建Default Phone。

<code class="hljs cs has-numbering">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startMonitoringImsService</span>() {
        <span class="hljs-comment">//必须先创建Default Phone</span>
        <span class="hljs-keyword">if</span> (mActivePhone != <span class="hljs-keyword">null</span>) {
            mActivePhone.startMonitoringImsService();
        }
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul>

接着来到PhoneBase.java的startMonitoringImsService()方法中,来到这里,主要是为了注册上监听IMS SERVICE UP或者DOWN的广播。等到ImsService onCreate()的时候,会发出ACTION_IMS_SERVICE_UP的广播,然后PhoneBase接收到广播之后就可以执行创建IMSPhone对象(1.3小节会详细讲)和通知modem turn on IMS的流程(1.4小节会详细讲)。

<code class="hljs java has-numbering">    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startMonitoringImsService</span>() {
        <span class="hljs-keyword">if</span> (getPhoneType() == PhoneConstants.PHONE_TYPE_SIP) {
            <span class="hljs-keyword">return</span>;
        }
        <span class="hljs-keyword">synchronized</span>(PhoneProxy.lockForRadioTechnologyChange) {
            IntentFilter filter = <span class="hljs-keyword">new</span> IntentFilter();
            filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
            filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
            <span class="hljs-comment">//注册监听IMS SERVICE UP或者DOWN的广播</span>
            mContext.registerReceiver(mImsIntentReceiver, filter);
            mImsIntentReceiverRegistered = <span class="hljs-keyword">true</span>;

            <span class="hljs-comment">// Monitor IMS service - but first poll to see if already up (could miss</span>
            <span class="hljs-comment">// intent)</span>
            <span class="hljs-comment">//初始化ImsManager对象</span>
            ImsManager imsManager = ImsManager.getInstance(mContext, getPhoneId());
            <span class="hljs-comment">//IMS service目前还不可用</span>
            <span class="hljs-keyword">if</span> (imsManager != <span class="hljs-keyword">null</span> && imsManager.isServiceAvailable()) {
                mImsServiceReady = <span class="hljs-keyword">true</span>;
                updateImsPhone();
                ImsManager.updateImsServiceConfig(mContext, mPhoneId, <span class="hljs-keyword">false</span>);
            }
        }
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li></ul>

1.2 IMS Service的启动

这里写图片描述
步骤11 :ImsServiceAutoboot.java监听了ACTION_BOOT_COMPLETED和ACTION_SIM_STATE_CHANGED广播

<code class="hljs lasso has-numbering">    <span class="hljs-comment">//ClassName是ImsService,所以下面启动的Service就是Ims Service</span>
    <span class="hljs-keyword">private</span> final static <span class="hljs-built_in">String</span> mClassName <span class="hljs-subst">=</span> ImsService<span class="hljs-built_in">.</span>class<span class="hljs-built_in">.</span>getName();

    <span class="hljs-keyword">public</span> <span class="hljs-literal">void</span> onReceive(Context context, Intent intent) {
        <span class="hljs-built_in">String</span> intentAction <span class="hljs-subst">=</span> intent<span class="hljs-built_in">.</span>getAction();
        <span class="hljs-comment">//在开机的时候,会收到ACTION_SIM_STATE_CHANGED广播</span>
        <span class="hljs-keyword">if</span> ((Intent<span class="hljs-built_in">.</span>ACTION_BOOT_COMPLETED<span class="hljs-built_in">.</span><span class="hljs-keyword">equals</span>(intentAction))  <span class="hljs-subst">||</span>
            (ACTION_SIM_STATE_CHANGED<span class="hljs-built_in">.</span><span class="hljs-keyword">equals</span>(intentAction))) {
            <span class="hljs-comment">//如果这时候Service还没有启动</span>
            <span class="hljs-keyword">if</span> (<span class="hljs-subst">!</span>isServiceRunning(context)) {
                <span class="hljs-keyword">Log</span><span class="hljs-built_in">.</span>d(<span class="hljs-built_in">TAG</span>, <span class="hljs-string">"Starting "</span> <span class="hljs-subst">+</span> mClassName <span class="hljs-subst">+</span> <span class="hljs-string">" : "</span> <span class="hljs-subst">+</span> intentAction <span class="hljs-subst">+</span> <span class="hljs-string">" received. "</span>);
                <span class="hljs-comment">//启动Service</span>
                startService(context);
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">Log</span><span class="hljs-built_in">.</span>d(<span class="hljs-built_in">TAG</span>, mClassName <span class="hljs-subst">+</span> <span class="hljs-string">" is already running. "</span> <span class="hljs-subst">+</span>
                           intentAction <span class="hljs-subst">+</span> <span class="hljs-string">" ignored. "</span>);
            }
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">Log</span><span class="hljs-built_in">.</span>e(<span class="hljs-built_in">TAG</span>, <span class="hljs-string">"Received Intent: "</span> <span class="hljs-subst">+</span> intent<span class="hljs-built_in">.</span>toString());
        }
    }

    <span class="hljs-keyword">private</span> <span class="hljs-literal">void</span> startService(Context context) {
        ComponentName comp <span class="hljs-subst">=</span> <span class="hljs-literal">new</span> ComponentName(context<span class="hljs-built_in">.</span>getPackageName(), mClassName);
        <span class="hljs-comment">//启动Ims Service</span>
        ComponentName service <span class="hljs-subst">=</span> context<span class="hljs-built_in">.</span>startService(<span class="hljs-literal">new</span> Intent()<span class="hljs-built_in">.</span>setComponent(comp));
        <span class="hljs-keyword">if</span> (service <span class="hljs-subst">==</span> <span class="hljs-built_in">null</span>) {
            <span class="hljs-keyword">Log</span><span class="hljs-built_in">.</span>e(<span class="hljs-built_in">TAG</span>, <span class="hljs-string">"Could Not Start Service "</span> <span class="hljs-subst">+</span> comp<span class="hljs-built_in">.</span>toString());
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">Log</span><span class="hljs-built_in">.</span>e(<span class="hljs-built_in">TAG</span>, mClassName <span class="hljs-subst">+</span> <span class="hljs-string">" service Started Successfully"</span>);
        }
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li></ul>

在ImsService.java的onCreate()方法中

<code class="hljs java has-numbering">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>() {
        <span class="hljs-keyword">super</span>.onCreate();
        Log.d (LOG_TAG, <span class="hljs-string">"ImsService created!"</span>);
        mServiceSub = <span class="hljs-keyword">new</span> ImsServiceSub[getNumSubscriptions()];
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < getNumSubscriptions(); i++) {
            <span class="hljs-comment">//在这里初始化ImsServiceSub对象,在ImsServiceSub的</span>
            <span class="hljs-comment">//构造方法中进而会初始化ImsSenderRxr、ImsServiceSubHandler</span>
            <span class="hljs-comment">//等对象并且监听IMS、Call等的状态变化;</span>
            <span class="hljs-comment">//(ImsSenderRxr跟RILJ作用类似,可以跟Qcril交互,</span>
            <span class="hljs-comment">// IMS的流程会走ImsSenderRxr而不会走RIL.java)</span>
            mServiceSub[i] = <span class="hljs-keyword">new</span> ImsServiceSub(i + <span class="hljs-number">1</span>, <span class="hljs-keyword">this</span>);
        }
        ServiceManager.addService(<span class="hljs-string">"ims"</span>, mBinder);
        Intent intent = <span class="hljs-keyword">new</span> Intent(ImsManager.ACTION_IMS_SERVICE_UP);
        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
        <span class="hljs-comment">//先判断是否是双卡</span>
        <span class="hljs-keyword">if</span> (TelephonyManager.getDefault().isMultiSimEnabled()) {
            <span class="hljs-comment">//如果是双卡,还需把ImsPhoneId传递过去</span>
            intent.putExtra(ImsManager.EXTRA_PHONE_ID, mImsPhoneId);
            <span class="hljs-comment">//发送广播,通知PhoneBase</span>
            <span class="hljs-keyword">this</span>.sendStickyBroadcast(intent);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">this</span>.sendStickyBroadcast(intent);
        }
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> defaultSub = <span class="hljs-number">1</span>;
        ImsVideoGlobals.init(mServiceSub[defaultSub-<span class="hljs-number">1</span>], <span class="hljs-keyword">this</span>);

        <span class="hljs-comment">/* Check if any change in socket communication is required */</span>
        initSubscriptionStatus();

        <span class="hljs-comment">/* Initialize Call Deflect support to not supported */</span>
        initCallDeflectStatus();
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li></ul>

在 IMS Service的启动之后,回到PhoneBase.java的mImsIntentReceiver广播接收器中

<code class="hljs java has-numbering">    <span class="hljs-keyword">private</span> BroadcastReceiver mImsIntentReceiver = <span class="hljs-keyword">new</span> BroadcastReceiver() {
        <span class="hljs-annotation">@Override</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onReceive</span>(Context context, Intent intent) {
            Rlog.d(LOG_TAG, <span class="hljs-string">"mImsIntentReceiver: action "</span> + intent.getAction());
            <span class="hljs-comment">//先判断intent中是否包含Phone Id</span>
            <span class="hljs-keyword">if</span> (intent.hasExtra(ImsManager.EXTRA_PHONE_ID)) {
                <span class="hljs-keyword">int</span> extraPhoneId = intent.getIntExtra(ImsManager.EXTRA_PHONE_ID,
                        SubscriptionManager.INVALID_PHONE_INDEX);
                Rlog.d(LOG_TAG, <span class="hljs-string">"mImsIntentReceiver: extraPhoneId = "</span> + extraPhoneId);
                <span class="hljs-comment">//如果获取到的Phone Id等于-1或者Phone Id跟当前get到的</span>
                <span class="hljs-comment">//phone Id不一样,说明有误,所以直接return</span>
                <span class="hljs-keyword">if</span> (extraPhoneId == SubscriptionManager.INVALID_PHONE_INDEX ||
                        extraPhoneId != getPhoneId()) {
                    <span class="hljs-keyword">return</span>;
                }
            }

            <span class="hljs-keyword">synchronized</span> (PhoneProxy.lockForRadioTechnologyChange) {
                <span class="hljs-comment">//如果ACTION是IMS_SERVICE_UP,说明IMS Service已启动</span>
                <span class="hljs-keyword">if</span> (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_UP)) {
                    <span class="hljs-comment">//将标志设置成true,在更新ImsPhone时会用到</span>
                    mImsServiceReady = <span class="hljs-keyword">true</span>;
                    <span class="hljs-comment">//创建或者更新ImsPhone(1.3小节会详细讲)</span>
                    updateImsPhone();
                    <span class="hljs-comment">//更新配置信息,通知Modem turn On/Off IMS(1.4小节会详细讲)</span>
                    ImsManager.updateImsServiceConfig(mContext, mPhoneId, <span class="hljs-keyword">false</span>);
                } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)){
                    mImsServiceReady = <span class="hljs-keyword">false</span>;
                    updateImsPhone();
                }
            }
        }
    };</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li></ul>

1.3 创建ImsPhone

这里写图片描述

<code class="hljs cs has-numbering">    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">updateImsPhone</span>() {
        Rlog.d(LOG_TAG, <span class="hljs-string">"updateImsPhone"</span>
                + <span class="hljs-string">" mImsServiceReady="</span> + mImsServiceReady);
        <span class="hljs-comment">//如果ImsService已启动并且ImsPhone还没有被创建</span>
        <span class="hljs-keyword">if</span> (mImsServiceReady && (mImsPhone == <span class="hljs-keyword">null</span>)) {
            <span class="hljs-comment">//通过PhoneFactory创建ImsPhone,这里的this就是default Phone</span>
            <span class="hljs-comment">//ImsPhone</span>
            mImsPhone = PhoneFactory.makeImsPhone(mNotifier, <span class="hljs-keyword">this</span>);
            <span class="hljs-comment">//registerPhone的作用是把新创建的ImsPhone对象添加到CallManager</span>
            <span class="hljs-comment">//的Phone list中,并且为ImsPhone注册监听各种Phone States</span>
            CallManager.getInstance().registerPhone(mImsPhone);
            mImsPhone.registerForSilentRedial(
                    <span class="hljs-keyword">this</span>, EVENT_INITIATE_SILENT_REDIAL, <span class="hljs-keyword">null</span>);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!mImsServiceReady && (mImsPhone != <span class="hljs-keyword">null</span>)) {
            CallManager.getInstance().unregisterPhone(mImsPhone);
            mImsPhone.unregisterForSilentRedial(<span class="hljs-keyword">this</span>);

            mImsPhone.dispose();
            <span class="hljs-comment">// Potential GC issue if someone keeps a reference to ImsPhone.</span>
            <span class="hljs-comment">// However: this change will make sure that such a reference does</span>
            <span class="hljs-comment">// not access functions through NULL pointer.</span>
            <span class="hljs-comment">//mImsPhone.removeReferences();</span>
            mImsPhone = <span class="hljs-keyword">null</span>;
        }
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li></ul>

接着PhoneFactory还是调了ImsPhoneFactory的makePhone()方法

<code class="hljs java has-numbering">    <span class="hljs-javadoc">/**
     * Makes a {@link ImsPhone} object.
     *<span class="hljs-javadoctag"> @return</span> the {@code ImsPhone} object or null if the exception occured
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ImsPhone <span class="hljs-title">makeImsPhone</span>(PhoneNotifier phoneNotifier, Phone defaultPhone) {
        <span class="hljs-keyword">return</span> ImsPhoneFactory.makePhone(sContext, phoneNotifier, defaultPhone);
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul>

而ImsPhoneFactory中只有一个方法,也就是

<code class="hljs cs has-numbering">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ImsPhone <span class="hljs-title">makePhone</span>(Context context,
            PhoneNotifier phoneNotifier, Phone defaultPhone) {

        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">//直接new ImsPhone,在ImsPhone的构造方法中又</span>
            <span class="hljs-comment">//会new ImsPhoneCallTracker,在ImsPhoneCallTracker的</span>
            <span class="hljs-comment">//构造方法中,会执行获取IMS注册状态的流程(第二节会详细讲)</span>
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ImsPhone(context, phoneNotifier, defaultPhone);
        } <span class="hljs-keyword">catch</span> (Exception e) {
            Rlog.e(<span class="hljs-string">"VoltePhoneFactory"</span>, <span class="hljs-string">"makePhone"</span>, e);
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
        }
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li></ul>

到这里,ImsPhone就被创建啦。


1.4 通知Modem turn On IMS

这里写图片描述
在PhoneBase.java的mImsIntentReceiver广播接收器中,除了创建ImsPhone对象,还调了ImsManager.java的updateImsServiceConfig()方法更新配置信息。

<code class="hljs java has-numbering">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">updateImsServiceConfig</span>(Context context, <span class="hljs-keyword">int</span> phoneId, <span class="hljs-keyword">boolean</span> force) {
        <span class="hljs-keyword">final</span> ImsManager imsManager = ImsManager.getInstance(context, phoneId);
        <span class="hljs-comment">//imsManager.mConfigUpdated的值还是false的话,说明还没有更新过</span>
        <span class="hljs-keyword">if</span> (imsManager != <span class="hljs-keyword">null</span> && (!imsManager.mConfigUpdated || force)) {
            <span class="hljs-keyword">try</span> {
                <span class="hljs-comment">//如果Volte、VideoCall、WfC(Wifi Calling)任意一项可用且enable的话,那么就要turn On IMS</span>
                <span class="hljs-keyword">boolean</span> turnOn = imsManager.updateVolteFeatureValue();
                turnOn |= imsManager.updateVideoCallFeatureValue();
                turnOn |= imsManager.updateWfcFeatureAndProvisionedValues();

                <span class="hljs-keyword">if</span> (turnOn) {
                    <span class="hljs-comment">//通知modem turn On IMS,不同平台有不同的实现</span>
                    imsManager.turnOnIms();
                } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (getBooleanCarrierConfig(context,
                        CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)) {
                    imsManager.turnOffIms();
                }
                <span class="hljs-comment">//把标志位设成true,除非强制更新,否则不会再进来这里</span>
                imsManager.mConfigUpdated = <span class="hljs-keyword">true</span>;
            } <span class="hljs-keyword">catch</span> (ImsException e) {
                loge(<span class="hljs-string">"updateImsServiceConfig: "</span> + e);
                imsManager.mConfigUpdated = <span class="hljs-keyword">false</span>;
            }
        }
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li></ul>

二、获取IMS注册状态


(如果图片看不清的话,可以右键选择在查看图片,或者把图片另存到自己电脑再查看。)
获取IMS注册状态本来也属于开机是执行的流程,但是本段流程有点长,所以把它独立起来讲。紧跟着1.3节创建ImsPhone,在ImsPhone的构造方法中:

<code class="hljs java has-numbering">    ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone) {
        <span class="hljs-keyword">super</span>(<span class="hljs-string">"ImsPhone"</span>, context, notifier);
        mDefaultPhone = (PhoneBase) defaultPhone;
        <span class="hljs-comment">//初始化ImsPhoneCallTracker对象</span>
        mCT = <span class="hljs-keyword">new</span> ImsPhoneCallTracker(<span class="hljs-keyword">this</span>);
        ......
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul>

而ImsPhoneCallTracker的构造方法中

<code class="hljs cs has-numbering">    ImsPhoneCallTracker(ImsPhone phone) {
        <span class="hljs-keyword">this</span>.mPhone = phone;

        IntentFilter intentfilter = <span class="hljs-keyword">new</span> IntentFilter();
        intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL);
        <span class="hljs-comment">//注册监听来电的广播</span>
        mPhone.getContext().registerReceiver(mReceiver, intentfilter);
        <span class="hljs-comment">//启动新线程</span>
        Thread t = <span class="hljs-keyword">new</span> Thread() {
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>() {
                <span class="hljs-comment">//执行Opens IMS service的流程</span>
                getImsService();
            }
        };
        t.start();
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">getImsService</span>() {
        <span class="hljs-keyword">if</span> (DBG) log(<span class="hljs-string">"getImsService"</span>);
        <span class="hljs-comment">//先得到ImsManager对象</span>
        mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId());
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">//然后调用它的open()方法获取到Service Id </span>
            <span class="hljs-comment">//这里传递进去的serviceClass是ImsServiceClass.MMTEL(它的值是1)</span>
            mServiceId = mImsManager.open(ImsServiceClass.MMTEL,
                    createIncomingCallPendingIntent(),
                    mImsConnectionStateListener);

            <span class="hljs-comment">// Get the ECBM interface and set IMSPhone's listener object for notifications</span>
            getEcbmInterface().setEcbmStateListener(mPhone.mImsEcbmStateListener);
            <span class="hljs-keyword">if</span> (mPhone.isInEcm()) {
                <span class="hljs-comment">// Call exit ECBM which will invoke onECBMExited</span>
                mPhone.exitEmergencyCallbackMode();
            }
            <span class="hljs-keyword">int</span> mPreferredTtyMode = Settings.Secure.getInt(
                mPhone.getContext().getContentResolver(),
                Settings.Secure.PREFERRED_TTY_MODE,
                Phone.TTY_MODE_OFF);
           mImsManager.setUiTTYMode(mPhone.getContext(), mServiceId, mPreferredTtyMode, <span class="hljs-keyword">null</span>);

        } <span class="hljs-keyword">catch</span> (ImsException e) {
            loge(<span class="hljs-string">"getImsService: "</span> + e);
            <span class="hljs-comment">//Leave mImsManager as null, then CallStateException will be thrown when dialing</span>
            mImsManager = <span class="hljs-keyword">null</span>;
        }
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li></ul>

步骤3~5,主要对获取到的Service Id进行判断和存储
步骤6: ImsServiceSub.java的getServiceId()方法中,主要就是获取Service Id和向modem获取IMS的注册状态。

<code class="hljs java has-numbering">    <span class="hljs-javadoc">/**
     * Creates/updates the tracker object for the service class
     *<span class="hljs-javadoctag"> @param</span> serviceClass a service class specified in {@link ImsServiceClass}
     *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
     *<span class="hljs-javadoctag"> @param</span> incomingCallPendingIntent When an incoming call is received,
     *        the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
     *        send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
     *        as the result code and the intent to fill in the call ID; It cannot be null
     *<span class="hljs-javadoctag"> @param</span> listener To listen to IMS registration events; It cannot be null
     *<span class="hljs-javadoctag"> @return</span> Unique identifier
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getServiceId</span>(<span class="hljs-keyword">int</span> serviceClass, PendingIntent intent,
            IImsRegistrationListener listener) {
        <span class="hljs-keyword">int</span> serviceId = <span class="hljs-number">0</span>; <span class="hljs-comment">// O is not used - boundary value between error and correct value</span>
        <span class="hljs-comment">//如果serviceClass不是1</span>
        <span class="hljs-keyword">if</span>(serviceClass != ImsServiceClass.MMTEL) {
            <span class="hljs-comment">//则返回-2,service Id为负数说明出错了。</span>
            serviceId = CODE_SERVICE_CLASS_NOT_SUPPORTED;
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">//先尝试从Map集合中获取ImsServiceClassTracker 对象</span>
            ImsServiceClassTracker tracker = mTrackerTable.get(<span class="hljs-keyword">new</span> Integer(serviceClass));
            <span class="hljs-comment">//获取不到</span>
            <span class="hljs-keyword">if</span> (tracker == <span class="hljs-keyword">null</span>) {
                <span class="hljs-comment">//创建ImsServiceClassTracker对象</span>
                tracker = <span class="hljs-keyword">new</span> ImsServiceClassTracker(serviceClass, intent, listener, mCi,
                        mContext, <span class="hljs-keyword">this</span>);
                tracker.updateFeatureCapabilities(isVideoSupported(), isVoiceSupported());
                <span class="hljs-comment">//将ImsServiceClassTracker对象放入Map集合中</span>
                mTrackerTable.put(<span class="hljs-keyword">new</span> Integer(serviceClass), tracker);
                mServiceIdTable.put(<span class="hljs-keyword">new</span> Integer(tracker.getServiceId()), tracker);
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-comment">//更新ImsServiceClassTracker对象的内容</span>
                tracker.mIncomingCallIntent = intent;
                tracker.mRegListener = listener;
            }
            createFeatureCapabilityCallBackThread(listener);
            <span class="hljs-comment">//获取到service Id </span>
            serviceId = tracker.getServiceId();
        }
        Log.d(LOG_TAG, <span class="hljs-string">"getServiceId returns "</span> + serviceId);
        <span class="hljs-comment">//向modem获取IMS的注册状态</span>
        mCi.getImsRegistrationState(mHandler.obtainMessage(EVENT_IMS_STATE_DONE));
        <span class="hljs-keyword">return</span> serviceId;
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li></ul>

modem返回IMS的注册状态信息

<code class="language-java hljs  has-numbering">        <span class="hljs-annotation">@Override</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleMessage</span>(Message msg) {
            .....
                <span class="hljs-keyword">case</span> EVENT_IMS_STATE_DONE:
                    ar = (AsyncResult) msg.obj;
                    <span class="hljs-comment">//对状态的处理</span>
                    handleImsStateChanged(ar);
                    <span class="hljs-keyword">break</span>;
            .....
        }

        <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleImsStateChanged</span>(AsyncResult ar) {
            log(<span class="hljs-string">"handleImsStateChanged"</span>);
            <span class="hljs-comment">//注册失败的错误码</span>
            <span class="hljs-keyword">int</span> errorCode = ImsReasonInfo.CODE_UNSPECIFIED;
            <span class="hljs-comment">//注册失败的消息,如REG99-unable to connect</span>
            String errorMessage = <span class="hljs-keyword">null</span>;
            <span class="hljs-comment">//注册状态有三种,分别是REGISTERED、NOT_REGISTERED、REGISTERING。</span>
            <span class="hljs-keyword">int</span> regState = ImsQmiIF.Registration.NOT_REGISTERED;
            <span class="hljs-comment">//LTE、WIFI或者IWLAN,默认是UNKNOWN</span>
            <span class="hljs-keyword">int</span> imsRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
            <span class="hljs-keyword">if</span> (ar != <span class="hljs-keyword">null</span> && ar.exception == <span class="hljs-keyword">null</span> && ar.result <span class="hljs-keyword">instanceof</span> ImsQmiIF.Registration) {
                ImsQmiIF.Registration registration = (ImsQmiIF.Registration) ar.result;

                errorCode = registration.hasErrorCode() ? registration.getErrorCode()
                        : ImsReasonInfo.CODE_UNSPECIFIED;
                errorMessage = registration.hasErrorMessage() ? registration
                        .getErrorMessage() : <span class="hljs-keyword">null</span>;
                regState = registration.hasState() ? registration.getState()
                        : ImsQmiIF.Registration.NOT_REGISTERED;
                imsRat = getRilRadioTech(registration);
                <span class="hljs-keyword">if</span> (regState != ImsQmiIF.Registration.NOT_REGISTERED) {
                    mCi.queryServiceStatus(mHandler.obtainMessage(EVENT_GET_SRV_STATUS));
                }
            } <span class="hljs-keyword">else</span> {
                loge(<span class="hljs-string">"handleImsStateChanged error"</span>);
            }

            <span class="hljs-comment">//创建ImsReasonInfo对象,传递到UI的话可以通过状态栏的形式提醒用户</span>
            ImsReasonInfo imsReasonInfo = <span class="hljs-keyword">new</span> ImsReasonInfo(
                    ImsReasonInfo.CODE_REGISTRATION_ERROR,
                    errorCode, errorMessage);
            <span class="hljs-keyword">for</span> (Map.Entry<Integer, ImsServiceClassTracker> e : mTrackerTable.entrySet()) {
                <span class="hljs-comment">//创建新线程,把状态信息传递给ImsPhoneCallTracker</span>
                createRegCallBackThread(e.getValue().mRegListener, regState, imsReasonInfo, imsRat);
            }
        }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li></ul>

最终IMS的注册信息会传递到ImsPhoneCallTracker.java中

<code class="hljs java has-numbering">    <span class="hljs-javadoc">/**
     * Listen to the IMS service state change
     *
     */</span>
    <span class="hljs-keyword">private</span> ImsConnectionStateListener mImsConnectionStateListener =
        <span class="hljs-keyword">new</span> ImsConnectionStateListener() {
        <span class="hljs-annotation">@Override</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onImsConnected</span>() {
            <span class="hljs-keyword">if</span> (DBG) log(<span class="hljs-string">"onImsConnected"</span>);
            <span class="hljs-comment">//更新Service的状态</span>
            mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
            <span class="hljs-comment">//把IMSPhone的mImsRegistered值设置成true</span>
            mPhone.setImsRegistered(<span class="hljs-keyword">true</span>);
        }
        ......
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul>

谷歌原生的流程到这里就结束了,ImsPhoneCallTracker.java还处于Framework层,如果想把IMS的注册状态上报给APP层,还需自己添加代码。比如添加并实现mPhone.notifyIMSRegisterStateChanged();
假如IMS已注册上,并且VoLTE又是Enable的话,那么插入某些运营商(如T-mobile)的SIM卡就可以拨打VoLTE Call。

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
怎样防止App在后台运行,点击App桌面的图标重新启动?            在项目中,遇到一个问题百思不得其解,那就是:我在app使用过程中,点击了home键,然后去看看微信之类的其他应用,这个时候再点击app桌面的图标,这个时候app是重新启动的,而不是从上次停止的界面开始的。            对于上面的情况,我觉得既然我的app已经在后台还运行着,为什么就不能继续重上一个界面继续运行,非得从新运行呢。然后我就去查资料解决了这个问题。首先讲讲这个现象的本质。            原因:当点击
1、ZIP文件目录遍历简介 因为ZIP压缩包文件中允许存在“../”的字符串,攻击者可以利用多个“../”在解压时改变ZIP包中某个文件的存放位置,覆盖掉应用原有的文件。如果被覆盖掉的文件是动态链接so、dex或者odex文件,轻则产生本地拒绝服务漏洞,影响应用的可用性,重则可能造成任意代码执行漏洞,危害用户的设备安全和信息安全。比如近段时间发现的“寄生兽”漏洞、海豚浏览器远程命令执行漏洞、三星默认输入法远程代码执行漏洞等都与ZIP文件目录遍历有关。 阿里聚安全的应用漏洞扫描服务,可以检测出应用的ZIP文

浅谈Android中的MVP - 2016-06-24 17:06:24

转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/51745798 本文出自: 【顾林海的博客】 前言 为什么使用MVP,网上有很多说法,最主要就是减轻了Activity的责任,相比于MVC中的Activity承担的责任太多,因此有必要讲讲MVP。 MVP入门 在MVC框架中,View是可以直接读取Model模型中的数据的,Model模型数据发生改变是会通知View数据显示发生相应的改变。而在MVP中Model和View之间的没有
阅读此文前请先阅读 Retrofit+okhttp网络框架介绍 从上文中我们已经了解通过如下代码即可得到返回给我们call 以及 response对象,今天我们通过源码来分析这个过程是如何实现的。 /** * 获取天气数据 * @param cityname * @param key * @return */ @GET ( "/weather/index" ) CallWeatherData getWeatherData( @Query ( "format" ) String format, @Query
一、Block 的类型 根据 Block 在内存中的位置分 为三种类型 NSGlobalBlock , NSStackBlock, NSMallocBlock 。 NSGlobalBlock :类似函数,位于 text 段; NSStackBlock :位于 栈内存,函数返回后 Block 将无效; NSMallocBlock :位于堆内存。 二、Block 的 copy 、 retain 、 release 操作   不同于 NSObjec 的 copy 、 retain 、 release 操作: B
前言 module 怎能少得了动画呢~ 代码解读 weex code API 接口 transition (node, options, callback) Arguments 参数node(Node):将要动画的元素。options( object ):操作选项styles( object ):指定要应用的过渡效果的样式的名称和值。color( string ):色彩的元素时,animaiton完成。transform( object ):变换函数被应用到元素。支持下列值。translate/ tran
目录 概述 这是一个关于 RecycleView 滑动事件的辅助类,该辅助类可以检测 RecycleView 滑动到顶部或者底部的状态. 可用于实现 RecycleView 加载更多或者刷新(虽然刷新可以直接用 SwipeRefreshLayout ).也可用于某些滑动相关的需求,如 FloatingActionButton 的隐藏与显示之类的. 关于 RecycleView 的滑动监听 RecycleView 本身已经提供了滑动的监听接口, OnScrollListener ,这个接口包含了以下的方法.
这个小案例建议在手机上运行。 package com.example.camera;import java.io.File;import android.net.Uri;import android.os.Bundle;import android.os.Environment;import android.provider.MediaStore;import android.app.Activity;import android.content.Intent;import android.view.Me
简介 NSScanner是一个类,用于在字符串中扫描指定的字符,尤其是把它们翻译/转换为数字和别的字符串。可以在创建NSScaner时指定它的string属性,然后scanner会按照你的要求从头到尾地扫描这个字符串的每个字符。 NSScanner官方文档 NSScanner类是一个类簇的抽象父类,该类簇为一个从NSString对象扫描值的对象提供了程序接口。 NSScanner对象把NSString 对象的的字符解释和转化成 number和string 类型的值。在创建NSScanner对象的时候为它分
目录 概述 StickHeaderItemDecoration 是用于显示固定头部的item装饰类,扩展来自系统的 ItemDecoration .本文参考了一部分 sticky-headers-recyclerview 原理 绘制头部 固定头部的 ItemDecoration 本质是在 RecycleView 上覆盖一个界面.该界面没有随着滑动变动所以看起来就像一个固定的头部. 绘制item间隔 ItemDecoration 也可以实现每个item之间的间隔的绘制(比如分隔线之类的),这种情况下就不是在