編輯:關於Android編程
由於Android幾乎所有的代碼都是公開的,如果要對Framework層分析就必需先拿到Framework層的代碼,我在前面已經搭建好了ubuntu14.04的環境,下載好了Android4.0的源碼,其中也包括了Framework層和Package的代碼,導出到宿主機Windows XP中用Source Insight 3.5工具來查看源碼,Package中的代碼可以導入到Eclipse下查看,我是把frameworksase整個目錄都導入到Source Insight 3.5工程中,可以選擇我們需要的目錄導入,如frameworksasemedia estscontentsmedia_api目錄下有很多音、視頻文件,可以只導入frameworksasecore、frameworksase elephony、frameworksaseservices、frameworksaseinclude等目錄。需要代碼的可以到這下載:http://pan.baidu.com/s/1i3KczeX在PhoneApp初始化時,有以下代碼
@Override public void onCreate() { //........... if (phone == null) { // 初始化phone frameworks層 PhoneFactory.makeDefaultPhones(this); // 獲取默認的phone對象 phone = PhoneFactory.getDefaultPhone(); mCM = CallManager.getInstance(); mCM.registerPhone(phone); //............. } //............... }在應用層的PhoneApp中調用PhoneFactory的靜態方法makeDefaultPhones創建一個默認的Phone對象,而framework中采用的是代理模式和工廠模式實現,在makedefaultPhone中
//***** Class Methods public static void makeDefaultPhones(Context context) { makeDefaultPhone(context); } /** * FIXME replace this with some other way of making these * instances */ public static void makeDefaultPhone(Context context) { synchronized(Phone.class) { if (!sMadeDefaults) { sLooper = Looper.myLooper(); sContext = context; if (sLooper == null) { throw new RuntimeException( PhoneFactory.makeDefaultPhone must be called from Looper thread); } int retryCount = 0; for(;;) { boolean hasException = false; retryCount ++; try { // use UNIX domain socket to // prevent subsequent initialization new LocalServerSocket(com.android.internal.telephony); } catch (java.io.IOException ex) { hasException = true; } if ( !hasException ) { break; } else if (retryCount > SOCKET_OPEN_MAX_RETRY) { throw new RuntimeException(PhoneFactory probably already running); } else { try { Thread.sleep(SOCKET_OPEN_RETRY_MILLIS); } catch (InterruptedException er) { } } } sPhoneNotifier = new DefaultPhoneNotifier(); // Get preferred network mode int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE; if (BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE) { preferredNetworkMode = Phone.NT_MODE_GLOBAL; } int networkMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.PREFERRED_NETWORK_MODE, preferredNetworkMode); Log.i(LOG_TAG, Network Mode set to + Integer.toString(networkMode)); // Get cdmaSubscription // TODO: Change when the ril will provides a way to know at runtime // the configuration, bug 4202572. And the ril issues the // RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, bug 4295439. int cdmaSubscription; int lteOnCdma = BaseCommands.getLteOnCdmaModeStatic(); switch (lteOnCdma) { case Phone.LTE_ON_CDMA_FALSE: cdmaSubscription = RILConstants.SUBSCRIPTION_FROM_NV; Log.i(LOG_TAG, lteOnCdma is 0 use SUBSCRIPTION_FROM_NV); break; case Phone.LTE_ON_CDMA_TRUE: cdmaSubscription = RILConstants.SUBSCRIPTION_FROM_RUIM; Log.i(LOG_TAG, lteOnCdma is 1 use SUBSCRIPTION_FROM_RUIM); break; case Phone.LTE_ON_CDMA_UNKNOWN: default: //Get cdmaSubscription mode from Settings.System cdmaSubscription = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION, preferredCdmaSubscription); Log.i(LOG_TAG, lteOnCdma not set, using PREFERRED_CDMA_SUBSCRIPTION); break; } Log.i(LOG_TAG, Cdma Subscription set to + cdmaSubscription); //reads the system properties and makes commandsinterface sCommandsInterface = new RIL(context, networkMode, cdmaSubscription); int phoneType = getPhoneType(networkMode); if (phoneType == Phone.PHONE_TYPE_GSM) { Log.i(LOG_TAG, Creating GSMPhone); sProxyPhone = new PhoneProxy(new GSMPhone(context, sCommandsInterface, sPhoneNotifier)); } else if (phoneType == Phone.PHONE_TYPE_CDMA) { switch (BaseCommands.getLteOnCdmaModeStatic()) { case Phone.LTE_ON_CDMA_TRUE: Log.i(LOG_TAG, Creating CDMALTEPhone); sProxyPhone = new PhoneProxy(new CDMALTEPhone(context, sCommandsInterface, sPhoneNotifier)); break; case Phone.LTE_ON_CDMA_FALSE: default: Log.i(LOG_TAG, Creating CDMAPhone); sProxyPhone = new PhoneProxy(new CDMAPhone(context, sCommandsInterface, sPhoneNotifier)); break; } } sMadeDefaults = true; } } }在PhoneFactory.java類中定義了
static private CommandsInterface sCommandsInterface = null; //reads the system properties and makes commandsinterface sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);所以後面用到的CommandsInterface對象都是RIL類,由於RIL實現了CommandsInterface
在以下代碼中根據不同的類型創建Phone,如GSM(2G中國移動和聯通)、CDMA(中國電信)等,采用了向上轉型,向上轉型是安全的。
int phoneType = getPhoneType(networkMode); if (phoneType == Phone.PHONE_TYPE_GSM) { Log.i(LOG_TAG, Creating GSMPhone); sProxyPhone = new PhoneProxy(new GSMPhone(context, sCommandsInterface, sPhoneNotifier)); } else if (phoneType == Phone.PHONE_TYPE_CDMA) { switch (BaseCommands.getLteOnCdmaModeStatic()) { case Phone.LTE_ON_CDMA_TRUE: Log.i(LOG_TAG, Creating CDMALTEPhone); sProxyPhone = new PhoneProxy(new CDMALTEPhone(context, sCommandsInterface, sPhoneNotifier)); break; case Phone.LTE_ON_CDMA_FALSE: default: Log.i(LOG_TAG, Creating CDMAPhone); sProxyPhone = new PhoneProxy(new CDMAPhone(context, sCommandsInterface, sPhoneNotifier)); break; } }GSMPhone和CDMAPhone都繼承了PhoneBase,以下分析默認創建的是GSMPhone;在GSMPhone.java的構造函數中
// Constructors public GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier) { this(context,ci,notifier, false); } public GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) { super(notifier, context, ci, unitTestMode); if (ci instanceof SimulatedRadioControl) { mSimulatedRadioControl = (SimulatedRadioControl) ci; } mCM.setPhoneType(Phone.PHONE_TYPE_GSM); mCT = new GsmCallTracker(this); //通話管理 mSST = new GsmServiceStateTracker (this); mSMS = new GsmSMSDispatcher(this, mSmsStorageMonitor, mSmsUsageMonitor); mIccFileHandler = new SIMFileHandler(this); mIccRecords = new SIMRecords(this); mDataConnectionTracker = new GsmDataConnectionTracker (this); mIccCard = new SimCard(this); if (!unitTestMode) { mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this); mSimSmsIntManager = new SimSmsInterfaceManager(this, mSMS); mSubInfo = new PhoneSubInfo(this); } mStkService = CatService.getInstance(mCM, mIccRecords, mContext, mIccFileHandler, mIccCard); mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); mCM.registerForOn(this, EVENT_RADIO_ON, null); mCM.setOnUSSD(this, EVENT_USSD, null); mCM.setOnSuppServiceNotification(this, EVENT_SSN, null); mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null); if (false) { try { //debugSocket = new LocalServerSocket(com.android.internal.telephony.debug); debugSocket = new ServerSocket(); debugSocket.setReuseAddress(true); debugSocket.bind (new InetSocketAddress(127.0.0.1, 6666)); debugPortThread = new Thread( new Runnable() { public void run() { for(;;) { try { Socket sock; sock = debugSocket.accept(); Log.i(LOG_TAG, New connection; resetting radio); mCM.resetRadio(null); sock.close(); } catch (IOException ex) { Log.w(LOG_TAG, Exception accepting socket, ex); } } } }, GSMPhone debug); debugPortThread.start(); } catch (IOException ex) { Log.w(LOG_TAG, Failure to open com.android.internal.telephony.debug socket, ex); } } //Change the system property SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE, new Integer(Phone.PHONE_TYPE_GSM).toString()); }在這裡創建了通話管理類GsmCallTracker mCT = new GsmCallTracker(this); //通話管理
//***** Constructors GsmCallTracker (GSMPhone phone) { this.phone = phone; cm = phone.mCM; cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null); cm.registerForOn(this, EVENT_RADIO_AVAILABLE, null); cm.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null); }由於GsmCallTracker的父類繼承了Handler,有以下方法
//****** Overridden from Handler public void handleMessage (Message msg) { AsyncResult ar; switch (msg.what) { case EVENT_POLL_CALLS_RESULT: ar = (AsyncResult)msg.obj; if (msg == lastRelevantPoll) { if (DBG_POLL) log( handle EVENT_POLL_CALL_RESULT: set needsPoll=F); needsPoll = false; lastRelevantPoll = null; handlePollCalls((AsyncResult)msg.obj); } break; case EVENT_OPERATION_COMPLETE: ar = (AsyncResult)msg.obj; operationComplete(); break; case EVENT_SWITCH_RESULT: case EVENT_CONFERENCE_RESULT: case EVENT_SEPARATE_RESULT: case EVENT_ECT_RESULT: ar = (AsyncResult)msg.obj; if (ar.exception != null) { phone.notifySuppServiceFailed(getFailedService(msg.what)); } operationComplete(); break; case EVENT_GET_LAST_CALL_FAIL_CAUSE: int causeCode; ar = (AsyncResult)msg.obj; operationComplete(); if (ar.exception != null) { // An exception occurred...just treat the disconnect // cause as normal causeCode = CallFailCause.NORMAL_CLEARING; Log.i(LOG_TAG, Exception during getLastCallFailCause, assuming normal disconnect); } else { causeCode = ((int[])ar.result)[0]; } // Log the causeCode if its not normal if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || causeCode == CallFailCause.TEMPORARY_FAILURE || causeCode == CallFailCause.SWITCHING_CONGESTION || causeCode == CallFailCause.CHANNEL_NOT_AVAIL || causeCode == CallFailCause.QOS_NOT_AVAIL || causeCode == CallFailCause.BEARER_NOT_AVAIL || causeCode == CallFailCause.ERROR_UNSPECIFIED) { GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); EventLog.writeEvent(EventLogTags.CALL_DROP, causeCode, loc != null ? loc.getCid() : -1, TelephonyManager.getDefault().getNetworkType()); } for (int i = 0, s = droppedDuringPoll.size() ; i < s ; i++ ) { GsmConnection conn = droppedDuringPoll.get(i); conn.onRemoteDisconnect(causeCode); } updatePhoneState(); phone.notifyPreciseCallStateChanged(); droppedDuringPoll.clear(); break; case EVENT_REPOLL_AFTER_DELAY: case EVENT_CALL_STATE_CHANGE: pollCallsWhenSafe(); break; case EVENT_RADIO_AVAILABLE: handleRadioAvailable(); break; case EVENT_RADIO_NOT_AVAILABLE: handleRadioNotAvailable(); break; } }在這個分支case EVENT_CALL_STATE_CHANGE: 去獲取當前的狀態
protected void pollCallsWhenSafe() { needsPoll = true; if (checkNoOperationsPending()) { lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); cm.getCurrentCalls(lastRelevantPoll); } }在RIL.java類中的實現
public void getCurrentCalls (Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result); if (RILJ_LOGD) riljLog(rr.serialString() + > + requestToString(rr.mRequest)); send(rr); } private void send(RILRequest rr) { Message msg; if (mSocket == null) { rr.onError(RADIO_NOT_AVAILABLE, null); rr.release(); return; } msg = mSender.obtainMessage(EVENT_SEND, rr); acquireWakeLock(); msg.sendToTarget(); }在這裡通知應用層改變狀態
事件通知流程
為了加深理解,我也自己寫了一個例子,在GsmCallTracker開一個線程去隨機模擬電話狀態的改變,程序相當簡單
程序目錄結構
在PhoneApp中做一些全局的初始化工作
package com.dzt.phonemsg; import android.app.Application; import android.util.Log; import com.dzt.phonemsg.framework.CallManager; import com.dzt.phonemsg.framework.Phone; /** * 演示Phone程序中的事件傳遞,由於Phone應用程序在代碼跟蹤時不是很方便, * 並且Phone的消息通訊也比較復雜,就自己把部分代碼拿出來模擬Handler的消息傳遞,用到了觀察者模式 * * @author Administrator * @date 2014.08.01 */ public class PhoneApp extends Application { private static final String TAG = PhoneApp_dzt; private static final boolean mIsShowLog = true; static PhoneApp instance = null; Phone phone = new Phone(); CallManager mCM; @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); instance = this; mCM = CallManager.getInstance(); mCM.registerPhone(phone); print_i(PhoneApp, onCreate); } /** * Returns the singleton instance of the PhoneApp. */ static PhoneApp getInstance() { return instance; } static boolean isRunning() { if (instance != null) return true; return false; } /** * 打印消息 * * @param pkg * @param msg */ public static void print_i(String pkg, String msg) { if (mIsShowLog) Log.i(TAG, [ + pkg + ]--------------------> + msg); } }在InCallScreen類中注冊需要處理的消息,並根據不同的狀態使用一個TextView來更新
package com.dzt.phonemsg; import com.dzt.phonemsg.framework.CallManager; import com.dzt.phonemsg.framework.Phone; import com.dzt.phonemsg.os.AsyncResult; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.TextView; public class InCallScreen extends Activity { private static final int PHONE_STATE_CHANGED = 101; private static final int REQUEST_UPDATE_SCREEN = 122; private TextView mText = null; private Phone.State mState = Phone.State.IDLE; private CallManager mCM; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mText = (TextView) findViewById(R.id.tv_text); mCM = PhoneApp.getInstance().mCM; registerForPhoneStates(); } @Override protected void onDestroy() { // TODO Auto-generated method stub if (PhoneApp.isRunning()) { PhoneApp.getInstance().phone.exitPhone(); } super.onDestroy(); } /** * Something has changed in the phone's state. Update the UI. */ private void onPhoneStateChanged(AsyncResult r) { mState = mCM.getState(); requestUpdateScreen(); } /** * 注冊PHONE_STATE_CHANGED標記,跟CallManager通訊 */ private void registerForPhoneStates() { mCM.registerForPreciseCallStateChanged(mHandler, PHONE_STATE_CHANGED, null); } /* package */void requestUpdateScreen() { PhoneApp.print_i(MainActivity, requestUpdateScreen()...); mHandler.removeMessages(REQUEST_UPDATE_SCREEN); mHandler.sendEmptyMessage(REQUEST_UPDATE_SCREEN); } /** * 處理UI的操作,如更新通話狀態和時間 */ private void updateScreen() { mText.setText(mState.toString()); } private Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case PHONE_STATE_CHANGED: onPhoneStateChanged((AsyncResult) msg.obj); break; case REQUEST_UPDATE_SCREEN: updateScreen(); break; default: break; } }; }; }GsmCallTracker這個類去改變不同的狀態,用一個線程來獲取隨機來改變
package com.dzt.phonemsg.framework; import java.util.Random; /** * 在這裡隨機改變狀態,模擬通話管理 * * @author Administrator * */ public class GsmCallTracker { Phone.State state = Phone.State.IDLE; boolean mIsRunning = false; Phone mPhone = null; GsmCallTracker(Phone phone) { // TODO Auto-generated constructor stub mIsRunning = true; mPhone = phone; new CallTrackerThread().start(); } public void exitGsmCallTracker() { mIsRunning = false; } class CallTrackerThread extends Thread { @Override public void run() { // TODO Auto-generated method stub while (mIsRunning) { Random random = new Random(); int data = random.nextInt(3); switch (data) { case 0: state = Phone.State.IDLE; break; case 1: state = Phone.State.RINGING; break; case 2: state = Phone.State.OFFHOOK; break; default: state = Phone.State.IDLE; break; } mPhone.notifyPreciseCallStateChanged(); // PhoneApp.print_i(GsmCallTracker, // CallTrackerThread data = // + data + ---- + state.toString()); try { sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
前言在Android開發中ListView是最為常用的控件之一,基本每個應用都會涉及到它,要使用ListView列表展示,就不可避免地涉及到另外一個東西—&m
之前寫過一篇關於Android 繼承DialogFragment彈出dialog對話框一,這次是在上次的基礎上修改了一些東西,就是怎樣在DialogFragment中獲取
簡介在各種不同的應用中,大家可能會經常見到這樣一個效果:Toolbar是透明的,有著一個背景圖片以及大標題,隨著頁面向上滑動,其標題逐漸縮放到Toolbar上,而背景圖片
JNI是一種可以在Java中調用C/C++代碼的技術,也就是說可以在Android上使用C/C++來開發。但是並不能用純C/C+&