Activar Wi-Fi
Encienda el interruptor Wifi. El interruptor Wifi está WifiEnabler
habilitado WifiEnabler
para SwitchWidgetController.OnSwitchChangeListener
monitoreo. Al encender/apagar el interruptor se volverá a llamar a
// 处理Switch 控件的状态变化事件
public boolean onSwitchToggled(boolean isChecked) {
//Do nothing if called as a result of a state machine event
// 通过Switch.setEnabled 方法设置Switch 控件状态时,不需要再次禁止/允许Wi-Fi,否则将造成循环调用的后果
if (mStateMachineEvent) {
return true;
}
// Show toast message if Wi-Fi is not allowed in airplane mode
//如果在飞行模式时不允许单独使用Wi-Fi,使用Toast 信息框进行提示
if (isChecked && !WirelessUtils.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/listener loop.
mSwitchWidget.setChecked(false);
return false;
}
if (isChecked) {
mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_WIFI_ON);
} else {
// Log if user was connected at the time of switching off.
mMetricsFeatureProvider.action(
mContext, SettingsEnums.ACTION_WIFI_OFF,
mConnected.get()
);
}
//通过WifiManager.setWifiEnabled()开启/关闭wifi
if (!mWifiManager.setWifiEnabled(isChecked)) {
// Error
mSwitchWidget.setEnabled(true);
Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
}
return true;
}
mWifiManager.setWifiEnabled(isChecked)
Se utiliza para apagar o encender Wi-Fi según el estado actual del control Switch.
onCheckedChanged
Se utiliza una variable al comienzo del método .mStateMachineEvent
Cuando la variable es verdadera,onCheckedChanged
el método salta directamente. De hecho, la razón para agregar esta condición de salto es porque el cambio de estado del control del interruptor puede tener las dos situaciones siguientes.
Haga clic directamente en el control Switch.
Llame al método Switch.setChecked.
Desafortunadamente, las dos situaciones anteriores desencadenan
onCheckedChanged
una llamada a un método. No importa qué método cambie el estado del control Switch,onCheckedChanged
llamarWifiManager.setWifiEnabled
al método para configurar el estado de Wi-Fi activaráonCheckedChanged
la llamada al método nuevamente. AlonCheckedChanged
volver a llamar al método, debemStateMachineEvent
establecer el valor de la variable en verdadero, de modo queonCheckedChanged
el método regrese inmediatamente al comienzo de la ejecución (si continúa ejecutando el siguiente código, provocará un bucle infinito) y luegomStateMachineEvent
configure el valor de la variable a falso. De esta manera, aún podrás continuar ejecutando el método la próxima vez que configures el estado de Wi-FionCheckedChanged
.
Establecer el estado del control del interruptor usando el método de transmisión
/packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java
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)) {
handleWifiStateChanged(mWifiManager.getWifiState());
} else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
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());
}
}
};
Para Wi-Fi, este receptor de transmisión se utiliza principalmente para recibir cambios de estado de Wi-Fi y realizar procesamientos adicionales. El método para manejar los cambios de estado de Wi-Fi es handleWifiStateChanged
private void handleWifiStateChanged(int state) {
// Clear any previous state
mSwitchWidget.setDisabledByAdmin(null);
switch(state) {
case WifiManager . WIFI_STATE_ENABLING : // 处理正在开启Wi-Fi的状态
break;
case WifiManager . WIFI_STATE_ENABLED : // 处理已经开启Wi-Fi的状态
setSwitchBarChecked(true);
mSwitchWidget.setEnabled(true);
break;
case WifiManager . WIFI_STATE_DISABLING : // 处理正在关闭Wi-Fi的状态
break;
case WifiManager . WIFI_STATE_DISABLED : // 处理已经关闭Wi-Fi的状态
setSwitchBarChecked(false);
mSwitchWidget.setEnabled(true);
break;
default: // 处理其他情况
setSwitchBarChecked(false);
mSwitchWidget.setEnabled(true);
}
}
/packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java
// 如果当前Wi-Fi 的状态与Switch 控件的状态不一致,改变Switch 控件的状态
private void setSwitchBarChecked(boolean checked) {
mStateMachineEvent = true;
mSwitchWidget.setChecked(checked);
mStateMachineEvent = false;
}
Como se puede ver en setSwitchChecked
el código del método, antes de cambiar el estado del control Switch (llamar Switch.setChecked
al método), mStateMachineEvent
la variable primero se establece en verdadero. Esto significa que al configurar el estado de control del Switch, onCheckedChanged
el estado de Wi-Fi no se volverá a configurar porque el método se activa, por lo que se realiza una llamada recursiva.
A partir de este punto se puede ver que WifiEnabler
el propósito del receptor de transmisión en la clase es solo configurar el estado del control Switch, no configurar el estado de Wi-Fi. El propósito de esto es que cuando el usuario WifiManager.setWifiEnabled
configura el estado de Wi-Fi a través del método, aunque el estado de Wi-Fi se puede configurar correctamente, el estado del control del Switch no se puede cambiar, por lo que el estado actual de Wi-Fi será inconsistente. con el estado del control Switch.Case. Para resolver este problema, WifiManager.setWifiEnabled
después de configurar el estado de Wi-Fi, el método enviará una transmisión ( WifiManager.WIFI_STATE_CHANGED_ACTION
), y luego el receptor de transmisión en la clase WifiEnabler recibirá la transmisión y cambiará el estado del control del Switch de acuerdo con la conexión Wi-Fi actual. -Estado de conexión.
Escanear Wi-Fi
WifiSettings
Se activa la lógica para iniciar el escaneo .
WifiTracker
Después de recibir la transmisión del cambio de estado wifi,
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)) {
updateWifiState(
intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN));
} else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { // 处理热点搜索完成的动作
mStaleScanResults = false;
mLastScanSucceeded =
intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true);
// 更新搜索到的热点
fetchScansAndConfigsAndUpdateAccessPoints();
} else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
|| WifiManager.ACTION_LINK_CONFIGURATION_CHANGED.equals(action)) {
fetchScansAndConfigsAndUpdateAccessPoints();
} else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { // 网络状态变化动作
// TODO(sghuman): Refactor these methods so they cannot result in duplicate
// onAccessPointsChanged updates being called from this intent.
NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
updateNetworkInfo(info);
fetchScansAndConfigsAndUpdateAccessPoints();
} else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
updateNetworkInfo(/* networkInfo= */ null);
}
}
};
Si el wifi ya está activado, comience a escanear wifi
private void updateWifiState(int state) {
if (isVerboseLoggingEnabled()) {
Log.d(TAG, "updateWifiState: " + state);
}
if (state == WifiManager.WIFI\_STATE\_ENABLED) {
synchronized (mLock) {
if (mScanner != null) {
// We only need to resume if mScanner isn't null because
// that means we want to be scanning.
mScanner.resume();
}
}
}
mListener.onWifiStateChanged(state);
}
Scanner es una clase utilizada para escanear Wifi:
class Scanner extends Handler {
static final int MSG_SCAN = 0;
private int mRetry = 0;
// 通过resume 方法可以触发循环扫描热点
void resume () {
if (isVerboseLoggingEnabled()) {
Log.d(TAG, "Scanner resume");
}
if (!hasMessages(MSG_SCAN)) {
sendEmptyMessage(MSG_SCAN);
}
}
public void handleMessage(Message message) {
if (message.what != MSG_SCAN) return;
// 开始扫描热点
if (mWifiManager.startScan()) {
mRetry = 0;
} else if (++mRetry >= 3) {
mRetry = 0;
if (mContext != null) {
Toast.makeText(mContext, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show();
}
return;
}
// 发送延迟消息,会每10 秒搜索一次热点
sendEmptyMessageDelayed(MSG_SCAN, WIFI_RESCAN_INTERVAL_MS);
}
}
Primero, la acción de transmisión será procesada por el receptor de transmisión creado en la clase WifiSettings WifiManager.NETWORK_STATE_CHANGED_ACTION
y luego se llamará al método durante el procesamiento Scanner.resume
para comenzar a escanear puntos de acceso. Cuando se completa el escaneo del punto de acceso, el sistema envía WifiManager.SCAN_RESULTS_AVAILABLE_ACTION
una transmisión.
Llame fetchScansAndConfigsAndUpdateAccessPoints
al método para actualizar la lista de puntos de acceso. La actualización específica está en updateAccessPoints()
. El objeto aquí envía Scanner
continuamente mensajes retrasados en el método, de modo que el sistema escanea los puntos calientes cada cierto tiempo (10 segundos).Scanner. handleMessage
private void fetchScansAndConfigsAndUpdateAccessPoints() {
// 获取所有搜索到的热点信息
List<ScanResult> newScanResults = mWifiManager.getScanResults();
// Filter all unsupported networks from the scan result list
final List<ScanResult> filteredScanResults =
filterScanResultsByCapabilities(newScanResults);
// if (isVerboseLoggingEnabled()) {
Log.i(TAG, "Fetched scan results: " + filteredScanResults);
// }
// 获取当前已经连接的热点信息
List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
updateAccessPoints(filteredScanResults, configs);
}
updateAccessPoints(filteredScanResults, configs);
Se creará el objeto AccessPointer de la información del punto de acceso conectado, se llamará la información del estado de actualización , accessPoint.update
se agregará la información del punto de acceso al punto de acceso y, finalmente, se llamará a la interfaz de usuario de actualización WifiSettings
en la devolución de llamada;onAccessPointsChanged()
Conéctate a Wi-Fi
Cuando el usuario hace clic en un punto de acceso, aparecerá un cuadro de diálogo solicitando una contraseña y finalmente hace clic en el botón "Conectar" para conectarse. Específicamente en el WifiSettings
método submit
:
void submit(WifiConfigController configController) {
final WifiConfiguration config = configController.getConfig();
if (config == null) {
if (mSelectedAccessPoint != null
&& mSelectedAccessPoint.isSaved()) {
connect(mSelectedAccessPoint.getConfig(),
true /* isSavedNetwork */,
CONNECT_SOURCE_UNSPECIFIED);
}
} else if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) {
mWifiManager.save(config, mSaveListener);
} else {
mWifiManager.save(config, mSaveListener);
if (mSelectedAccessPoint != null) { // Not an "Add network"
connect(config, false /* isSavedNetwork */,
CONNECT_SOURCE_UNSPECIFIED);
}
}
mWifiTracker.resumeScanning();
}
protected void connect(final WifiConfiguration config,
boolean isSavedNetwork, @ConnectSource int connectSource) {
// Log subtype if configuration is a saved network.
mMetricsFeatureProvider.action(getContext(), SettingsEnums.ACTION_WIFI_CONNECT,
isSavedNetwork);
mConnectSource = connectSource;
// 连接Wifi
mWifiManager.connect(config, mConnectListener);
mClickedConnect = true;
}
Después de hacer clic en Conectar, si la configuración no es nula, la red se guardará primero y luego se conectará, por lo que incluso si la conexión falla, la red seguirá estando en la lista de redes guardadas.