Android4.4.2源码分析之WiFi模块(一)

由对Androidsetting的源码分析之WiFi模块的界面fragment为WiFisettings.java,关于setting模块的源码分析可以参考

Android系统源码剖析(一)---Settings

已经写了几篇关于Android源码的,源码代码量太大,所以如果想分析某个模块可能不知如何下手,说一下思路

1,分析源码英文阅读能力要够,想要分析某个模块一般找模块对应的英文,就是模块

2,找到之后首先查看清单配置文件Androidmani.fest,找到程序主界面activity

3,通过查看配置文件中的权限可以知道应用都有什么用

大致通过以上三步就可以进入源码进行分析

针对WiFi,我们通过清单文件可以知道,需要以下几个权限,其含义如下注释

<!-- 允许程序获取WiFi状态 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <!--允许程序改变WiFi状态-->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    <!--允许程序获取手机网络状态-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!--允许程序改变网络状态-->
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
    <!--允许程序访问网络-->
    <uses-permission android:name="android.permission.INTERNET"/>

如果使用as做开发工具的话在使用WiFi时如果不加权限会有警告来提示你,感觉还是很人性化的,如果把as的所有警告都解决了那么程序应该会很少出意料之外的bug,而且as的单步调试可以很好的去发现bug,界面美观,忍不住强烈推荐。

因为是首次接触WiFi源码,我会按代码顺序分析,当然这样可能会有些乱,完成全部分析后会再按照功能点分析,不过得等到下一篇博客喽。好了,闲话少叙,正式进入对WiFi源码的分析,

   

1,WifiSettings属于fragment,实现了对话框的点击事件接口,代码如下

public class WifiSettings extends RestrictedSettingsFragment
        implements DialogInterface.OnClickListener 

在onCreateView方法中首先判断应用是否是第一次运行,根据是否是第一次运行来设置不同的布局

1>,if(mSetupWizardMode) 则加载启动布局

View view = inflater.inflate(R.layout.setup_preference, container, false);

否则加载正常布局

View v = inflater.inflate(R.layout.add_preference_list_fragment,null);

接着onActivityCreated方法中

2>

mP2pSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT);

这句代码用来判断是否支持WiFi直连:if  the device supports Wi-Fi Direct networking or not

WiFi直连的含义是说:设备可以直接通过WiFi共享文件,图片等,类似蓝牙传输,实现点对点的连接,通信(要求两设备必须位于同一网段)。可以看到判断设备是否支持WiFi直连是通过PackageManager的hasSystemFeature

目录为/android/external/robolectric/src/main/java/com/xtremelabs/robolectric/res/

 public boolean hasSystemFeature(String name) {
 return systemFeatureList.containsKey(name) ? systemFeatureList.get(name) : false;
   }

由源码可知该方法会遍历systemFeaturelist列表,判断是否包含所传入的feature,通过PackageManagerService中的readPermissionsFromXml(File f)传入xml文件进行解析获取到设备所支持的permission

3>,接下来获取到WiFi的管理类对象mWifiManager,用于开启/关闭WiFi,扫描WiFi,连接WiFi等等

mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);

4>,通过获取到set_wifi_priority的值来对preference列表进行 不同显示

<pre name="code" class="java">if (getResources().getBoolean(R.bool.set_wifi_priority)) {
            addPreferencesFromResource(R.xml.wifi_sort_settings);
            mDefaultTrustAP
                    = (PreferenceCategory)findPreference("default_trust_access_points");
            mConfigedAP = (PreferenceCategory)findPreference("configed_access_points");
            mUnKnownAP = (PreferenceCategory)findPreference("unknown_access_points");
        }
        else {
            addPreferencesFromResource(R.xml.wifi_settings);
        }

对于boolean值“set_wifi_priority”的值可以查看Z:\L7-A1\android\packages\apps\Settings\res\values\bools.xml文件该值表示whether 
to show hotspot via the ap‘s classification接入点优先级设定即是否按照所搜索到的WiFi优先级来显示Wifi列表,可以看到等级有三种:默认的信任接入点,信任接入点,未知接入点因为在这里boolean值被设置为false,所以不按优先级排序5>,接下来就是添加WiFi开关,至于通过代码在导航栏actionbar添加switch的代码类似蓝牙,在此不再赘述,可以看到,蓝牙开关switch传入了WifiEnabler,所以对于Wifi的开关的管理位于WiFiEnablerswitch中

mWifiEnabler = new WifiEnabler(activity, actionBarSwitch);

进入WiFiEnabler可以看到,该类就做了两件事,第一注册广播监听WiFi的变化并随之改变switch的状态,第二为switch添加点击监听事件

广播所监听的事件有

//当WiFi状态发生改变时会发送该广播
 mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);

 // The order matters! We really should not depend on this. :(
        mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
       //当设备网络状态发生改变时会发送该广播
 mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);

action WifiManager.SUPPLICANT_STATE_CHANGD_ACTION定义如下,表示正在创建连接的状态发生改变,又有了新的连接可用,可用获取到WiFi的具体的连接状态,如果你只是对连接的整体状态感兴趣则该广播无用

 /**
     * Broadcast intent action indicating that the state of establishing a connection to
     * an access point has changed.One extra provides the new
     * {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and
     * is not generally the most useful thing to look at if you are just interested in
     * the overall state of connectivity.
     * @see #EXTRA_NEW_STATE
     * @see #EXTRA_SUPPLICANT_ERROR
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String SUPPLICANT_STATE_CHANGED_ACTION =
        "android.net.wifi.supplicant.STATE_CHANGE";

接下来看广播的处理

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
                //WiFi状态生改变时去更新switch的状态,WiFi的状态存在于WifiManager.EXTRA_WIFI_STATE
                handleWifiStateChanged(intent.getIntExtra(
                        WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN));
              //needPrompt方法用于判断是否是飞行模式以及飞行模式是否无效
           if (WifiSettings.needPrompt(context)) {
                    setSwitchChecked(false);
                }
            } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
            //连接发生改变时的更新,WifiManager.EXTRA_NEW_STATE存放改变后的状态
             if (!mConnected.get()) {
                    handleStateChanged(WifiInfo.getDetailedStateOf((SupplicantState)
                            intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
                }
            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            //网络状态发生改变时的更新
           NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
                        WifiManager.EXTRA_NETWORK_INFO);
                mConnected.set(info.isConnected());
                handleStateChanged(info.getDetailedState());
            }
        }
    };

当WiFi开关状态发生改变时更新放啊如下:

 private void handleWifiStateChanged(int state) {
        switch (state) {
            case WifiManager.WIFI_STATE_ENABLING://正在打开WiFi
              mSwitch.setEnabled(false);
                break;
            case WifiManager.WIFI_STATE_ENABLED://WiFi已经打开
                setSwitchChecked(true);
                mSwitch.setEnabled(true);
                break;
            case WifiManager.WIFI_STATE_DISABLING://正在关闭WiFi
                mSwitch.setEnabled(false);
                break;
            case WifiManager.WIFI_STATE_DISABLED://WiFi已经关闭
                setSwitchChecked(false);
                mSwitch.setEnabled(true);
                break;
            default:
                setSwitchChecked(false);
                mSwitch.setEnabled(true);
                break;
        }
    }

对于网络状态发生改变时的更新方法,可以看到方法已经被注释掉,因为不需要去更新preference的副标题

  private void handleStateChanged(@SuppressWarnings("unused") NetworkInfo.DetailedState state) {
        // After the refactoring from a CheckBoxPreference to a Switch, this method is useless since
        // there is nowhere to display a summary.
        // This code is kept in case a future change re-introduces an associated text.
        /*
        // WifiInfo is valid if and only if Wi-Fi is enabled.
        // Here we use the state of the switch as an optimization.
        if (state != null && mSwitch.isChecked()) {
            WifiInfo info = mWifiManager.getConnectionInfo();
            if (info != null) {
                //setSummary(Summary.get(mContext, info.getSSID(), state));
            }
        }
        */
    }

对于switch的点击事件的监听代码如下

public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        //Do nothing if called as a result of a state machine event
        if (mStateMachineEvent) {
            return;
        }

        if (mContext.getResources().getBoolean(R.bool.wifi_to_cell)) {
            ConnectivityManager mConnService = (ConnectivityManager) mContext.
                    getSystemService(Context.CONNECTIVITY_SERVICE);
            if (mConnService != null) {
                NetworkInfo netInfo = (NetworkInfo) mConnService
                        .getNetworkInfo(ConnectivityManager.TYPE_WIFI);
                if (netInfo != null && netInfo.isConnected()) {
                    Settings.System.putInt(mContext.getContentResolver(), WIFI_IS_CONNECTED,
                            CONNECTED);
                } else {
                    Settings.System.putInt(mContext.getContentResolver(), WIFI_IS_CONNECTED,
                            DISCONNECTED);
                }
            }
        }

        // Show toast message if Wi-Fi is not allowed in airplane mode
         //判断是否在飞行模式中WiFi是不允许的
         if (isChecked
                && (WifiSettings.needPrompt(mContext) || !WirelessSettings.isRadioAllowed(
                        mContext, Settings.Global.RADIO_WIFI))) {
            Toast.makeText(mContext, R.string.wifi_in_airplane_mode,
                    Toast.LENGTH_SHORT).show();
            // Reset switch to off. No infinite check/listenenr loop.
            buttonView.setChecked(false);
            return;
        }

        // Disable tethering if enabling Wifi
        int wifiApState = mWifiManager.getWifiApState();
        if (isChecked && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
                (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
            mWifiManager.setWifiApEnabled(null, false);
        }

        // shouldn't setWifiEnabled(true) in airplane mode.
        if (isChecked && WifiSettings.needPrompt(mContext)) {
            return;
        } else {
          //setWifiEnabled打开或者关闭WiFi的方法,会发送WiFi状态改变的广播:WIFI_STATE_CHANGED_ACTION
          if (mWifiManager.setWifiEnabled(isChecked)) {
                // Intent has been taken into account, disable until new state
                // is active
                mSwitch.setEnabled(false);
            } else {
                // Error
                Toast.makeText(mContext, R.string.wifi_error,
                        Toast.LENGTH_SHORT).show();
            }
        }
    }<span style="font-size:14px;">
</span>

对于mStateMachineEvent的值是在对switch进行设置时赋值,起开关保护的作用,保证当点击switch的时候先将switch状态设置成功再进入点击事件方法

private void setSwitchChecked(boolean checked) {
        if (checked != mSwitch.isChecked()) {
            mStateMachineEvent = true;
            mSwitch.setChecked(checked);
            mStateMachineEvent = false;
        }
    }

打开或关闭WiFi的方法为,mWifiManager.setWifiEnabled(boolean enable),在打开时会发送广播WifiManager.WIFI_STATE_CHANGED_ACTION,可监听该广播来改变UI

Android4.4.2源码分析之WiFi模块(二)

时间: 2024-08-08 05:22:04

Android4.4.2源码分析之WiFi模块(一)的相关文章

Android4.4.2源码分析之WiFi模块(二)

接着上一篇继续对WiFi源码的分析 Android4.4.2源码分析之WiFi模块(一) onResume方法中 6>,首先是调用WiFiEnabler的resume方法对switch进行管理 接下来注册广播 getActivity().registerReceiver(mReceiver, mFilter); 广播监听的action如下 //wifi状态改变的action mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); //W

Android4.42-Setting源码分析之蓝牙模块Bluetooth(下)

接着上一篇Android4.42-Settings源码分析之蓝牙模块Bluetooth(上) 继续蓝牙模块源码的研究 THREE,蓝牙模块功能实现 switch的分析以及本机蓝牙重命名和可见性的分析见上一篇,接下来进行第三章第三部分的介绍:关于蓝牙远程设备列表的加载.如果没有看过,建议看看上一篇关第一章蓝牙的布局,有助于理解 3>,设备列表的加载 因为这部分代码很多,所以在介绍时先说一下思路,程序首先通过底层的BluetoothAdapter的getBondedDevices()方法获取到已配对

elasticsearch源码分析之search模块(client端)

elasticsearch源码分析之search模块(client端) 注意,我这里所说的都是通过rest api来做的搜索,所以对于接收到请求的节点,我姑且将之称之为client端,其主要的功能我们可以简单地概括为将的数据请求发送到node,然后在对返回的结果做处理并返回给调用方,话虽如此,但是过程并非那么简单. 请求初始化 1.api的注册,上一篇已经提到了,所以的api都是通过Guice框架注册进来的,在注册的时候会在controller上将不同的url绑定到不同的handler中: co

[nginx] nginx源码分析--健康检查模块锁分析

健康检查模块 见前文:[nginx] nginx源码分析--健康检查模块 其中有一张框架图, 接下来的内容,将会利用到这个图中的内容. [classic_tong @ https:////www.cnblogs.com/hugetong/p/12274125.html ]  描述 我们知道nginx是多进程的,每个进程都保存了相同的配置.但是实际上, 并不需要每一个进程对每一个后端服务器进行. 于是健康检查模块在这里需要一个进程间同步机制,用来协商哪一个进程对 哪一个后端服务器进行检查. 接下来

jQuery 源码分析(十一) 队列模块 Queue详解

队列是常用的数据结构之一,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队).特点是先进先出,最先插入的元素最先被删除. 在jQuery内部,队列模块为动画模块提供基础功能,负责存储动画函数.自动出队并执行动画函数,同时还要确保动画函数的顺序执行. jQuery的静态方法含有如下API: $.queue(elem,type,data) ;返回或修改匹配元素关联的队列,返回最新的队列,参数如下:   elem ;DOM元素或JavaScript对象 type  ;

Android4.0 Launcher 源码分析2——Launcher入口及Launcher.xml的加载

2.Launcher入口及Launcher.xml的加载 2.1 Launcher入口 1) LauncherApplication 我们在源代码中可以找到LauncherApplication, 它继承了Application类,当整个Launcher启动时,它就是整个程序的入口.我们先来看它们在AndroidManifest.xml中是怎么配置的. 1 <application 2 android:name="com.android.launcher2.LauncherApplicat

Android4.0 Launcher 源码分析1——Launcher整体结构

1.Launcher整体结构 桌面程序其实并不包含桌面壁纸,桌面壁纸其实是由 WallpaperManagerService来提供,整个桌面其实是叠加在整个桌面壁纸上的另外一个层. 1.1 WorkSpace Launcher整个布局的根是DragLayer,DragLayer继承了FrameLayout,所以DragLayer本身可以看作是一个FrameLayout.下面是dock_divider,它通过include关键字包含了另外一个布局文件workspace_divider.xml,而这

Android4.0 Launcher 源码分析3——WorkSpace结构

3.WorkSpace结构 桌面的左右滑动功能主要是在PagedView类中实现的,而WorkSpace是PagedView类的子类,所以会继承PagedView中的方法.当我们的手指点击WorkSpace时,首先就会触发PageView中的onInterceptTouchEvent()方法,会根据相应的条件来判断是否对Touch事件进行拦截,如果onInterceptTouchEvent()方法返回为true,则会对Touch事件进行拦截,PageView类的onTouch方法会进行响应从而得

WebRTC源码分析:音频模块结构分析

一.概要介绍WebRTC的音频处理流程,见下图: webRTC将音频会话抽象为一个通道Channel,譬如A与B进行音频通话,则A需要建立一个Channel与B进行音频数据传输.上图中有三个Channel,每个Channel包含编解码和RTP/RTCP发送功能. 以一个Channel而言,应用程序中将包含三个活动线程,录音线程,音频接收线程和播放线程. 1)录音线程:负责麦克风音频的采集,见图中红色路径,采集到音频后,缓存到一定长度,进行音频处理,主要包括EC,AGC和NS等.然后送到Chann