網(wǎng)絡(luò)連接處理
在說WiFi之前席楚,先來說說網(wǎng)絡(luò)連接處理祟峦。
在Android開發(fā)過程中卧蜓,對(duì)于一個(gè)需要連接網(wǎng)絡(luò)的Android設(shè)備妓蛮,對(duì)設(shè)備的網(wǎng)絡(luò)狀態(tài)檢測(cè)是很有必要的!有很多的App都需要連接網(wǎng)絡(luò)殿衰。判斷設(shè)備是否已經(jīng)連接網(wǎng)絡(luò)朱庆,并且在連接網(wǎng)絡(luò)的狀態(tài)下判斷是wifi無線連接還是GPRS手機(jī)網(wǎng)絡(luò)連接,這樣就可以在不同的網(wǎng)絡(luò)連接下去調(diào)用不同的方法闷祥,處理不同的事情娱颊。
現(xiàn)在app大多都需要從網(wǎng)絡(luò)上獲得數(shù)據(jù)。所以訪問網(wǎng)絡(luò)是在所難免凯砍。但是在訪問網(wǎng)絡(luò)之前箱硕,我們應(yīng)該先做一下網(wǎng)絡(luò)的狀態(tài)判斷。其實(shí)在訪問網(wǎng)絡(luò)之前我們要做一些狀態(tài)判斷悟衩,對(duì)應(yīng)一些狀態(tài)判斷來做處理剧罩,并不是直接使用Http訪問網(wǎng)絡(luò)即可。很多人在開發(fā)就經(jīng)常把網(wǎng)絡(luò)這塊直接跳過座泳,直接訪問網(wǎng)絡(luò)惠昔,一旦斷網(wǎng),各種體驗(yàn)效果不好挑势,不是說app沒法用镇防,只是體驗(yàn)效果差。還有潮饱,就是我們可能為用戶考慮来氧,因?yàn)楝F(xiàn)在一般連網(wǎng)是wifi和手機(jī)流量,都知道后者收費(fèi)是比較高的饼齿。假如我們的app加載的圖片或者有大的數(shù)據(jù)下載操作饲漾,可是用戶的本意是要是在流量下的話就不要操作這些很費(fèi)流量的的操作,這樣就必須要我們做一些連網(wǎng)狀態(tài)的判斷缕溉。網(wǎng)絡(luò)是否連接良好考传,連接的wifi還是流量,斷網(wǎng)或者網(wǎng)絡(luò)改變了的時(shí)候怎么做证鸥,這都是一些細(xì)節(jié)僚楞,但是要注意處理。
查看當(dāng)前網(wǎng)絡(luò)狀態(tài)需要的權(quán)限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
允許查看當(dāng)前網(wǎng)絡(luò)狀態(tài)枉层,比如是3G還是WIFI上網(wǎng)泉褐。
連接管理
涉及的常用類
ConnectivityManager
翻譯成中文即:網(wǎng)絡(luò)連接管理者
主要作用:
- 監(jiān)聽手機(jī)網(wǎng)絡(luò)狀態(tài)(包括GPRS,WIFI鸟蜡, UMTS等)
- 手機(jī)狀態(tài)發(fā)生改變時(shí)膜赃,發(fā)送廣播
- 當(dāng)一個(gè)網(wǎng)絡(luò)連接失敗時(shí)進(jìn)行故障切換
- 為應(yīng)用程序提供可以獲取可用網(wǎng)絡(luò)的高精度和粗糙的狀態(tài)
獲取ConnectivityManager
ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo
翻譯成中文即:網(wǎng)絡(luò)的狀態(tài)信息
獲取NetworkInfo
通過ConnectivityManager獲取
ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
主要方法:
- getDetailedState():獲取詳細(xì)狀態(tài)。
- isAvailable():判斷該網(wǎng)絡(luò)是否可用,是否可以尋找到網(wǎng)絡(luò)
- isConnected():判斷是否已經(jīng)連接
- isConnectedOrConnecting():判斷是否已經(jīng)連接或正在連接揉忘。
- getState() 獲取連接狀態(tài)
- getExtraInfo():獲取附加信息跳座。
- getType():獲取網(wǎng)絡(luò)類型(一般為移動(dòng)(0)或Wi-Fi(1))端铛。
- getTypeName():獲取網(wǎng)絡(luò)類型名稱(一般取值“WIFI”或“MOBILE”)。
- getReason():獲取連接失敗的原因疲眷。
- isFailover():判斷是否連接失敗禾蚕。
- isRoaming():判斷是否漫游
注意:
1.當(dāng)用wifi上的時(shí)候
getType是WIFI
getExtraInfo是空的
2.當(dāng)用手機(jī)上的時(shí)候
getType是MOBILE
3.用移動(dòng)CMNET方式
getExtraInfo的值是cmnet
4.用移動(dòng)CMWAP方式
getExtraInfo的值是cmwap,但是不在代理的情況下訪問普通的網(wǎng)站訪問不了
5.用聯(lián)通3gwap方式
getExtraInfo的值是3gwap
6.用聯(lián)通3gnet方式
getExtraInfo的值是3gnet
7.用聯(lián)通uniwap方式
getExtraInfo的值是uniwap
8.用聯(lián)通uninet方式
getExtraInfo的值是uninet
TelephonyManager
TelephonyManager類主要提供了一系列用于訪問與手機(jī)通訊相關(guān)的狀態(tài)和信息的get方法狂丝。其中包括手機(jī)SIM的狀態(tài)和信息换淆、電信網(wǎng)絡(luò)的狀態(tài)及手機(jī)用戶的信息。在應(yīng)用程序中可以使用這些get方法獲取相關(guān)數(shù)據(jù)几颜。
需要添加權(quán)限:
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
第一步倍试,也要像ConnectivityManager一樣獲取管理器:
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
常用方法:
- getCallState()
CALL_STATE_IDLE 無任何狀態(tài)時(shí)
CALL_STATE_OFFHOOK 接起電話時(shí)
CALL_STATE_RINGING 電話進(jìn)來時(shí) - getDataActivity()
DATA_ACTIVITY_IN 數(shù)據(jù)連接狀態(tài):活動(dòng),正在接受數(shù)據(jù)
DATA_ACTIVITY_OUT 數(shù)據(jù)連接狀態(tài):活動(dòng)蛋哭,正在發(fā)送數(shù)據(jù)
DATA_ACTIVITY_INOUT 數(shù)據(jù)連接狀態(tài):活動(dòng)易猫,正在接受和發(fā)送數(shù)據(jù)
DATA_ACTIVITY_NONE 數(shù)據(jù)連接狀態(tài):活動(dòng),但無數(shù)據(jù)發(fā)送和接受 - getDataState()
DATA_CONNECTED 數(shù)據(jù)連接狀態(tài):已連接
DATA_CONNECTING 數(shù)據(jù)連接狀態(tài):正在連接
DATA_DISCONNECTED 數(shù)據(jù)連接狀態(tài):斷開
DATA_SUSPENDED 數(shù)據(jù)連接狀態(tài):暫停 - getDeviceSoftwareVersion() 移動(dòng)終端的軟件版本具壮,例如:GSM手機(jī)的IMEI/SV碼
- getLine1Number() 手機(jī)號(hào)碼,對(duì)于GSM網(wǎng)絡(luò)來說即MSISDN(不一定能拿到)
- getNetworkCountryIso() ISO標(biāo)準(zhǔn)的國(guó)家碼哈蝇,即國(guó)際長(zhǎng)途區(qū)號(hào)
- getDeviceId() GSM網(wǎng)絡(luò)棺妓,返回IMEI;如果是CDMA網(wǎng)絡(luò)炮赦,返回MEID
IMEI是International Mobile Equipment Identity (國(guó)際移動(dòng)設(shè)備標(biāo)識(shí))的簡(jiǎn)稱
IMEI由15位數(shù)字組成的”電子串號(hào)”怜跑,它與每臺(tái)手機(jī)一一對(duì)應(yīng),而且該碼是全世界唯一的吠勘,其組成為:- 前6位數(shù)(TAC)是”型號(hào)核準(zhǔn)號(hào)碼”性芬,一般代表機(jī)型
- 接著的2位數(shù)(FAC)是”最后裝配號(hào)”,一般代表產(chǎn)地
- 之后的6位數(shù)(SNR)是”串號(hào)”剧防,一般代表生產(chǎn)順序號(hào)
- 最后1位數(shù)(SP)通常是”0″植锉,為檢驗(yàn)碼,目前暫備用
MEID 移動(dòng)設(shè)備識(shí)別碼(Mobile Equipment Identifier)是CDMA手機(jī)的身份識(shí)別碼峭拘,也是每臺(tái)CDMA手機(jī)或通訊平板唯一的識(shí)別碼俊庇。
MEID由14個(gè)十六進(jìn)制字符標(biāo)識(shí),第15位為校驗(yàn)位鸡挠,不參與空中傳輸辉饱。
RR:范圍A0-FF,由官方分配
XXXXXX:范圍 000000-FFFFFF拣展,由官方分配
ZZZZZZ:范圍 000000-FFFFFF彭沼,廠商分配給每臺(tái)終端的流水號(hào)
C/CD:0-F,校驗(yàn)碼
- getNetworkOperator() MCC+MNC代碼 (SIM卡運(yùn)營(yíng)商國(guó)家代碼和運(yùn)營(yíng)商網(wǎng)絡(luò)代碼)(IMSI)
IMSI是國(guó)際移動(dòng)用戶識(shí)別碼的簡(jiǎn)稱(International Mobile Subscriber Identity)
IMSI共有15位备埃,其結(jié)構(gòu)如下:
MCC+MNC+MIN
MCC:Mobile Country Code姓惑,移動(dòng)國(guó)家碼褐奴,共3位,中國(guó)為460;
MNC:Mobile NetworkCode挺益,移動(dòng)網(wǎng)絡(luò)碼歉糜,共2位
在中國(guó),移動(dòng)的代碼為電00和02望众,聯(lián)通的代碼為01匪补,電信的代碼為03
合起來就是(也是Android手機(jī)中APN配置文件中的代碼):
中國(guó)移動(dòng):46000 46002
中國(guó)聯(lián)通:46001
中國(guó)電信:46003
舉例,一個(gè)典型的IMSI號(hào)碼為460030912121001 - getNetworkOperatorName() 移動(dòng)網(wǎng)絡(luò)運(yùn)營(yíng)商的名字(SPN)
- getNetworkType() 移動(dòng)網(wǎng)絡(luò)類型
int NETWORK_TYPE_CDMA 網(wǎng)絡(luò)類型為CDMA
int NETWORK_TYPE_EDGE 網(wǎng)絡(luò)類型為EDGE
int NETWORK_TYPE_EVDO_0 網(wǎng)絡(luò)類型為EVDO0
int NETWORK_TYPE_EVDO_A 網(wǎng)絡(luò)類型為EVDOA
int NETWORK_TYPE_GPRS 網(wǎng)絡(luò)類型為GPRS
int NETWORK_TYPE_HSDPA 網(wǎng)絡(luò)類型為HSDPA
int NETWORK_TYPE_HSPA 網(wǎng)絡(luò)類型為HSPA
int NETWORK_TYPE_HSUPA 網(wǎng)絡(luò)類型為HSUPA
int NETWORK_TYPE_UMTS 網(wǎng)絡(luò)類型為UMTS
在中國(guó)烂翰,聯(lián)通的3G為UMTS或HSDPA夯缺,移動(dòng)和聯(lián)通的2G為GPRS或EGDE,電信的2G為CDMA甘耿,電信的3G為EVDO - getPhoneType() 手機(jī)制式類型
int PHONE_TYPE_CDMA 手機(jī)制式為CDMA踊兜,電信
int PHONE_TYPE_GSM 手機(jī)制式為GSM,移動(dòng)和聯(lián)通
int PHONE_TYPE_NONE 手機(jī)制式未知 - getSimState()
int SIM_STATE_ABSENT SIM卡未找到
int SIM_STATE_NETWORK_LOCKED SIM卡網(wǎng)絡(luò)被鎖定佳恬,需要Network PIN解鎖
int SIM_STATE_PIN_REQUIRED SIM卡PIN被鎖定捏境,需要User PIN解鎖
int SIM_STATE_PUK_REQUIRED SIM卡PUK被鎖定,需要User PUK解鎖
int SIM_STATE_READY SIM卡可用
int SIM_STATE_UNKNOWN SIM卡未知 - getSimCountryIso() SIM卡提供商的國(guó)家代碼
- getSimOperator()毁葱,getSimOperatorName() MCC+MNC代碼 (SIM卡運(yùn)營(yíng)商國(guó)家代碼和運(yùn)營(yíng)商網(wǎng)絡(luò)代碼)(IMSI)
- getSimSerialNumber() SIM卡的序列號(hào)(IMEI)
- getSubscriberId() 用戶唯一標(biāo)識(shí)垫言,比如GSM網(wǎng)絡(luò)的IMSI編號(hào)
- getVoiceMailAlphaTag() 語音信箱號(hào)碼關(guān)聯(lián)的字母標(biāo)識(shí)
- getVoiceMailNumber() 語音郵件號(hào)碼
- isNetworkRoaming() 手機(jī)是否處于漫游狀態(tài)
網(wǎng)絡(luò)連接處理方式
獲取單個(gè)網(wǎng)絡(luò)是否連接
貼代碼:
ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
//WiFi是否連接
NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
boolean isWifiConn = networkInfo.isConnected();
//手機(jī)網(wǎng)絡(luò)是否連接
networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean isMobileConn = networkInfo.isConnected();
獲取網(wǎng)絡(luò)是否連接
ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
通過廣播獲取連接狀態(tài)改變(常用方式)
- 注冊(cè)廣播,監(jiān)聽ConnectivityManager.CONNECTIVITY_ACTION頻道
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(mReceiver, filter);
- 網(wǎng)絡(luò)狀態(tài)廣播
BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)) {
Log.i(TAG, "netWork has lost");
}
NetworkInfo tmpInfo = (NetworkInfo)
intent.getExtras().get(ConnectivityManager.EXTRA_NETWORK_INFO);
Log.i(TAG, tmpInfo.toString() + " {isConnected = " + tmpInfo.isConnected() + "}");
}
};
- 清單文件注冊(cè)(Demo寫內(nèi)部類了)
<receiver android:name=".ConnectionReceiver" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
無網(wǎng)絡(luò)連接處理
提示無網(wǎng)絡(luò)倾剿,或跳轉(zhuǎn)到網(wǎng)絡(luò)設(shè)置界面:
/**
* 判斷手機(jī)系統(tǒng)的版本筷频!如果API大于10 就是3.0+
* 因?yàn)?.0以上的版本的設(shè)置和3.0以下的設(shè)置不一樣,調(diào)用的方法不同
*/
Intent intent ;
if (Build.VERSION.SDK_INT > 10) {
intent = new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS);
} else {
intent = new Intent();
ComponentName component = new ComponentName("com.android.settings","com.android.settings.WirelessSettings");
intent.setComponent(component);
intent.setAction("android.intent.action.VIEW");
}
startActivity(intent);
不同Rom可能不同前痘。
下面來說WiFi
WiFi
WIFI是一種無線聯(lián)網(wǎng)技術(shù)凛捏,常見的是使用無線路由器,在這個(gè)無線路由器的信號(hào)覆蓋的范圍內(nèi)都可以采用WIFI連接的方式進(jìn)行聯(lián)網(wǎng)芹缔。如果無線路由器連接了一個(gè)ADSL線路或其他的聯(lián)網(wǎng)線路坯癣,則又被稱為“熱點(diǎn)”。
首先乖菱,要操作WiFi坡锡,先要加入權(quán)限
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
Android中對(duì)于Wifi操作本身提供了一些有用的包android.net.wifi
WiFi常用相關(guān)類
WifiManager用來管理我們的wifi連接
取得WifiManager對(duì)象
WifiManager mWifiManager=(WifiManager) context.getSystemService(Context.WIFI_SERVICE);
打開wifi
if(!mWifiManager.isWifiEnabled())
{
mWifiManager.setWifiEnabled(true);
}
關(guān)閉wifi
if(mWifiManager.isWifiEnabled())
{
mWifiManager.setWifiEnabled(false);
}
檢查當(dāng)前wifi狀態(tài)
mWifiManager.getWifiState()
WIFI網(wǎng)卡狀態(tài)
- WifiManager.WIFI_STATE_DISABLING : WIFI網(wǎng)卡正在關(guān)閉(0)
- WifiManager.WIFI_STATE_DISABLED : WIFI網(wǎng)卡不可用(1)
- WifiManager.WIFI_STATE_ENABLING : WIFI網(wǎng)正在打開(2) (WIFI啟動(dòng)需要一段時(shí)間)
- WifiManager.WIFI_STATE_ENABLED : WIFI網(wǎng)卡可用(3)
- WifiManager.WIFI_STATE_UNKNOWN : 未知網(wǎng)卡狀態(tài)
WifiInfo
wifi無線連接的信息,包括接入點(diǎn)窒所,網(wǎng)絡(luò)連接狀態(tài)鹉勒,隱藏的接入點(diǎn),IP地址吵取,連接速度禽额,MAC地址,網(wǎng)絡(luò)ID,信號(hào)強(qiáng)度等信息
WifiInfo mWifiInfo=mWifiManager.getConnectionInfo();
- getBSSID() 獲取BSSID,在手機(jī)WIFI中脯倒,就是MAC地址
- getSSID() 獲取SSID
- getDetailedStateOf() 獲取客戶端的連通性
- getHiddenSSID() 獲得SSID是否被隱藏
- getIpAddress() 獲取IP地址
- getLinkSpeed() 獲得連接的速度
- getMacAddress() 獲得Mac地址
- getRssi() 獲得802.11n網(wǎng)絡(luò)的信號(hào)
- getSupplicanState() 返回具體客戶端狀態(tài)的信息
ScanResult
主要用來描述已經(jīng)檢測(cè)出的接入點(diǎn)实辑,包括接入點(diǎn)的地址,接入點(diǎn)的名稱藻丢,
身份認(rèn)證剪撬,頻率,信號(hào)強(qiáng)度等信息悠反。
開始掃描
mWifiManager.startScan()
得到掃描結(jié)果
List<ScanResult> mWifiList=mWifiManager.getScanResults();
WifiConfiguration
Wifi網(wǎng)絡(luò)的配置残黑,包括安全設(shè)置等。
得到配置好的網(wǎng)絡(luò)連接
List<WifiConfiguration> mWifiConfigurations=mWifiManager.getConfiguredNetworks()
連接配置好指定ID的網(wǎng)絡(luò)
mWifiManager.enableNetwork(mWifiConfigurations.get(index).networkId, true)
處理網(wǎng)絡(luò)
動(dòng)態(tài)處理WiFi網(wǎng)絡(luò)
//添加一個(gè)網(wǎng)絡(luò)并連接
public void addNetWork(WifiConfiguration configuration){
int wcgId=mWifiManager.addNetwork(configuration);
mWifiManager.enableNetwork(wcgId, true);
}
//斷開指定ID的網(wǎng)絡(luò)
public void disConnectionWifi(int netId){
mWifiManager.disableNetwork(netId);
mWifiManager.disconnect();
}
連接到對(duì)應(yīng)WiFi網(wǎng)絡(luò)
創(chuàng)建WifiConfiguration配置對(duì)象
String ssid = "Rair";
String pwd = "88888888";
WifiConfiguration localWifiConfiguration = new WifiConfiguration();
//公認(rèn)的IEEE 802.11驗(yàn)證算法斋否。
localWifiConfiguration.allowedAuthAlgorithms.clear();
localWifiConfiguration.allowedAuthAlgorithms.set(0);
//公認(rèn)的的公共組密碼梨水。
localWifiConfiguration.allowedGroupCiphers.clear();
localWifiConfiguration.allowedGroupCiphers.set(2);
//公認(rèn)的密鑰管理方案。
localWifiConfiguration.allowedKeyManagement.clear();
localWifiConfiguration.allowedKeyManagement.set(1);
//密碼為WPA茵臭。
localWifiConfiguration.allowedPairwiseCiphers.clear();
localWifiConfiguration.allowedPairwiseCiphers.set(1);
localWifiConfiguration.allowedPairwiseCiphers.set(2);
//公認(rèn)的安全協(xié)議疫诽。
localWifiConfiguration.allowedProtocols.clear();
localWifiConfiguration.SSID = ("\"" + ssid + "\"");
localWifiConfiguration.preSharedKey = ("\"" + pwd + "\"");
//不廣播其SSID的網(wǎng)絡(luò)
localWifiConfiguration.hiddenSSID = true;
WifiConfiguration對(duì)應(yīng)的配置值可到文檔中查看,不知道怎么翻譯描述旦委。O(∩_∩)O~
添加WIFI網(wǎng)絡(luò)
wcnetworkid = mWifiManager.addNetwork(localWifiConfiguration);
使WIFI網(wǎng)絡(luò)有效
mWifiManager.enableNetwork(wcnetworkid,true);
鎖定網(wǎng)絡(luò)
手機(jī)屏幕關(guān)閉之后奇徒,并且其他的應(yīng)用程序沒有在使用wifi的時(shí)候,系統(tǒng)大概在兩分鐘之后缨硝,會(huì)關(guān)閉wifi逼龟,使得wifi處于睡眠狀態(tài),有利于電源能量的節(jié)省和延長(zhǎng)電池壽命追葡。
android為wifi提供了一種叫WifiLock的鎖,能夠阻止wifi進(jìn)入睡眠狀態(tài)奕短,使wifi一直處于活躍狀態(tài)宜肉。這種鎖,在下載一個(gè)較大的文件的時(shí)候翎碑,比較適合使用谬返。
添加權(quán)限
<uses-permission android:name="android.permission.WAKE_LOCK"/>
創(chuàng)建wifiLock
方式1:
// lockName為鎖的名稱
WifiLock wifiLock = wifiManager.createWifiLock(lockName);
方式2:
wifiLock = wifiManager.createWifiLock(lockType, lockName);
lockType可以取以下值:
- WIFI_MODE_FULL == 1 //掃描,自動(dòng)的嘗試去連接一個(gè)曾經(jīng)配置過的熱點(diǎn)
- WIFI_MODE_SCAN_ONLY == 2 //只剩下掃描
- WIFI_MODE_FULL_HIGH_PERF = 3 //在第一種模式的基礎(chǔ)上日杈,保持最佳性能
wifi添加鎖
wifiLock.acquire();
wifi釋放鎖
//判斷wifi是否被lock鎖持用
if (wifiLock.isHeld())
{
// 釋放鎖
wifiLock.release();
}
一般到了這個(gè)時(shí)候就該給個(gè)Demo了
ConnectivityManagerDemo:連接管理的Demo,里面帶一個(gè)NetworkConnUtil(網(wǎng)絡(luò)連接幫助類)遣铝,Demo都是以log形式顯示結(jié)果。懶得寫界面啦 (?*)4
WifiDemo: 里面帶一個(gè)WifiUtil(Wifi連接幫助類)
Demo已上傳:RairDemo
GitHub:https://github.com/Rairmmd/android-demo
Coding:https://coding.net/u/Rair/p/RairDemo/git