1. 程式人生 > >Android 4.4 Kitkat Phone工作流程淺析(七)__來電(MT)響鈴流程

Android 4.4 Kitkat Phone工作流程淺析(七)__來電(MT)響鈴流程

本文來自http://blog.csdn.net/yihongyuelan 轉載請務必註明出處
本文程式碼以MTK平臺Android 4.4為分析物件,與Google原生AOSP有些許差異,請讀者知悉。

前置文章:

Android 4.4 Kitkat Phone工作流程淺析(一)__概要和學習計劃

概述

        Android 4.4對於響鈴流程有所改動,把響鈴觸發放到了TeleService中,這也符合4.4 Phone的設計風格即UI和Logic分離。當來電流程發起時,抓取radio_log進行分析後,可以看到整個來電過程狀態改變如下:

  1. handleMessage (EVENT_VOICE_CALL_INCOMING_INDICATION) //設定voice call的標誌  
  2. handleMessage (EVENT_NEW_RINGING_CONNECTION)         //新來電標誌  
  3. handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)     //狀態改變  
  4. handleMessage (EVENT_INCOMING_RING)                  //響鈴  
  5. handleMessage (EVENT_CRSS_SUPP_SERVICE_NOTIFICATION) //收到AT指令CLIP後,更新通話資訊(CLIP即來電顯示作用)  
  6. handleMessage (EVENT_INCOMING_RING)  
  7. handleMessage (EVENT_CRSS_SUPP_SERVICE_NOTIFICATION)  
  8. handleMessage (EVENT_INCOMING_RING)  
  9. handleMessage (EVENT_CRSS_SUPP_SERVICE_NOTIFICATION)  
  10. handleMessage (EVENT_INCOMING_RING)  
  11. handleMessage (EVENT_CRSS_SUPP_SERVICE_NOTIFICATION)  
  12. handleMessage (EVENT_INCOMING_RING)  
  13. handleMessage (EVENT_CRSS_SUPP_SERVICE_NOTIFICATION)  
  14. handleMessage (EVENT_DISCONNECT)                     //斷開連線  
  15. handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)     //狀態改變  
  16. handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)  
這些log是CallManager中打印出來的,當然我們也可以在RIL.java中去檢視對應的log。

當Modem側收到來電訊息後所做的操作如下

1. 根據來電型別設定INCOMING_INDICATION;

2. 發起NEW_RINGING_CONNECTION,新來電標誌

3. 觸發CALL_STATE_CHANGED狀態,狀態改變促使介面更新;

4. 發起響鈴通知INCOMING_RING,響鈴流程由此發起;

5. 根據CLIP返回資訊觸發CRSS_SUPP_SERVICE_NOTIFICATION,這是由MTK加入的,其作用是根據CLIP的返回更新Call的資訊;

6. 迴圈上面4和5步,持續響鈴;

7. 斷開連線DISCONNECT,本次MT流程結束(未接聽);

8. 觸發CALL_STATE_CHANGED狀態,狀態改變促使介面更新;

Telephony Framework處理響鈴事件

        當來電訊息到來時,經過Modem以及RILD的處理後便傳遞到Telephony framework中,並由RILJ繼續發起。流程圖如下:

RILJ

        在MT流程發起後,會有相關的unsolicited資訊反饋到RILJ中,這裡主要關注響鈴流程(ringing_flow),因此可以找到以下AT指令返回的log資訊:

  1. 01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: +CRING: VOICE  
  2. 01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT:   
  3. 01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: +CLIP: "13800138000",0,"",0,"",0  
  4. 01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT:   
  5. 01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: +ECPI: 1,4,0,1,1,0,"13800138000",129,""  
  6. 01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: AT< +CRING: VOICE  
  7. 01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: RIL_URC_READER:+CRING: VOICE  
  8. 01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: RIL_URC_READER Enter processLine  
  9. 01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-RIL: Nw URC:+CRING: VOICE  
  10. 01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-RIL: receiving RING!!!!!!  
  11. 01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-RIL: receiving first RING!!!!!!  

整個radio_log中每隔3877ms便會列印一次+CRING: VOICE的log,通話結束通話前一共有5次。根據AT返回資訊可以知道,來電型別為Voice,並且是一次響鈴事件。緊接著在RILJ中收到了與之對應的事件RIL_UNSOL_CALL_RING:

  1. 01-01 02:46:02.155 1443 1837 D RILJ : RIL(2) :[UNSL RIL]< UNSOL_CALL_RING [[email protected]  
這裡是UnSolicited事件,觸發processUnsolicited方法,並執行到以下程式碼處:
  1. case RIL_UNSOL_CALL_RING:  
  2.     if (RILJ_LOGD) unsljLogRet(response, ret);  
  3.     if (mRingRegistrant != null) {  
  4.     //觀察者模式
  5.         mRingRegistrant.notifyRegistrant(  
  6.                 new AsyncResult (null, ret, null));  
  7.     }  
前面的文章中我們已經分析過Registrant這種觸發模式,這裡再次分析下notifyRegistrant()方法觸發後的跳轉地點。

首先檢視到mRingRegistrant的定義在SourceCode/frameworks/opt/telephony/src/java/com/android/internal/telephony/BaseCommands.java中,且賦值在setOnCallRing方法中,如下:

  1. @Override
  2. publicvoid setOnCallRing(Handler h, int what, Object obj) {  
  3.     mRingRegistrant = new Registrant (h, what, obj);  
  4. }  
這裡的setOnCallRing方法在SourceCode/frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneBase.java的構造方法中呼叫,如下:
  1. mCi.setOnCallRing(this, EVENT_CALL_RING, null);  
mCi是CommandsInterface的物件,而BaseCommands implements CommandsInterface,也就是說在PhoneBase的構造方法被呼叫時,就應經完成了對mRingRegistrant的賦值。
需要注意兩點:

(1). PhoneBase extends Handler,因此setOnCallRing中的this即代表了PhoneBase本身。換句話說,當觸發notifyRegistrant時,便會回撥到PhoneBase的handleMessage中;

(2). setOnCallRing中的EVENT_CALL_RING表示當觸發notifyRegistrant時,回撥handleMessage中的相應case;

mRingRegistrant註冊流程


        當Phone第一次啟動時便會執行PhoneFactory中的makeDefaultPhone()方法,用於完成Phone的初始化,在初始化過程中即完成了mRingRegistrant的註冊。

PhoneBase

        當執行mRingRegistrant.notifyRegistrant()方法後,跳轉到PhoneBase的handleMessage方法中,程式碼如下:
  1. case EVENT_CALL_RING:  
  2.     ar = (AsyncResult)msg.obj;  
  3.     if (ar.exception == null) {  
  4.         PhoneConstants.State state = getState();  
  5.         if ((!mDoesRilSendMultipleCallRing)  
  6.                 && ((state == PhoneConstants.State.RINGING) ||  
  7.                         (state == PhoneConstants.State.IDLE))) {  
  8.             mCallRingContinueToken += 1;  
  9.             sendIncomingCallRingNotification(mCallRingContinueToken);  
  10.         } else {  
  11.             //執行這裡
  12.             notifyIncomingRing();  
  13.         }  
  14.     }  
  15.     break;  
這裡繼續檢視notifyIncomingRing()方法:
  1. privatevoid notifyIncomingRing() {  
  2.     if (!mIsVoiceCapable)  
  3.         return;  
  4.     AsyncResult ar = new AsyncResult(nullthisnull);  
  5.     mIncomingRingRegistrants.notifyRegistrants(ar);  
  6. }  
同樣使用了觀察者模式,查詢相應的registerXXX方法,如下:
  1. @Override
  2. publicvoid registerForIncomingRing(  
  3.         Handler h, int what, Object obj) {  
  4.     checkCorrectThread(h);  
  5.     mIncomingRingRegistrants.addUnique(h, what, obj);  
  6. }  
可以找到在CallManager的registerForPhoneStates方法中,呼叫了IncomingRing的register方法:
  1. if (FeatureOption.MTK_GEMINI_SUPPORT == true && !(phone instanceof SipPhone)) {  
  2.     if(phone instanceof GeminiPhone) {  
  3.         int offset;        <span style="white-space:pre">   </span>  
  4.         int count = (MAXIMUM_SIM_COUNT < PhoneConstants.GEMINI_SIM_NUM) ? MAXIMUM_SIM_COUNT : PhoneConstants.GEMINI_SIM_NUM;  
  5.         Phone targetPhone;  
  6.         for (int i = 0; i < count; i++) {  
  7.             offset = i * NOTIFICATION_ID_OFFSET;  
  8.             targetPhone = ((GeminiPhone)phone).getPhonebyId(PhoneConstants.GEMINI_SIM_1 + i);      
  9.             //... ...省略
  10.             targetPhone.registerForIncomingRing(mHandler, EVENT_INCOMING_RING + offset, null);  
這裡涉及到MTK的雙卡機制,同時可能大家會有疑惑如何斷定是這裡呼叫的呢?我們可以反向思考,當發現這裡呼叫之後,我們反過來看是哪裡呼叫了registerForPhoneStates,然後依次檢視。最終我們可以看到這些都是在Phone初始化時候順序呼叫的,我們只是反過來查詢而已。

通過上面CallManager中的程式碼可以知道:

(1). mHandler在CallManager中定義,相關handleMessage即可找到;

(2). case對應事件為:EVENT_INCOMING_RING;

mIncomingRingRegistrants註冊流程


        mIncomingRingRegistrants的註冊流程始於Phone啟動並初始化時,需要關注的一點是CallManager中的registerForPhoneStates()方法。為什麼這裡直接從CallManager跳轉到PhoneBase呢?實際上targetPhone物件是通過PhoneProxy傳遞過來的,而PhoneProxy是GSMPhone和CDMAPhone的代理,GSMPhone和CDMAPhone都繼承自PhoneBase,最終的實現也在PhoneBase中,這裡省略了部分跳轉,請讀者知悉。

CallManager

        根據前面的分析,在PhoneBase的notifyIncomingRing()方法中會呼叫mIncomingRingRegistrants.notifyRegistrants()方法,可以找到在CallManager中對應的handleMessage方法以及對應的處理事件EVENT_INCOMING_RING:

  1. @Override
  2. publicvoid handleMessage(Message msg) {  
  3.     int index;  
  4.     switch (msg.what) {  
  5.         //... ...省略
  6.         case EVENT_INCOMING_RING:  
  7.         case EVENT_INCOMING_RING + NOTIFICATION_ID_OFFSET:  
  8.         case EVENT_INCOMING_RING + (NOTIFICATION_ID_OFFSET * 2):  
  9.         case EVENT_INCOMING_RING + (NOTIFICATION_ID_OFFSET * 3):  
  10.             if (!hasActiveFgCall()) {  
  11.                index = (msg.what - EVENT_INCOMING_RING) / NOTIFICATION_ID_OFFSET;  
  12.                mIncomingRingRegistrantsGemini[index].notifyRegistrants((AsyncResult) msg.obj);  
  13.                mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj);  
  14.             }  
  15.         break;  
看到這裡繼續檢視mIncomingRingRegistrantsGemini和mIncomingRingRegistrants的registerXXX方法,程式碼如下:
  1. //mIncomingRingRegistrantsGemini的registe方法
  2. publicvoid registerForIncomingRingEx(Handler h, int what, Object obj, int simId){  
  3.       int index = getRegistrantsArrayIndex(simId);  
  4.       if (index != -1) {  
  5.         mIncomingRingRegistrantsGemini[index].addUnique(h, what, obj);  
  6.     }  
  7. }  
  8. //mIncomingRingRegistrants的registe方法
  9. publicvoid registerForIncomingRing(Handler h, int what, Object obj){  
  10.     mIncomingRingRegistrants.addUnique(h, what, obj);  
  11. }  
以上方法會根據手機制式來呼叫,如果是雙卡則呼叫Gemini,在CallManagerWrapper中可以找到:
  1. publicstaticvoid registerForIncomingRing(Handler handler, int what, Object obj) {  
  2.     if (GeminiUtils.isGeminiSupport()) {  
  3.         finalint[] geminiSlots = GeminiUtils.getSlots();  
  4.         for (int geminiSlot : geminiSlots) {  
  5. <span style="white-space:pre">  </span>    //雙卡
  6.             CallManager.getInstance().registerForIncomingRingEx(handler, what, obj,  
  7.                     geminiSlot);  
  8.         }  
  9.     } else {  
  10. <span style="white-space:pre">  </span>//單卡
  11.         CallManager.getInstance().registerForIncomingRing(handler, what, obj);  
  12.     }  
  13. }  
而以上方法在CallManagerWrapper中還經過了一層包裝:
  1. publicstaticvoid registerForIncomingRing(Handler handler, int what) {  
  2.     registerForIncomingRing(handler, what, null);  
  3. }  
那麼後續呼叫是在哪裡呢?我們可以在CallStateMonitor的registerForNotifications()方法中找到:
  1. CallManagerWrapper.registerForIncomingRing(this, PHONE_INCOMING_RING);  
而registerForNotifications()方法在CallStateMonitor初始化以及Radio狀態改變的時候會呼叫。

通過以上分析我們可以知道:

(1). 在CallStateMonitor中註冊了來電響鈴回撥,也就是這裡的this。CallStateMonitor繼承自Handler,那麼CallManager中的notifyRegistrants()方法會跳轉到CallStateMonitor中;

(2). 對應的case事件為:PHONE_INCOMING_RING;

mIncomingRingRegistrantsGemini註冊流程

        整個註冊流程是從TeleService啟動時開始的,TeleService有監聽BOOT_COMPLETE的廣播,在隨機啟動之後便開始了整個註冊流程。在第8步需要注意,雙卡執行CallManager.getInstance().registerForIncomingRingEx(),單卡則執行CallManager.getInstance().registerForIncomingRing()。

TeleService處理響鈴

        當Telephony Framework處理完響鈴事件之後,會將響鈴事件上報到CallStateMonitor中,並最終在TeleService中發起響鈴操作,流程圖如下:

CallStateMonitor

        經過framework的層層處理之後,響鈴事件傳遞到了TeleService的CallStateMonitor中。通過前面的分析可以知道,在CallManager的handleMessage中,通過mIncomingRingRegistrantsGemini[index].notifyRegistrants()方法跳轉到了CallStateMonitor的handleMessage中,如下:
  1. @Override
  2. publicvoid handleMessage(Message msg) {  
  3.     for (Handler handler : registeredHandlers) {  
  4.         handler.handleMessage(msg);  
  5.     }  
  6. }  
這裡會根據registerHandler觸發對應的回撥,在前面來電(MT)流程的分析過程有看到,CallNotifier和CallModeler註冊了CallStateMonitor的Handler回撥,但通過進一步分析後發現,只有CallNotifier中才處理了PHONE_INCOMING_RING的事件,所以接著我們需要檢視CallNotifier對響鈴事件的處理。

CallNotifier

        在CallNotifier的handleMessage方法中找到PHONE_INCOING_RING的處理事件如下:
  1. @Override
  2. publicvoid handleMessage(Message msg) {  
  3.     //... ...省略
  4.     case CallStateMonitor.PHONE_INCOMING_RING:  
  5.         log("PHONE_INCOMING_RING !");  
  6.         if (msg.obj != null && ((AsyncResult) msg.obj).result != null) {  
  7.                 //... ...省略
  8.                 if (provisioned && !isRespondViaSmsDialogShowing()) {  
  9.                     mRinger.ring();  
  10.                 }  
  11.             } else {  
  12.                 if (DBG) log("RING before NEW_RING, skipping");  
  13.             }  
  14.         }  
  15.     break;  
通過這裡會呼叫Ringer的ring()方法,從而開始響鈴。

Ringer

        到這裡就應該準備播放鈴聲了,但ring()方法中還有一系列事情需要處理:
  1. void ring() {  
  2.     synchronized (this) {  
  3.         //... ...省略
  4.         //建立Looper執行緒,用於播放/停止鈴聲,通過handleMessage接收播放/停止請求
  5.         makeLooper();  
  6.         //如果是第一次播放則mFirstRingEventTime = -1
  7.         if (mFirstRingEventTime < 0) {  
  8.         //這裡獲取系統開機後經過的時間,包括休眠
  9.             mFirstRingEventTime = SystemClock.elapsedRealtime();  
  10.             if (mRingHandler != null) {  
  11.             //發起播放鈴聲的請求
  12.                 mRingHandler.sendEmptyMessage(PLAY_RING_ONCE);  
  13.             }  
  14.         } else {  
  15.             //如果不是第一次播放
  16.             if (mFirstRingStartTime > 0) {  
  17.                 if (mRingHandler != null) {  
  18.      //延遲傳送播放請求,延遲時間為第一次啟動播放時間減去第一次傳送PLAY_RING_ONCE的時間(133ms)
  19.                     mRingHandler.sendEmptyMessageDelayed(PLAY_RING_ONCE,  
  20.                         mFirstRingStartTime - mFirstRingEventTime);  
  21.                 }  
  22.             } else {  
  23.                 mFirstRingEventTime = SystemClock.elapsedRealtime();  
  24.             }  
  25.         }  
  26.     }  
  27. }  
在這裡就要特別注意了,通過makeLooper()方法建立了一個Looper執行緒,用於播放/停止鈴聲,那這裡為什麼要用Looper呢? 後面通過sendEmptyMessage()和sendEmptyMessageDelayed()方法發起播放鈴聲的請求,接下來分析一下makeLooper()的構造以及使用Looper的緣由。
        makeLooper()方法主要建立一個ringer的執行緒,用於播放/停止鈴聲,程式碼如下:
  1. privatevoid makeLooper() {  
  2.     //如果第一響鈴mRingThread==null
  3.     if (mRingThread == null) {  
  4.         //Worker實現了Runnable介面,在其構造方法Worker(String name)
  5.         //中建立並啟動了名為"ringer"的工作執行緒
  6.         mRingThread = new Worker("ringer");  
  7.         //若還未獲取到ringer執行緒的Looper物件則返回
  8.         if (mRingThread.getLooper() == null) {  
  9.             return ;  
  10.         }  
  11.         //建立Handler並依附於ringer執行緒的Looper物件
  12.         mRingHandler = new Handler(mRingThread.getLooper()) {  
  13.             @Override
  14.             publicvoid handleMessage(Message msg) {  
  15.                 Ringtone r = null;  
  16.                 switch (msg.what) {  
  17.                     case PLAY_RING_ONCE:  
  18.                         if (DBG) log("mRingHandler: PLAY_RING_ONCE...");  
  19.                         if (mRingtone == null && !hasMessages(STOP_RING)) {  
  20.                             // create the ringtone with the uri
  21.                             if (DBG) log("creating ringtone: " + mCustomRingtoneUri);  
  22.                             r = RingtoneManager.getRingtone(mContext, mCustomRingtoneUri);  
  23.                             synchronized (Ringer.this) {  
  24.                                 if (!hasMessages(STOP_RING)) {  
  25.                                     mRingtone = r;  
  26.                                 }  
  27.                             }  
  28.                         }  
  29.                         r = mRingtone;  
  30.                         if (r != null && !hasMessages(STOP_RING) && !r.isPlaying()) {  
  31.                             PhoneLog.d(LOG_TAG, "play ringtone... ");  
  32.                             PhoneUtils.setAudioMode();  
  33.                             //播放鈴聲
  34.                             r.play();  
  35.                             synchronized (Ringer.this) {  
  36.                //將第一次播放時間(開機後的時間包括休眠)賦值給mFirstRingStartTime
  37.                                 if (mFirstRingStartTime < 0) {  
  38.                                     mFirstRingStartTime = SystemClock.elapsedRealtime();  
  39.                                 }  
  40.                             }  
  41.                         }  
  42.                         break;  
  43.                     case STOP_RING:  
  44.                         if (DBG) log("mRingHandler: STOP_RING...");  
  45.                         r = (Ringtone) msg.obj;  
  46.                         if (r != null) {  
  47.                             //停止播放鈴聲
  48.                             r.stop();  
  49.                         } else {  
  50.                             if (DBG) log("- STOP_RING with null ringtone!  msg = " + msg);  
  51.                         }  
  52.                         //退出Looper迴圈
  53.                         getLooper().quit();  
  54.                         break;  
makeLooper()主要做了三件事: (1). 建立並啟動名為ringer的Looper執行緒; (2). 例項化mHandler並依附於ringer的Looper物件; (3). 接收並處理播放/停止鈴聲的Message; 可以看到makeLooper做了相當重要的事情,特別是對於第三點,後續處理播/停止鈴聲便在handleMessage中。 接下來需要看看Looper,我們看到Worker類的實現,如下:
  1. privateclass Worker implements Runnable {  
  2.     //建立mLock鎖
  3.     privatefinal Object mLock = new Object();  
  4.     private Looper mLooper;  
  5.     Worker(String name) {  
  6.     //建立並啟動名為"name"的執行緒
  7.         Thread t = new Thread(nullthis, name);  
  8.         t.start();  
  9.         synchronized (mLock) {  
  10.             while (mLooper == null) {  
  11.                 try {  
  12.                 //阻塞直到前面的"name"執行緒已成功執行(執行了run方法),最大阻塞時間5s
  13.                     mLock.wait(5000);  
  14.                 } catch (InterruptedException ex) {  
  15.                 }     
  16.             }     
  17.         }     
  18.     }  
  19.     public Looper getLooper() {  
  20.     //返回"name"執行緒的Looper物件
  21.         return mLooper;  
  22.     }  
  23.     publicvoid run() {  
  24.         synchronized (mLock) {  
  25.         //啟用Looper
  26.             Looper.prepare();  
  27.         //返回當前執行緒(也就是這裡的子執行緒"name")的Looper物件
  28.             mLooper = Looper.myLooper();  
  29.         //喚醒鎖,不再阻塞
  30.             mLock.notifyAll();  
  31.         }  
  32.     //開啟Looper迴圈
  33.         Looper.loop();  
  34.     }  
  35.     publicvoid quit() {  
  36.     //退出Looper迴圈
  37.         mLooper.quit();  
  38.     }     
  39. }  
Looper用於線上程中開啟訊息迴圈,普通執行緒預設是沒有訊息迴圈的,線上程中通過呼叫Looper.prepare()開啟訊息迴圈,通過Looper.loop()處理訊息迴圈直到執行緒迴圈終止,我們可以呼叫Looper的quit()方法退出迴圈。 (關於Looper官方解釋,StackOverFlow上的回答,或者檢視CSDN網友分析)         回到前面的問題,這裡為什麼要使用Looper呢?使用執行緒來播放鈴聲,同時又需要根據不同的狀態,停止鈴聲的播放,也就是說當持續需要執行緒來做某件事情,同時又想控制該事情的啟動與停止,這便可以使用Looper。         當第一次收到響鈴請求時,開啟執行緒啟動Loop,並播放鈴聲。通過對Radio_log的分析,RILJ每隔3.877s收到一次響鈴請求,此時上報並傳到到TeleService中,如果此時鈴聲已經播放完畢,那麼則繼續執行ringtone.play()方法啟動鈴聲的播放,如果之前的鈴聲並未播放完畢,則不會再次啟動ringtone.play()方法。如果此時對方/己方結束通話了來電,那麼響鈴需要立即終止,此時只需在子執行緒中處理STOP_RING方法即可。

總結

        整個響鈴流程跟來電(MT)流程類似,但響鈴並未涉及到介面的變換,在TeleService中呼叫到media中的ringtone.play()方法進行鈴聲的播放。特別需要注意的是整個過程中,大量的使用了觀察者模式,最後使用Looper執行緒來做鈴聲播放/停止操作。        文中涉及的時序圖資源免積分下載,戳這裡。 整個流程圖如下:

相關推薦

Android 4.4 Kitkat Phone工作流程淺析()__來電(MT)響鈴流程

本文來自http://blog.csdn.net/yihongyuelan 轉載請務必註明出處 本文程式碼以MTK平臺Android 4.4為分析物件,與Google原生AOSP有些許差異,請讀者知悉。 前置文章: 《Android 4.4 Kitkat Phone工作流

Android 4.4 Kitkat Phone工作流程淺析(一)__概要和學習計劃

        美國時間 2013 年 10 月 31 日, Google 正式釋出了全新版本的移動作業系統 Android 4.4 KitKat 。據 google官方介紹,Android 4.4 降低了硬體的需求,提高了程式執行效率。距離 4.4 釋出已經過去3個月了,

Android Studio(4)---開發人員工作流程基礎

開發人員工作流程基礎 開發Android應用程式的工作流程在概念上與其他應用程式平臺相同。但是,要有效地為Android構建精心設計的應用程式,您需要一些專門的工具。以下列表概述了構建Android應用程式的過程,幷包含您在開發的每個階段應使用的一些Android Stud

GetPathFromUri4kitkat【Android 4.4 kitkat以上及以下根據uri獲取路徑的方法】

under als providers textview href 數據權限 res activit nload 版權聲明:本文為博主原創文章,未經博主允許不得轉載。 前言 在Android4.4之前和之後,通過Intent調用文件管理器選擇文件,獲取的文件uri地址形

Nexus 5 Android L 使用感受,以及如何刷回 4.4 Kitkat

看了Google I/O後,對Material Design,電池省電模式等感到非常好奇,於是決定刷Android L 爽一把。 比較讓人討厭的是每次刷機後,之前裝的app的資料,都會被erase掉,還得重灌一遍。 好奇心趨勢吧,終於刷了Android L。刷機過程參考Google官方網

s5p4418 Android 4.4.2 驅動層 HAL層 服務層 應用層 開發流程記錄(一 硬體驅動層)

歡迎轉載,務必註明出處:http://blog.csdn.net/wang_shuai_ww/article/details/44303069 本文章是記錄Android開發中驅動層、HAL層、應用層之間的關係,以及其開發方法,本文將會以實現LED的控制為例來進行記錄。 一

Android系統載入Apk檔案的時機和流程分析(1)--Android 4.4.4 r1的原始碼

Android系統在啟動時安裝應用程式的過程,這些應用程式安裝好之後,還需要有一個Home應用程式來負責把它們在桌面上展示出來,在Android系統中,這個預設的Home應用程式就是Launcher了。Android系統的Home應用程式Launcher是由Activit

android kitkat4.4以上)各個版本的特性解析

先看下android官網 API Kitkat(4.4 巧克力)的新特性: Lollipop(5.0 棒棒糖)的新特性: MarshMallow(6.0 棉花糖)新特性: Nougat(7.0 牛軋糖)的新特性: 下面我來具體的說一下各個版本的重點更新: Kitka

關於android 4.4簡訊(sms)接收流程-狀態機篇

google從4.4版本開始,為了解決重複接收多條簡訊問題,在簡訊接收的框架層中增加了一個狀態機專門用來接收簡訊。 首先什麼是狀態機,這裡不多說,網上已經有很多相關的文章,這邊引用一個:http://blog.csdn.net/pi9nc/article/details

Android 4.4 自動撥打分機流程分析

Android 自動撥打分機流程分析,現在我們只關注framework層,以CDMA為例,GSM同理。 至於什麼是自動撥打分機,如下圖,輸入一個電話號碼,再選擇“等待時間延長2秒”,就會顯示一個分號,接著就可以輸入分機號碼了 本文來自http://b

[Android]異常4-javax.mail.AuthenticationFailedException

llb src pop set com ava smtp exceptio .net javax.mail.AuthenticationFailedException 背景:JavaMail發送電子郵件 異常原因: 可能一>發件人帳號、密碼有誤 可能二>需要使用

[Android 4.4.2] 泛泰A850 Mokee4.4.2 20140509 RC2.0 by syhost

無線 tails 新的 ble safe 機會 color 替代 them 感謝: tenfar(R大師),zhaochengw(z大)。windxixi(雪狐),xuefy(大星星)。suky, cofface 感謝參考代碼: Cyanogenmod , mar

Android 4.4.2引入的超炫動畫庫

4.5 1.4.1 name api level image orm 基本 aca 概述 概述 Scene Transition TransitionManager 常用API 1.4.1. AutoTransition 1.4.2. Chan

使用VS2017開發APP中使用VUE.js開發遇到打包出來的android文件 在低版本的android4.3)中無法正常使用

vue.js 文件 默認 項目 let ons dir file 開發app 使用VS2017開發VUE的APP應用遇到的問題集合 1, 打包出來的apk文件在Android 6.0版本以上手機可以正常打開,在Android 4.3版本手機上無法打開 原因:一開

[Android 4.4.2] 泛泰A870 Mokee4.4.2 20140531 RC1.0 by syhost

statistic 狀態欄 and 自己 最新版 xixi 方法 新版 網絡 歡迎關註泛泰非盈利專業第三方開發團隊 VegaDevTeam??(本team 由 syhost suky zhaochengw(z大) x

4月18日工作日誌

需求分析 需求 圖片 說明 image ont src alt com 工作內容:   對房渤萱、劉帥二人所做的需求分析文檔進行補充說明,對其中的不足之處進行了改正。   設計了精靈王國的劇情走向圖。 4月18日工作日誌

4.4.2 查詢系統如何工作

4.4.2 查詢系統如何工作 在4.4.4部分中我們將把查詢直譯器的實現程式表示為一系列的程式的組合而成的集合。在這一部分中, 我們給出一個概述,來解釋系統的通用的結構,它是獨立於低層的實現細節的。在描述瞭解釋器的實現 之後,我們將處於一個位置,也就是理解了查詢語言的邏輯操作與數學的邏輯操作的不同之

Android EditText輸入框按銀行賬號格式輸入(4 4 4 4 3)

我們在做輸入框輸入的時候總會遇到各種各樣的格式輸入要求,下面來給大家介紹一個按銀行賬號3 4 4 4 4格式的輸入(5222 2222 2222 2222 222)。就是輸入完前三個數加一個空格,之後每輸入完四個數加一個空格,並控制只能輸入銀行賬號的字元數。下面來看程式碼: xml佈局

Centos7.4別名設定提高工作效率

  一、開啟 .bashrc檔案   1、位置:~(cd ~)目錄下   2、cat .bashrc 原檔案內容如下: # .bashrc # User specific aliases and functions alias rm='rm -i' #別名 alias cp='

MP1584採用貼片8腳封裝。工作電壓4.5-28V,工作頻率1.5MHz,輸出電流3A。

MP1584採用貼片8腳封裝。工作電壓4.5-28V,工作頻率1.5MHz,輸出電流3A。通過在MOS管Q上加上開關訊號PWM,控制開關管的導通與關斷,使電感和電容充放電達到將電源進行降壓的目的。MP1584是具有整合內部高邊高壓功率MOSFET的高頻降壓開關穩壓器。  MP1584,