Android开发笔记(六十)网络的检测与连接

检测网络

APP在访问网络之前,正常都要先检测网络状态,因为如果未连接网络就上网的话,常常导致超时等待。另外,APP有时也需区分当前网络是wifi环境还是数据连接环境,如果是数据连接环境,那么为了节省流量,一般不进行下载操作也不加载大图片;如果是wifi环境,那就都可以做而不必担心消耗流量。


ConnectivityManager就是用于检测网络连接的工具类,其对象从系统服务Context.CONNECTIVITY_SERVICE中获取。该类的常用方法是getActiveNetworkInfo,调用该方法返回一个NetworkInfo对象,下面是NetworkInfo的常用方法:
getType : 获取网络类型。ConnectivityManager.TYPE_WIFI表示wifi,ConnectivityManager.TYPE_MOBILE表示数据连接,ConnectivityManager.TYPE_WIMAX表示wimax,ConnectivityManager.TYPE_ETHERNET表示以太网,ConnectivityManager.TYPE_BLUETOOTH表示蓝牙。
getState : 获取网络状态。State.CONNECTING表示正在连接,State.CONNECTED表示已连接,State.SUSPENDED表示挂起,State.DISCONNECTING表示正在断开,State.DISCONNECTED表示已断开,State.UNKNOWN表示未知。
getSubtype : 获取网络子类型。当网络类型为数据连接时,子类型为2G/3G/4G的细分类型,如CDMA、EVDO、HSDPA、LTE等等。


当网络类型是wifi时,要想获取详细的wifi信息,又得使用WifiManager,该类的对象从系统服务Context.WIFI_SERVICE中获取。下面是WifiManager的常用网络检测方法:
isWifiEnabled : 判断WLAN功能是否开启
setWifiEnabled : 开关WLAN功能
getWifiState : 获取当前wifi的状态。WIFI_STATE_DISABLED表示已断开,WIFI_STATE_DISABLING表示正在断开,WIFI_STATE_ENABLED表示已连上,WIFI_STATE_ENABLING表示正在连接,WIFI_STATE_UNKNOWN表示未知。
getConnectionInfo : 获取当前wifi的连接信息。该方法返回一个WifiInfo对象,WifiInfo可通过相应的get方法获取如下信息:wifi名称、路由器MAC、WIFI信号强度、连接速率、IP地址、MAC地址、网络编号等等。


连接wifi

下面是WifiManager的常用网络检测方法:
startScan : 开始扫描周围的wifi信息。
getScanResults : 获取周围wifi的扫描结果。
calculateSignalLevel : 根据信号强度计算信号等级。
getConfiguredNetworks : 获取已配置的网络信息。
addNetwork : 添加指定wifi配置。
enableNetwork : 启用指定wifi。第二个参数表示是否同时禁用其他的wifi
disableNetwork : 禁用指定wifi。
disconnect : 断开当前wifi。


要连上某个具体的wifi,实际开发中的调用顺序为:首先调用startScan开始扫描周围wifi,然后调用getScanResults获取扫描的wifi列表,接着通过getConfiguredNetworks查找已配置的网络信息;如果找到指定的网络配置,则调用enableNetwork启用该wifi;如果没找到指定wifi配置,则先调用addNetwork添加wifi配置(addNetwork会返回一个网络ID来标识刚添加的wifi),然后调用enableNetwork启用该wifi。
需要注意的是,在addNetwork之前还得创建新的wifi配置信息,即一个WifiConfiguration实例。WifiConfiguration的赋值比较难懂,就不费口舌了,直接上代码:
	private WifiConfiguration createWifiInfo(String ssid, String password, int type) {
		WifiConfiguration config = new WifiConfiguration();
		config.SSID = "\"" + ssid + "\"";
		if (type == WifiConfiguration.KeyMgmt.NONE) {  //明文密码
			config.wepKeys[0] = "";
			config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
			config.wepTxKeyIndex = 0;
		} else {  //WPA加密或者WPA2加密
			config.preSharedKey = "\"" + password + "\"";
			config.hiddenSSID = true;
			config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
			config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
			config.allowedKeyManagement.set(type);
			config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
			config.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
			config.status = WifiConfiguration.Status.ENABLED;
		}
		return config;
	}


若要断开当前的wifi连接,则既可调用disableNetwork方法,也可调用disconnect方法。disconnect与disableNetwork的区别在于:disableNetwork不但断开连接,并且此后也不会自动重连;而disconnect只是断开本次连接,不会阻止将来的自动重连。


反射reflect

Android因为自身在不断更新升级,同时新技术也是层出不穷,所以并没有把所有的公共方法开放出来。如果我们查看Android的sdk源码,会发现少数函数被标记了hide,表示该函数虽然是public但尚未正式开放,可能是不稳定或者有待完善。可是有时我们又确实需要调用这些隐藏方法,就得通过java的反射机制来间接实现。反射机制指的是在运行过程中,程序对于任意一个类,都知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,而不被hide标记所束缚。


关于反射机制的原理说来话长,我们还是直接说说它的实际应用。首先获取目标类的Class对象,可通过调用对象的getClass方法或者调用Class.forName方法;其次调用该Class对象的getMethod方法,这里需要指定将要访问的方法名、方法参数(先传入参数的类型)等等;再次调用Method的invoke方法,即输入Class对象,以及各参数的具体取值;最后获取invoke的返回值,也就是方法调用的返回结果。


下面是实际开发中运用反射的两个代码例子:
1、开关个人热点
      Method method = wifiMgr.getClass().getMethod(
              "setWifiApEnabled", WifiConfiguration.class, Boolean.TYPE);
      Boolean result = (Boolean) method.invoke(wifiMgr, apConfig, true);
2、获取手机序列号
			Class<?> c = Class.forName("android.os.SystemProperties");
			Method get = c.getMethod("get", String.class);
			String serial = (String) get.invoke(c, "ro.serialno");


如果大家留心,可能发现前面的一些博文已经运用了反射机制,具体说明如下:
1、SignalStrength类获取LTE信号的相关方法,如getLteSignalStrength、getLteRsrp、getLteRsrq等等,参见《 Android开发笔记(四十六)手机相关事件》
2、TelephonyManager类获取网络大类与名称的相关方法,如getNetworkClass和getNetworkTypeName,参见《 Android开发笔记(五十五)手机设备基本操作》
3、ConnectivityManager类数据连接的相关方法,如getMobileDataEnabled和setMobileDataEnabled,参见《 Android开发笔记(五十五)手机设备基本操作》
4、WifiManager类管理热点的相关方法,如setWifiApEnabled、getWifiApState、getWifiApConfiguration等等,参见《 Android开发笔记(六十)网络的检测与连接》
5、StorageManager类管理存储的相关方法,如getVolumePaths等等,参见《 Android开发笔记(七十九)资源与权限校验》


个人热点

Android支持把手机变成一个wifi热点,其他手机可接入该手机的wifi,从而共享服务端手机的数据流量。下面是WifiManager中与热点相关的方法(注意这些方法都是隐藏的,得通过反射机制来调用):
setWifiApEnabled : 开关热点。true表示开启,false表示关闭。
getWifiApState : WIFI_AP_STATE_DISABLED表示已断开,WIFI_AP_STATE_DISABLING表示正在断开,WIFI_AP_STATE_ENABLED表示已连接,WIFI_AP_STATE_ENABLING表示正在连接,WIFI_AP_STATE_FAILED表示开关失败。
isWifiApEnabled : 判断热点是否启用。只有已连接状态才返回true,其余都返回false。
getWifiApConfiguration : 获取热点的配置信息。
setWifiApConfiguration : 设置热点的配置信息。


因为热点管理本身就不是很完善,所以还存在一些目前无法解决的问题。下面是热点编码的几个注意事项:
1、wifi和热点不能同时打开,所以打开热点的时候需要关闭wifi。
2、热点的配置信息主要有:热点名称、热点密码、加密方式(常用的有明文、WPA、WPA2三种)。如果更改了热点配置,新配置并不会立即生效;只能先关闭热点,然后使用新配置开启热点,新配置才会生效。
3、要想查看连上本机热点的设备,可定期扫描系统文件/proc/net/arp,该文件保存了与本机连接的设备列表。可是这些设备并不一定都真正连上,所以还得检测对方IP是否连通。检测连通性可使用InetAddress的isReachable,不过实际测试发现,部分笔记本电脑已连上,但isReachable结果却是连不上。此时又得调用系统的ping命令,如果ping得通,那么不管isReachable结果为何,都算是已连上。


下面是热点管理的几个尚待解决的问题(至少博主目前没办法,若有朋友解决了还请不吝赐教):
1、/proc/net/arp能找到已连接设备的IP和MAC,却找不到对方设备的真实名称(文件中有名称字段,可是实际测试发现该字段都是ap0或者wlan0这种笼统名称);
2、WifiManager没有阻止某个设备连接热点的方法,其他公开的api也没发现能够实现该功能的方法;
3、无法查看每个设备的流量使用情况。linux下有个iftop工具可以统计每个设备的设备流量,Android作为一个瘦身版的linux,虽然也有iftop工具,但是功能简单没法统计单个的流量。
对于以上问题,有的机型可以支持,有的不能支持,不知道支持的机型是不是改写了Android的内核源码。


代码示例

下面是检测网络的代码示例:
import com.example.exmnetwork.util.Utils;
import com.example.exmnetwork.util.IPv4Util;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.telephony.TelephonyManager;
import android.widget.TextView;

public class InfoActivity extends Activity {

	private static final String TAG = "InfoActivity";
	private ConnectivityManager mConnectMgr;
	private TelephonyManager mTelMgr;
	
	private TextView tv_info;
	private TextView tv_all;
	private Handler mHandler = new Handler();
	private String[] mNetStateArray = {"正在连接", "已连接", "暂停", 
			"正在断开", "已断开", "未知"};
	private String[] mWifiStateArray = {"正在断开", "已断开", "正在连接", 
			"已连接", "未知"};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_info);

		tv_info = (TextView) findViewById(R.id.tv_info);
		tv_all = (TextView) findViewById(R.id.tv_all);
		mHandler.postDelayed(mRefresh, 50);;
	}

	private Runnable mRefresh = new Runnable() {
		@Override
		public void run() {
			getAvailableNet();
			getAllNetwork();
			mHandler.postDelayed(this, 1000);;
		}
	};

	@SuppressLint("DefaultLocale")
	private void getAvailableNet() {
		String desc = "";
		mTelMgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
		mConnectMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo info = mConnectMgr.getActiveNetworkInfo();
		if (info != null && info.getState() == NetworkInfo.State.CONNECTED) {
			if (info.getType() == ConnectivityManager.TYPE_WIFI) {
				WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
		        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
		        int state = wifiManager.getWifiState();
		        String SSID = wifiInfo.getSSID();
		        if (SSID == null || SSID.length()<=0 || SSID.indexOf("unknown")>=0) {
		        	desc = "\n当前联网的网络类型是WIFI,但未成功连接已知的wifi信号";
		        } else {
		        	desc = String.format("%s当前联网的网络类型是WIFI,", desc);
		        	desc = String.format("%s状态是%s。\n", desc, mWifiStateArray[state]);
		        	desc = String.format("%s WIFI名称是:%s\n", desc, SSID);
		        	desc = String.format("%s 路由器MAC是:%s\n", desc, wifiInfo.getBSSID());
		        	desc = String.format("%s WIFI信号强度是:%s\n", desc, wifiInfo.getRssi());
		        	desc = String.format("%s 连接速率是:%s\n", desc, wifiInfo.getLinkSpeed());
		        	desc = String.format("%s IP地址是:%s\n", desc, IPv4Util.intToIp(wifiInfo.getIpAddress()));
		        	desc = String.format("%s MAC地址是:%s\n", desc, wifiInfo.getMacAddress());
		        	desc = String.format("%s 网络编号是:%s\n", desc, wifiInfo.getNetworkId());
		        }
			} else if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
				int net_type = info.getSubtype();
				desc = String.format("\n当前联网的网络类型是%s %s", Utils.getNetworkTypeName(mTelMgr, net_type), Utils.getClassName(mTelMgr, net_type));
			} else {
				desc = String.format("\n当前联网的网络类型是%d", info.getType());
			}
		} else {
			desc = "\n当前无上网连接";
		}
		tv_info.setText(desc);
	}
	
	private void getAllNetwork() {
		String desc = "";
		NetworkInfo[] info = mConnectMgr.getAllNetworkInfo();
		if (info != null) {
			for (int i = 0; i < info.length; i++) {
				//NetworkInfo.State.CONNECTED 此状态表示已连接
				desc = String.format("%s%s %s %s\n", desc, mNetStateArray[info[i].getState().ordinal()],
						info[i].getTypeName(), info[i].getSubtypeName());
			}
			tv_all.setText(desc);
		}
	}

}


下面是连接wifi的代码示例:
import java.util.ArrayList;
import java.util.List;

import com.example.exmnetwork.adapter.WifiListAdapter;
import com.example.exmnetwork.bean.WifiConnect;
import com.example.exmnetwork.dialog.InputDialogFragment.InputCallbacks;
import com.example.exmnetwork.util.Utils;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

@SuppressLint("DefaultLocale")
public class WifiActivity extends Activity implements OnCheckedChangeListener,InputCallbacks {

	private static final String TAG = "WifiActivity";
	private WifiManager mWifiManager;
	private TextView tv_result;
	private TextView tv_config;
	private ListView lv_wifi;
	private CheckBox ck_wlan;
	private Handler mHandler = new Handler();

	@Override
	protected void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_wifi);

		tv_result = (TextView) findViewById(R.id.tv_result);
		tv_config = (TextView) findViewById(R.id.tv_config);
		lv_wifi = (ListView) findViewById(R.id.lv_wifi);
		ck_wlan = (CheckBox) findViewById(R.id.ck_wlan);
		if (Utils.getWlanStatus(this) == true) {
			ck_wlan.setChecked(true);
		}
		ck_wlan.setOnCheckedChangeListener(this);
		
		mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
		mHandler.postDelayed(mRefresh, 50);;
	}

	@Override
	public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
		if (buttonView.getId() == R.id.ck_wlan) {
			Utils.setWlanStatus(this, isChecked);
		}
	}
  
	private void setWifiList() {
        ArrayList<WifiConnect> wifiList = new ArrayList<WifiConnect>();
        WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
        int state = mWifiManager.getWifiState();
        String SSID = "";
        if (state==WifiManager.WIFI_STATE_ENABLED || state==WifiManager.WIFI_STATE_ENABLING) {
        	SSID = wifiInfo.getSSID();
        } else {
    		WifiListAdapter wifiAdapter = new WifiListAdapter(this, mWifiManager, wifiList);
    		lv_wifi.setAdapter(wifiAdapter);
        	return;
        }
        
		mWifiManager.startScan();
    	ArrayList<ScanResult> newResultList = getResultList();
    	List<WifiConfiguration> configList = mWifiManager.getConfiguredNetworks();
    	for(int i=0; i<newResultList.size(); i++) {
    		ScanResult item = newResultList.get(i);
    		WifiConnect wifi = new WifiConnect();
    		wifi.SSID = item.SSID;
    		wifi.level = WifiManager.calculateSignalLevel(item.level, 4);
    		if (SSID.indexOf(wifi.SSID) >= 0) {
    			wifi.status = true;
    		}
    		if (item.capabilities.toUpperCase().indexOf("WPA2") >= 0) {
    			wifi.type = 4;
    		} else if (item.capabilities.toUpperCase().indexOf("WPA") >= 0) {
    			wifi.type = WifiConfiguration.KeyMgmt.WPA_PSK;
    		} else {
    			wifi.type = WifiConfiguration.KeyMgmt.NONE;
    		}
        	for(int j=0; j<configList.size(); j++) {
        		if (configList.get(j).SSID.indexOf(wifi.SSID) >= 0) {
        			wifi.networkId = configList.get(j).networkId;
        			break;
        		}
        	}
        	wifiList.add(wifi);
    	}
		WifiListAdapter wifiAdapter = new WifiListAdapter(this, mWifiManager, wifiList);
		lv_wifi.setAdapter(wifiAdapter);
	}
	
	private ArrayList<ScanResult> getResultList() {
    	List<ScanResult> resultList = mWifiManager.getScanResults();
    	ArrayList<ScanResult> newResultList = new ArrayList<ScanResult>();
    	for(int i=0; i<resultList.size(); i++) {
    		ScanResult item = resultList.get(i);
    		int j;
        	for(j=0; j<newResultList.size(); j++) {
        		ScanResult newItem = newResultList.get(j);
        		if (item.SSID.equals(newItem.SSID)) {
        			if (item.level>newItem.level) {
            			newResultList.set(j, item);
        			}
        			break;
        		}
        	}
        	if (j >= newResultList.size()) {
        		newResultList.add(item);
        	}
    	}
    	return newResultList;
	}

	private Runnable mRefresh = new Runnable() {
		@Override
		public void run() {
			//scanWifi();
			setWifiList();
			mHandler.postDelayed(this, 3000);;
		}
	};

	@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
	private void scanWifi() {
		tv_result.setVisibility(View.VISIBLE);
		tv_config.setVisibility(View.VISIBLE);
		mWifiManager.startScan();
    	ArrayList<ScanResult> newResultList = getResultList();
    	String resultStr = String.format("扫描发现%d个wifi信息\n", newResultList.size());
    	for(int i=0; i<newResultList.size(); i++) {
    		ScanResult item = newResultList.get(i);
    		resultStr = String.format("%s名称%s,MAC地址%s,描述%s,时间%s,信号强度%d,频率%dMHz\n", 
    				resultStr, item.SSID, item.BSSID, item.capabilities, Utils.getTimeStamp(item.timestamp), item.level, item.frequency);
    	}
		tv_result.setText(resultStr);

    	List<WifiConfiguration> configList = mWifiManager.getConfiguredNetworks();
    	String configStr = String.format("扫描发现%d个wifi配置\n", configList.size());
    	for(int i=0; i<configList.size(); i++) {
    		WifiConfiguration item = configList.get(i);
    		configStr = String.format("%s名称%s,MAC地址%s,网络编号%d,状态%d,密码%s,优先级%d,类型%s\n", 
    				configStr, item.SSID, item.BSSID, item.networkId, item.status, item.preSharedKey, item.priority, item.allowedKeyManagement);
    	}
    	tv_config.setText(configStr);
	}

	@Override
	public void onInput(String ssid, String password, int type) {
		WifiConfiguration config = createWifiInfo(ssid, password, type);
		int netId = mWifiManager.addNetwork(config);
		if (netId == -1) {
			Toast.makeText(this, "密码错误", Toast.LENGTH_LONG).show();
		} else {
			mWifiManager.enableNetwork(netId, true);
		}
	}

	private WifiConfiguration createWifiInfo(String ssid, String password, int type) {
		WifiConfiguration config = new WifiConfiguration();
		config.SSID = "\"" + ssid + "\"";
		if (type == WifiConfiguration.KeyMgmt.NONE) {  //明文密码
			config.wepKeys[0] = "";
			config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
			config.wepTxKeyIndex = 0;
		} else {  //WPA加密或者WPA2加密
			config.preSharedKey = "\"" + password + "\"";
			config.hiddenSSID = true;
			config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
			config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
			config.allowedKeyManagement.set(type);
			config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
			config.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
			config.status = WifiConfiguration.Status.ENABLED;
		}
		return config;
	}

}


下面是个人热点管理的代码示例:
import java.util.ArrayList;
import java.util.BitSet;

import com.example.exmnetwork.adapter.ClientListAdapter;
import com.example.exmnetwork.bean.ClientScanResult;
import com.example.exmnetwork.util.ApUtil;

import android.app.Activity;
import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.text.Selection;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ListView;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

public class ApActivity extends Activity implements OnClickListener,OnLongClickListener,OnCheckedChangeListener {

	private static final String TAG = "ApActivity";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_ap);
		initView();
		initWifi();
	}

	private CheckBox ck_ap_switch;
	private TextView tv_ap_connect, tv_ap_device;
	private EditText et_ap_name, et_ap_password;
	private Spinner sp_ap_des;
	private Button btn_ap_cancel, btn_ap_save; 
	private ListView lv_client;
    private WifiManager mWifiManager;
    private WifiConfiguration mApConfig;
    private int mDesType = 0, mTmpDesType = 0;
    private String mApName = "", mApPassword = "";
    
	private void initView() {
		ck_ap_switch = (CheckBox) findViewById(R.id.ck_ap_switch);
		tv_ap_connect = (TextView) findViewById(R.id.tv_ap_connect);
		tv_ap_device = (TextView) findViewById(R.id.tv_ap_device);
		et_ap_name = (EditText) findViewById(R.id.et_ap_name);
		et_ap_password = (EditText) findViewById(R.id.et_ap_password);
		btn_ap_cancel = (Button) findViewById(R.id.btn_ap_cancel);
		btn_ap_save = (Button) findViewById(R.id.btn_ap_save);
		lv_client = (ListView) findViewById(R.id.lv_client);

		btn_ap_cancel.setOnClickListener(this);
		btn_ap_save.setOnClickListener(this);
		et_ap_name.setOnLongClickListener(this);
		et_ap_password.setOnLongClickListener(this);
		et_ap_name.addTextChangedListener(new MyTextWatcher(et_ap_name, et_ap_password));
		et_ap_password.addTextChangedListener(new MyTextWatcher(et_ap_password, sp_ap_des));

		sp_ap_des = (Spinner) findViewById(R.id.sp_ap_des);
		ArrayAdapter<String> starAdapter = new ArrayAdapter<String>(this,
				R.layout.spinner_item, desTextArray);
		starAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
		sp_ap_des.setAdapter(starAdapter);
		sp_ap_des.setSelection(mDesType);
		sp_ap_des.setPrompt("请选择加密方式");
		sp_ap_des.setOnItemSelectedListener(new DesSelectedListener());

	}

	private String[] desTextArray = {"无", "WPA PSK", "WPA2 PSK"};
	private int[] desTypeArray = {WifiConfiguration.KeyMgmt.NONE, 
			WifiConfiguration.KeyMgmt.WPA_PSK, 4};
	private ArrayList<ClientScanResult> mClientArray = new ArrayList<ClientScanResult>();
	
	class DesSelectedListener implements OnItemSelectedListener {
		@Override
		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
			mTmpDesType = arg2;
		}

		@Override
		public void onNothingSelected(AdapterView<?> arg0) {
		}
	}

	class MyTextWatcher implements TextWatcher {
		private EditText mThisView = null;
		private View mNextView = null;
		 
		public MyTextWatcher(EditText vThis, View vNext) {
			super();
			mThisView = vThis;
			if (vNext != null) {
				mNextView = vNext;
			}
		}
		
		@Override
		public void beforeTextChanged(CharSequence s, int start, int count, int after) {
		}

		@Override
		public void onTextChanged(CharSequence s, int start, int before, int count) {
		}

		@Override
		public void afterTextChanged(Editable s) {
			String str = s.toString();
			if (str.indexOf("\r") >= 0 || str.indexOf("\n") >= 0) {
				mThisView.setText(str.replace("\r", "").replace("\n", ""));
				if (mNextView != null) {
					mNextView.requestFocus();
					if (mNextView instanceof EditText) {
						EditText et = (EditText)mNextView;
						Editable edit = et.getText();
						Selection.setSelection(edit, edit.length());
					}
				}
			}
		}
	 }
	private void initWifi() {
        mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        mApConfig = new WifiConfiguration();
        int apStatus = ApUtil.getWifiApState(mWifiManager);
		if (apStatus == ApUtil.WIFI_AP_STATE_ENABLED) {
			readSystemAp();
		} else {
			mApName = Build.SERIAL;
			mApPassword = "12345678";
			mDesType = mTmpDesType = WifiConfiguration.KeyMgmt.WPA_PSK;
		}

		mApConfig.SSID = mApName;
        et_ap_name.setText(mApName);
        et_ap_password.setText(mApPassword);
		sp_ap_des.setSelection(mDesType);
        refreshStatus();
        if (apStatus==ApUtil.WIFI_AP_STATE_ENABLING || apStatus==ApUtil.WIFI_AP_STATE_ENABLED) {
        	ck_ap_switch.setChecked(true);
        }
		ck_ap_switch.setOnCheckedChangeListener(this);
	}
	
	private void readSystemAp() {
		WifiConfiguration apConfig = ApUtil.getWifiApConfiguration(mWifiManager);
		if (apConfig!=null && apConfig.SSID!=null && apConfig.allowedKeyManagement!=null) {
			mApName = apConfig.SSID;
			BitSet bit = apConfig.allowedKeyManagement;
			if (bit.get(WifiConfiguration.KeyMgmt.NONE) == true) {
				mDesType = mTmpDesType = WifiConfiguration.KeyMgmt.NONE;
				mApPassword = apConfig.wepKeys[0];
			} else if (bit.get(WifiConfiguration.KeyMgmt.WPA_PSK) == true) {
				mDesType = mTmpDesType = WifiConfiguration.KeyMgmt.WPA_PSK;
				mApPassword = apConfig.preSharedKey;
			} else if (bit.get(4) == true) {
				mDesType = mTmpDesType = 2;  //desTypeArray第三个元素的值就是4
				mApPassword = apConfig.preSharedKey;
			}
		}
	}
	
	@Override
	public void onStart() {
		mRefreshHandler.postDelayed(mStatusRefresh, 25);
		mRefreshHandler.postDelayed(mClientRefresh, 50);
		super.onStart();
	}
	
	@Override
	public void onStop() {
		mRefreshHandler.removeCallbacks(mStatusRefresh);
		mRefreshHandler.removeCallbacks(mClientRefresh);
		super.onStop();
	}

	private final Handler mRefreshHandler = new Handler();
	private Runnable mStatusRefresh = new Runnable() {
		@Override
		public void run() {
	        refreshStatus();
			mRefreshHandler.postDelayed(this, 1000);
		}
	};

	private Runnable mClientRefresh = new Runnable() {
		@Override
		public void run() {
	        (new ClientScanThread()).start(); 
			mRefreshHandler.postDelayed(this, 3000);
		}
	};

	private Runnable mReOpenRefresh = new Runnable() {
		@Override
		public void run() {
			int ap_status = ApUtil.getWifiApState(mWifiManager);
	        if (ap_status == ApUtil.WIFI_AP_STATE_DISABLED) {
				ck_ap_switch.setChecked(true);
	        } else {
				mRefreshHandler.postDelayed(this, 2000);
	        }
		}
	};

	private int mLastStatus = 20;
	private void refreshStatus() {
        int apStatus = ApUtil.getWifiApState(mWifiManager);
        if (apStatus == mLastStatus) {
        	return;
        }
        Log.d(TAG, "apStatus="+apStatus+", mLastStatus="+mLastStatus+", mName="+mApName);
        mLastStatus = apStatus;
        if (apStatus == ApUtil.WIFI_AP_STATE_ENABLING) {
        	tv_ap_connect.setText("正在开启热点......");
        	tv_ap_connect.setVisibility(View.VISIBLE);
        	ck_ap_switch.setEnabled(false);
        } else if (apStatus == ApUtil.WIFI_AP_STATE_ENABLED) {
        	tv_ap_connect.setText("热点"+mApName+"已开启");
        	tv_ap_connect.setVisibility(View.VISIBLE);
        	ck_ap_switch.setEnabled(true);
        	if (ck_ap_switch.isChecked() == false) {
            	ck_ap_switch.setChecked(true);
        	}
        } else if (apStatus==ApUtil.WIFI_AP_STATE_DISABLING) {
        	tv_ap_connect.setText("正在断开热点......");
        	tv_ap_connect.setVisibility(View.VISIBLE);
        	ck_ap_switch.setEnabled(false);
        } else {
        	tv_ap_connect.setVisibility(View.GONE);
        	ck_ap_switch.setEnabled(true);
        	if (ck_ap_switch.isChecked() == true) {
            	ck_ap_switch.setChecked(false);
        	}
        }
	}
	
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.btn_ap_cancel:
			et_ap_name.setText(mApName);
			et_ap_password.setText(mApPassword);
			sp_ap_des.setSelection(mDesType);
			Toast.makeText(this, "已取消本次热点设置", Toast.LENGTH_LONG).show();
			break;
		case R.id.btn_ap_save:
			if (et_ap_name.getText().length() < 4) {
				Toast.makeText(this, "热点名称长度需不小于四位", Toast.LENGTH_LONG).show();
				return;
			}
			if (mTmpDesType!=0 && et_ap_password.getText().length() < 8) {
				Toast.makeText(this, "热点密码长度需不小于八位", Toast.LENGTH_LONG).show();
				return;
			}
			mApName = et_ap_name.getText().toString();
			mApPassword = et_ap_password.getText().toString();
			mDesType = mTmpDesType;
			Toast.makeText(this, "已保存本次热点设置", Toast.LENGTH_LONG).show();
			if (ck_ap_switch.isChecked() == true) {  ////只有当前已开启热点的,才需要断开并重连。当前未开启热点的,保存设置后不自动开热点
				ck_ap_switch.setChecked(false);
				mRefreshHandler.postDelayed(mReOpenRefresh, 1000);
			}
			break;
		default:
			break;
		}
	}
	
	@Override
	public boolean onLongClick(View v) {
		if (v.getId() == R.id.et_ap_name) {
			et_ap_name.setText("");
		} else if (v.getId() == R.id.et_ap_password) {
			et_ap_password.setText("");
		}
		return false;
	}
	
	private void resetApConfig() {
		mApConfig.allowedKeyManagement.clear();
		mApConfig.SSID = mApName;
		if (mDesType == 0) {
			mApConfig.preSharedKey = "";
	        mApConfig.wepKeys[0] = mApPassword;
	        mApConfig.wepTxKeyIndex = 0;
		} else {
			mApConfig.allowedKeyManagement.set(desTypeArray[mDesType]);
			mApConfig.preSharedKey = mApPassword;
	        mApConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
	        mApConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
	        mApConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
	        mApConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
	        mApConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
	        mApConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
		}
	}
	
	@Override
	public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
		int id = buttonView.getId();
		if (id == R.id.ck_ap_switch) {
			String desc = "";
			if (isChecked == false) {
				desc = ApUtil.setWifiApEnabled(mWifiManager, mApConfig, isChecked);
			} else {
				mDesType = mTmpDesType;
				resetApConfig();
				desc = ApUtil.setWifiApEnabled(mWifiManager, mApConfig, isChecked);
			}
        	Log.d(TAG, desc);
			if (desc!=null && desc.length()>0) {
				Toast.makeText(this, desc, Toast.LENGTH_LONG).show();
				ck_ap_switch.setChecked(!isChecked);
			}
		}
	}

	private class ClientScanThread extends Thread {
		@Override
		public void run() {
			mClientArray = ApUtil.getClientList(true);
			Message message = mScanHandler.obtainMessage();
			message.what = 99;
			mScanHandler.sendMessage(message);
		}
	}
	
	private int mClientCount = 9;
	
	private Handler mScanHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			mScanHandler.removeMessages(99);
			Log.d(TAG, "mClientArray.size()="+mClientArray.size());
			int ap_status = ApUtil.getWifiApState(mWifiManager);
	        if (ap_status!=ApUtil.WIFI_AP_STATE_ENABLING && ap_status!=ApUtil.WIFI_AP_STATE_ENABLED) {
	        	mClientArray.clear();
	        } else if (mClientArray == null) {
	        	mClientArray = new ArrayList<ClientScanResult>();
	        }
	        if (mClientCount!=9 && mClientCount!=mClientArray.size()) { //连续两次设备数量相同才更新,避免Android间歇性抽风
		        mClientCount = mClientArray.size();
	        	return;
	        }
			String text = String.format("已连接设备(%d)", mClientArray.size());
			tv_ap_device.setText(text);

			ClientListAdapter clientAdapter = new ClientListAdapter(ApActivity.this, mClientArray);
			lv_client.setAdapter(clientAdapter);
		}
	};

}





点此查看Android开发笔记的完整目录

你可能感兴趣的:(android,反射,reflect,wifi,热点)