編輯:關於Android編程
設置項網絡按鈕類定義
網絡監聽二:statusbar frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
EthernetStateMachine.java -> 網絡狀態機,用於管理網絡狀態變化及動作邏輯 EthernetManager.java -> 網絡管理器,是app和EthernetService信息交互的橋梁 EthernetInfo.java -> 網絡狀態參數類,是Parcelable的一個實現 EthernetInfo.aidl -> aidl文件,Manager和service統一使用的數據結構 IEthernetManager.aidl -> aidl文件,用於Manager和service通信
private class InterfaceStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(EthernetManager.INTERFACE_STATE_CHANGED_ACTION)) { ... Intent newIntent = new Intent(EthernetManager.NETWORK_STATE_CHANGED_ACTION); newIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); newIntent.putExtra(EthernetManager.EXTRA_ETHERNET_INFO, ei);在service中,可以看到發送NETWORK_STATE_CHANGED_ACTION的發送動作,而這個發送行為還不是底層上報的狀態直接啟動的,而是上面說的網絡狀態機,它發送的INTERFACE_STATE_CHANGED_ACTION廣播信息,怎麼源頭又跑上面去了?有些人可能並不理解為什麼在framework裡面要把一個簡單的事件廣播要這麼來回的發送,等明白了網絡狀態機的作用,就知道這些過程的邏輯性了。
addState(mRootState); addState(mIdleState, mRootState); //addState(mObtainingLinkState, mRootState); addState(mObtainingIpState, mRootState); addState(mIPConnectedState, mRootState); addState(mDisconnectingState, mRootState);
private void sendInterfaceStateChangedBroadcast() { if (DBG) Slog.d(TAG, Sending INTERFACE_STATE_CHANGED_ACTION for + mEthernetInfo.getName()); Intent intent = new Intent(EthernetManager.INTERFACE_STATE_CHANGED_ACTION); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(EthernetManager.EXTRA_ETHERNET_INFO, new EthernetInfo(mEthernetInfo)); mContext.sendBroadcast(intent); } private void setNetworkDetailedState(DetailedState state) { if (DBG) Slog.d(TAG, mEthernetInfo.getName() + setDetailed state, old = + mEthernetInfo.getDetailedState() + and new state= + state); if (state != mEthernetInfo.getDetailedState()) { mEthernetInfo.setDetailedState(state, null, null); mEthernetInfo.setIsAvailable(true); sendInterfaceStateChangedBroadcast(); } } void dhcpSuccess(DhcpResults dr) { if (DBG) Slog.d(TAG, mEthernetInfo.getName() + DHCP successful); LinkProperties lp = dr.linkProperties; ... setNetworkDetailedState(DetailedState.CONNECTED); }
public void updateInterface(EthernetInfo newInfo) { if (newInfo == null) { Slog.e(TAG, Null EthernetInfo); return; } if (mAvailableInterface == null) { Slog.e(TAG, Unable to find statemachine for interface + newInfo.getName()); return; } sendMessage(mAvailableInterface, EthernetStateMachine.CMD_UPDATE_INTERFACE, newInfo); if(DBG) Slog.d(TAG, newInfo.getName() + updateInterface done); }看到了來來回回的廣播,至此算是結束了,這裡還要注意一點,廣播收發程序中,我們要注意一個序列化參數的傳遞,就是EthernetInfo對象,這個對象存儲著當前網絡狀態參數。
public EthernetService(Context context) { mContext = context; mNetd = INetworkManagementService.Stub.asInterface( ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE) ); try { mNetd.registerObserver(new NetworkManagementEventObserver()); } catch (RemoteException e) { Slog.e(TAG, Remote NetworkManagementService error: + e); }看下NetworkManagementEventObserver的實現:
private class NetworkManagementEventObserver extends INetworkManagementEventObserver.Stub { public void interfaceAdded(String iface) { if(DBG) Slog.d(TAG, interfaceAdded: + iface); addInterface(iface); } public void interfaceRemoved(String iface) { if(DBG) Slog.d(TAG, interfaceRemoved: + iface); removeInterface(iface); } public void limitReached(String limitName, String iface) {} public void interfaceClassDataActivityChanged(String label, boolean active) {} public void interfaceLinkStateChanged(String iface, boolean up) { if(DBG) Slog.d(TAG, interfaceLinkStateChanged for + iface + , up = + up); if (mAvailableInterface != null && up) { //sendMessage(mAvailableInterface, //EthernetStateMachine.CMD_LINK_UP); } } public void interfaceStatusChanged(String iface, boolean up) { if(DBG) Slog.d(TAG, interfaceStatusChanged for + iface + , up = + up); //addInterface(iface); } public void addressUpdated(String address, String iface, int flags, int scope) {} public void addressRemoved(String address, String iface, int flags, int scope) {} }這裡我們看到seivice從NetworkManagementService進行函數回調。 NetworkManagementService也是注冊到系統中的服務項,顧名思義負責網絡管理服務,具體功能在本篇不做深入分析,其中之一通過socket和netd進行交互,這個在後面繼續跟蹤。
SystemServer.java try { Slog.i(TAG, NetworkManagement Service); networkManagement = NetworkManagementService.create(context); ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement); } catch (Throwable e) { reportWtf(starting NetworkManagement Service, e); }在後續的分析中,我們開始了解到framework到native的交互,這裡我們先看一張網絡的圖示
private static final String NETD_SOCKET_NAME = netd; private NetworkManagementService(Context context, String socket) { mContext = context; if (simulator.equals(SystemProperties.get(ro.product.device))) { return; } mConnector = new NativeDaemonConnector( new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160); mThread = new Thread(mConnector, NETD_TAG); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); }這裡socket值就是netd字符串,service啟動了名為NativeDaemonConnector的Runnable線程,同時從構造函數中傳遞了NetdCallbackReceiver 對象,用於回調處理各種網絡事件。
private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks { @Override public void onDaemonConnected() { @Override public boolean onEvent(int code, String raw, String[] cooked) { switch (code) { case NetdResponseCode.InterfaceChange: } else if (cooked[2].equals(linkstate) && cooked.length == 5) { // 網絡狀態變化事件在這裡回調處理 notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals(up)); return true; }
/** * Notify our observers of an interface link state change * (typically, an Ethernet cable has been plugged-in or unplugged). */ private void notifyInterfaceLinkStateChanged(String iface, boolean up) { final int length = mObservers.beginBroadcast(); for (int i = 0; i < length; i++) { try { mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up); } catch (RemoteException e) { } catch (RuntimeException e) { } } mObservers.finishBroadcast(); }上面說了NativeDaemonConnector是一個Runnable的實現,那麼這個線程在後台做些什麼工作呢?在線程run函數中可以看到線程在while死循環中一直listenToSocket,可以猜想這裡是在監聽獲取native中網絡相關事件的地方了。
@Override public void run() { mCallbackHandler = new Handler(FgThread.get().getLooper(), this); while (true) { try { listenToSocket(); } catch (Exception e) { loge(Error in NativeDaemonConnector: + e); SystemClock.sleep(5000); } } } private void listenToSocket() throws IOException { LocalSocket socket = null; try { // 創建一個socket socket = new LocalSocket(); LocalSocketAddress address = determineSocketAddress(); socket.connect(address); // 從socket中獲取流數據並處理 InputStream inputStream = socket.getInputStream(); synchronized (mDaemonLock) { mOutputStream = socket.getOutputStream(); } ... mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage( event.getCode(), event.getRawEvent())); // 收到流數據時,直接發給主線程,通過NetdCallbackReceiver 對象進行回調處理 @Override public boolean handleMessage(Message msg) { String event = (String) msg.obj; try { if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) { log(String.format(Unhandled event '%s', event)); } } catch (Exception e) { loge(Error handling ' + event + ': + e); } return true; } private LocalSocketAddress determineSocketAddress() { // If we're testing, set up a socket in a namespace that's accessible to test code. // In order to ensure that unprivileged apps aren't able to impersonate native daemons on // production devices, even if said native daemons ill-advisedly pick a socket name that // starts with __test__, only allow this on debug builds. if (mSocket.startsWith(__test__) && Build.IS_DEBUGGABLE) { return new LocalSocketAddress(mSocket); } else { return new LocalSocketAddress(mSocket, LocalSocketAddress.Namespace.RESERVED); } }我們接下來看一下LocalSocket相關的類,了解一下這個socket是如何connect和get的。 LocalSocket*相關的類定義在 frameworks/base/core/java/android/net/ |->LocalSocket.java
static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_net_LocalSocketImpl),了解了庫加載的問題後,我們接著看LocalSocketImpl.java中使用的幾個重要的函數:
connect() getInputStream() getOutputStream()函數具體內容不具體貼出來,其中可以看到調用了本地方法如read_native(),writeba_native()等。我們就走到native的大門了。打開本地函數文件,看下native中本地函數列表。 frameworks/base/core/jni/android_net_LocalSocketImpl.cpp
static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ {getOption_native, (Ljava/io/FileDescriptor;I)I, (void*)socket_getOption}, {setOption_native, (Ljava/io/FileDescriptor;III)V, (void*)socket_setOption}, {connectLocal, (Ljava/io/FileDescriptor;Ljava/lang/String;I)V, (void*)socket_connect_local}, {bindLocal, (Ljava/io/FileDescriptor;Ljava/lang/String;I)V, (void*)socket_bind_local}, {listen_native, (Ljava/io/FileDescriptor;I)V, (void*)socket_listen}, {accept, (Ljava/io/FileDescriptor;Landroid/net/LocalSocketImpl;)Ljava/io/FileDescriptor;, (void*)socket_accept}, {shutdown, (Ljava/io/FileDescriptor;Z)V, (void*)socket_shutdown}, {available_native, (Ljava/io/FileDescriptor;)I, (void*) socket_available}, {pending_native, (Ljava/io/FileDescriptor;)I, (void*) socket_pending}, {read_native, (Ljava/io/FileDescriptor;)I, (void*) socket_read}, {readba_native, ([BIILjava/io/FileDescriptor;)I, (void*) socket_readba}, {writeba_native, ([BIILjava/io/FileDescriptor;)V, (void*) socket_writeba}, {write_native, (ILjava/io/FileDescriptor;)V, (void*) socket_write}, {getPeerCredentials_native, (Ljava/io/FileDescriptor;)Landroid/net/Credentials;, (void*) socket_get_peer_credentials} //,{getSockName_native, (Ljava/io/FileDescriptor;)Ljava/lang/String;, // (void *) socket_getSockName} }; int register_android_net_LocalSocketImpl(JNIEnv *env){}
上周花了一周時間做的課程設計的項目,實現的功能如下:基本功能:(1) 該APP能夠通過藍牙自動搜索周圍其他使用了該APP的手機,用戶可選擇其中某一個APP發起對戰的要求,
蘋果上的UI基本上都是這個效果,然而Android機上的頂部狀態欄總是和app的主題顏色不搭。還好如今的api19以上的版本,我們也能做出這樣的效果。第一步: // 需
前言在上一篇Android網絡編程(九)Retrofit2前篇[基本使用]中我們了解了Retrofit的最基本的GET方式訪問網絡的寫法以及請求參數的簡單介紹。這一篇我們
這裡寫鏈接內容仿映客送小禮物的特效,順便復習一下屬性動畫,話不多說先看效果圖。需求分析可以看到整個動畫有幾部分組成,那我們就把每個部分拆分出來各個擊破。1.要顯示那些內容