安卓 WIFI通訊之聊天小程式
安卓 WIFI通訊之聊天小程式
一、簡述
記--使用WIFI實現的一個簡單一對一聊天小程式。一臺裝置開啟WIFI熱點,另外一臺裝置進行連線,然後互相收發資訊。
例子打包:連結: https://pan.baidu.com/s/1uOGxQJPmfJhtM8S6soqkVQ 提取碼: 5b56
二、效果
三、工程結構
四、原始檔
新增的許可權 (改變網路狀態許可權,改變WIFI狀態許可權,獲取網路狀態許可權,獲取WIFI狀態許可權,網路許可權)
<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" /> <uses-permission android:name="android.permission.INTERNET" />
MainActivity.java檔案
package com.liang.wifi; import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.DhcpInfo; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; public class MainActivity extends Activity implements View.OnClickListener { private Button btn_create_hostspot;//"建立WIFI熱點"按鈕 private Button btn_close_hostspot;//"關閉熱點" private Button btn_exit;//"退出" private Button btn_send;//"傳送資訊" private Button btn_search;//搜尋附近熱點資訊 private TextView tv_rmsg;//顯示連線資訊、接收的資訊 private TextView tv_state;//顯示WIFI連線狀態 private EditText edt_smsg;//要傳送的資訊 private ScrollView sv;//滾動檢視,適配TextView內容,當內容過多,以滾動條形式顯示 private WifiManager wifiManager;//WIFI管理物件 private WifiConfiguration config;//WIFI配置 private int netID;//網路身份ID private boolean scan_WIFIHOT = false;//用來控制跳轉到WIFI熱點列表的 private static final String WIFI_HOTSPOT_SSID = "TEST";//WIFI熱點名稱 private static final String WIFI_PWD = "12345678";//熱點密碼 private static final int PORT = 54321;//埠號 public static final int WIFICIPHER_NOPASS = 1;//熱點無密碼 public static final int WIFICIPHER_WEP = 2;//熱點加密方式為 WEP public static final int WIFICIPHER_WPA = 3;//熱點加密方式為 WPA public static final int DEVICE_CONNECTING = 4;//有裝置正在連線熱點 public static final int DEVICE_CONNECTED = 5;//有裝置連上熱點 public static final int SEND_MSG_SUCCSEE = 6;//傳送訊息成功 public static final int SEND_MSG_ERROR = 7;//傳送訊息失敗 public static final int GET_MSG = 8;//獲取新訊息 public static final int TOAST_MSG = 10;//彈出toast提示框 public static final int REQUEST_CONNECT_DEVICE = 11;//用來表示請求連線裝置 private ConnectThread connectThread;//連線執行緒 private ListenerThread listenerThread;//監聽執行緒 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);//設定主頁面 initView();//初始化控制元件 initBroadcastReceiver();//註冊廣播 //獲取WIFI管理助手物件 wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); //開啟監聽執行緒 listenerThread = new ListenerThread(PORT, handler); listenerThread.start(); } //初始化控制元件,並繫結列表選項的點選事件 private void initView() { //獲取控制元件控制代碼 btn_create_hostspot = (Button) findViewById(R.id.btn_create_hostspot); btn_close_hostspot = (Button) findViewById(R.id.btn_close_hostspot); btn_exit = (Button) findViewById(R.id.btn_exit); btn_send = (Button) findViewById(R.id.btn_send); btn_search = (Button) findViewById(R.id.btn_search); tv_rmsg = (TextView) findViewById(R.id.tv_rmsg); tv_state = (TextView) findViewById(R.id.tv_state); edt_smsg = (EditText) findViewById(R.id.edt_smsg); sv = (ScrollView)findViewById(R.id.sv_list); //繫結點選事件 btn_create_hostspot.setOnClickListener(this); btn_close_hostspot.setOnClickListener(this); btn_exit.setOnClickListener(this); btn_send.setOnClickListener(this); btn_search.setOnClickListener(this); } //連線WIFI熱點,根據WIFI熱點的配置資訊進行連線 private void connect(WifiConfiguration config) { tv_state.append("連線中...");//顯示當前連線狀態 netID = wifiManager.addNetwork(config);//根據熱點配置新增網路,返回網路身份ID,如果是-1則新增失敗 wifiManager.enableNetwork(netID, true);//連線網路(連線成功會有相應的廣播資訊) } //自定義廣播接收者 private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction();//收到的廣播動作 //根據廣播動作做出相應的響應 if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { // wifi已成功掃描到可用wifi熱點 if(scan_WIFIHOT)//點選了"搜尋"按鈕才跳轉到WIFI列表 { //獲取掃描的結果(有可能含有SSID為空的資料,尚未處理) ArrayList<ScanResult> scanResultList = (ArrayList<ScanResult>)wifiManager.getScanResults(); Intent scanIntent = new Intent(MainActivity.this, WiFiListActivity.class); //跳轉到WIFI列表介面 scanIntent.putParcelableArrayListExtra("SCANRESLIST", scanResultList);//將熱點資料傳遞過去,顯示到ListView startActivityForResult(scanIntent, REQUEST_CONNECT_DEVICE); //設定返回巨集定義 scan_WIFIHOT = false; } } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {//WIIF狀態改變 //獲取WIFI狀態 int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0); switch (wifiState) { case WifiManager.WIFI_STATE_ENABLED: //獲取到wifi開啟的廣播時,開始掃描 wifiManager.startScan(); break; case WifiManager.WIFI_STATE_DISABLED://wifi關閉發出的廣播 Toast.makeText(MainActivity.this, "WiFi已關閉", Toast.LENGTH_SHORT).show(); break; } } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {//網路狀態改變 //獲取網路資訊 NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); if (info.getState().equals(NetworkInfo.State.DISCONNECTED)) { //斷開連線 tv_state.setText("提示:連線已斷開。"); } else if (info.getState().equals(NetworkInfo.State.CONNECTED)) { //已連線網路 WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); final WifiInfo wifiInfo = wifiManager.getConnectionInfo(); tv_state.setText("已連線到熱點:" + wifiInfo.getSSID()+"\n"); String local_ip = intToIp(wifiInfo.getIpAddress());//自己的IP DhcpInfo ipinfo = wifiManager.getDhcpInfo();//獲取WIFI熱點的IP final String ip = intToIp(ipinfo.serverAddress); if (wifiInfo.getSSID().equals("\""+WIFI_HOTSPOT_SSID+"\"") ) { tv_state.append("熱點ip:"+ip+"\n"); tv_state.append("本機ip:"+local_ip+"\n"); //開啟連線執行緒 new Thread(){ @Override public void run() { Socket socket; try { socket = new Socket(ip, PORT);//建立與熱點通訊的socket connectThread = new ConnectThread(socket, handler); connectThread.start(); } catch (UnknownHostException e1) { sendHandlerMsg("err1"); e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); sendHandlerMsg("err2"); } } }.start(); } } else { //實時網路連線狀態 NetworkInfo.DetailedState state = info.getDetailedState(); if (state == DetailedState.CONNECTING) { tv_state.setText("連線中..."); } else if (state == DetailedState.AUTHENTICATING) { tv_state.setText("正在驗證身份資訊..."); } else if (state == DetailedState.OBTAINING_IPADDR) { tv_state.setText("正在獲取IP地址..."); } else if (state == DetailedState.FAILED) { tv_state.setText("連線失敗"); } } } } }; //int形式的ip轉為字串形式的ip private String intToIp(int i) { return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." + ((i >> 24) & 0xFF); } //初始化廣播並註冊 private void initBroadcastReceiver() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(receiver, intentFilter);//註冊廣播 } //按鈕的點選響應事件 @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_create_hostspot: createWifiHotspot();//開啟WIFI熱點 break; case R.id.btn_close_hostspot: closeWifiHotspot();//關閉WIFI熱點 break; case R.id.btn_send://傳送資訊 if (connectThread != null) { if(!edt_smsg.getText().toString().equals(""))//訊息不為空 { connectThread.sendData( edt_smsg.getText().toString() ); edt_smsg.setText(""); } } else { Toast.makeText(this, "未連線裝置", Toast.LENGTH_SHORT).show(); } break; case R.id.btn_search://搜尋周邊熱點資訊 searchWiFiHot(); break; case R.id.btn_exit://退出程式 showAlertDialog("退出應用", "您確認退出嗎?", "取消" ,"確認", 0); break; } } /** * 建立Wifi熱點 */ private void createWifiHotspot() { if (wifiManager.isWifiEnabled()) { //如果wifi處於開啟狀態,則關閉wifi, wifiManager.setWifiEnabled(false); } //熱點設定 WifiConfiguration config = new WifiConfiguration(); config.SSID = WIFI_HOTSPOT_SSID;//熱點名稱 config.preSharedKey = WIFI_PWD;//熱點密碼 config.hiddenSSID = false;//是否隱藏密碼 config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);//開放系統認證 config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);//設定加密方式 config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); config.allowedPairwiseCiphers .set(WifiConfiguration.PairwiseCipher.CCMP); config.status = WifiConfiguration.Status.ENABLED; //通過反射呼叫設定熱點 try { Method method = wifiManager.getClass().getMethod( "setWifiApEnabled", WifiConfiguration.class, Boolean.TYPE); boolean enable = (Boolean) method.invoke(wifiManager, config, true); if (enable) { //DhcpInfo info = wifiManager.getDhcpInfo();//熱點本機IP //String ip = intToIp(info.serverAddress); tv_state.setText("熱點已開啟 熱點名稱:" + WIFI_HOTSPOT_SSID +" 密碼:"+WIFI_PWD+"\n"); } else { tv_state.setText("建立熱點失敗"); } } catch (Exception e) { e.printStackTrace(); tv_state.setText("建立熱點失敗"); } } /** * 關閉WiFi熱點 (利用反射訪問隱藏的熱點設定函式) */ private void closeWifiHotspot() { try { Method method = wifiManager.getClass().getMethod("getWifiApConfiguration"); method.setAccessible(true); WifiConfiguration config = (WifiConfiguration) method.invoke(wifiManager); Method method2 = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class); method2.invoke(wifiManager, config, false); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } tv_state.setText("熱點已關閉"); } /** * 搜尋wifi熱點 */ private void searchWiFiHot() { if (!wifiManager.isWifiEnabled()) //如果還沒有開啟WIFI則開啟 { //開啟wifi wifiManager.setWifiEnabled(true); } wifiManager.startScan();//掃描周邊熱點資訊 Toast.makeText(this, "正在掃描周邊熱點。。。請稍後!", Toast.LENGTH_SHORT).show(); scan_WIFIHOT = true;//可以跳轉到WIFI熱點列表 } /* * 接收活動結果,響應startActivityForResult() */ public void onActivityResult(int requestCode, int resultCode, Intent data) { switch(requestCode) { case REQUEST_CONNECT_DEVICE://從熱點列表介面返回 if (resultCode == RESULT_OK)//選擇一個裝置 { //獲取返回的資料(所選擇的熱點資訊) final String ssid = data.getStringExtra("SSID"); wifiManager.disconnect();//斷開之前的連線 String capabilities = data.getStringExtra("CAPABILITIES"); //判斷WIFI熱點加密型別 int type = MainActivity.WIFICIPHER_WPA; if (!TextUtils.isEmpty(capabilities)) { if (capabilities.contains("WPA") || capabilities.contains("wpa")) { type = MainActivity.WIFICIPHER_WPA; } else if (capabilities.contains("WEP") || capabilities.contains("wep")) { type = MainActivity.WIFICIPHER_WEP; } else { type = MainActivity.WIFICIPHER_NOPASS; } } //根據熱點的SSID判斷之前是否連線過 config = isExsits(ssid); if (config == null) //沒有連線過 { if (type != WIFICIPHER_NOPASS)//需要密碼 { //彈出密碼輸入框 final EditText editText = new EditText(MainActivity.this); final int finalType = type; new AlertDialog.Builder(MainActivity.this).setTitle("請輸入Wifi密碼").setIcon( android.R.drawable.ic_dialog_info).setView( editText).setPositiveButton("確定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { config = createWifiInfo(ssid, editText.getText().toString(), finalType); connect(config); } }) .setNegativeButton("取消", null).show(); return; } else//熱點是開放的(不需要密碼) { config = createWifiInfo(ssid, "", type); connect(config); } } else { //之前連線過則直接進行連線 connect(config); } } break; default:break; } } //退出程式時釋放資源 @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(receiver);//登出廣播註冊 //關閉執行緒、釋放socket資源等等 } //Handler操作 (收到Hansler訊號做出反應,通常用來處理子執行緒的請求) @SuppressLint("HandlerLeak") //jdk版本問題 private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case DEVICE_CONNECTING://進行連線(本機開熱點,連線其他手機) //有客戶端連線 tv_state.append( "【IP:"+(String)msg.obj +"】"); connectThread = new ConnectThread(listenerThread.getSocket(),handler); connectThread.start(); break; case DEVICE_CONNECTED://有裝置連線成功 tv_rmsg.append("***裝置連線成功***\n"); sv.scrollTo(0,tv_rmsg.getMeasuredHeight()); //適配內容 break; case SEND_MSG_SUCCSEE://成功傳送訊息 tv_rmsg.append("(我)" + msg.getData().getString("MSG")+"\n"); sv.scrollTo(0,tv_rmsg.getMeasuredHeight()); //適配內容 break; case SEND_MSG_ERROR://傳送訊息失敗 tv_rmsg.append("傳送失敗:" + msg.getData().getString("MSG")+"\n"); sv.scrollTo(0,tv_rmsg.getMeasuredHeight()); //適配內容 break; case GET_MSG://收到訊息 tv_rmsg.append(msg.getData().getString("MSG")+"\n"); sv.scrollTo(0,tv_rmsg.getMeasuredHeight()); //適配內容 break; case TOAST_MSG: Toast.makeText(MainActivity.this, (String)msg.obj, Toast.LENGTH_SHORT).show(); break; } } }; //傳送Handler訊息,彈出Toast資訊 public void sendHandlerMsg(String msg) { Message message = Message.obtain(); message.what = MainActivity.TOAST_MSG; message.obj = msg; handler.sendMessage(message); } /** * 判斷當前wifi是否有儲存 * @param SSID 熱點資訊 * @return */ public WifiConfiguration isExsits(String SSID) { List<WifiConfiguration> existingConfigs = wifiManager.getConfiguredNetworks(); for (WifiConfiguration existingConfig : existingConfigs) { if (existingConfig.SSID.equals("\"" + SSID + "\"")) { return existingConfig; } } return null; } //建立WIFI配置資訊 public WifiConfiguration createWifiInfo(String SSID, String password, int type) { WifiConfiguration config = new WifiConfiguration(); config.allowedAuthAlgorithms.clear(); config.allowedGroupCiphers.clear(); config.allowedKeyManagement.clear(); config.allowedPairwiseCiphers.clear(); config.allowedProtocols.clear(); config.SSID = "\"" + SSID + "\""; if (type == WIFICIPHER_NOPASS) { config.wepKeys[0] = "\"" + "\""; config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); config.wepTxKeyIndex = 0; } else if (type == WIFICIPHER_WEP) { config.preSharedKey = "\"" + password + "\""; config.hiddenSSID = false; config.allowedAuthAlgorithms .set(WifiConfiguration.AuthAlgorithm.SHARED); config.allowedGroupCiphers .set(WifiConfiguration.GroupCipher.CCMP); config.allowedGroupCiphers .set(WifiConfiguration.GroupCipher.TKIP); config.allowedGroupCiphers .set(WifiConfiguration.GroupCipher.WEP40); config.allowedGroupCiphers .set(WifiConfiguration.GroupCipher.WEP104); config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); config.wepTxKeyIndex = 0; } else if (type == WIFICIPHER_WPA) { config.preSharedKey = "\"" + password + "\""; config.hiddenSSID = false; config.allowedAuthAlgorithms .set(WifiConfiguration.AuthAlgorithm.OPEN); config.allowedGroupCiphers .set(WifiConfiguration.GroupCipher.TKIP); config.allowedKeyManagement .set(WifiConfiguration.KeyMgmt.WPA_PSK); config.allowedPairwiseCiphers .set(WifiConfiguration.PairwiseCipher.TKIP); // config.allowedProtocols.set(WifiConfiguration.Protocol.WPA); config.allowedGroupCiphers .set(WifiConfiguration.GroupCipher.CCMP); config.allowedPairwiseCiphers .set(WifiConfiguration.PairwiseCipher.CCMP); config.status = WifiConfiguration.Status.ENABLED; } else { return null; } return config; } //彈出確認對話方塊 private void showAlertDialog(String title, String content, String negative, String positive, final int action) { //建立一個對話方塊 AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this); dialog.setTitle(title); //對話方塊標題 dialog.setMessage(content);//設定對話方塊內容提示 if(!negative.isEmpty() && negative != "" )//按需要是否新增“取消”按鈕 { //新增"取消按鈕",並且單擊時響應 dialog.setNegativeButton(negative,new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) {} }); } //新增一個確定按鈕,並且單擊時響應 dialog.setPositiveButton(positive, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch(action) { case 0://退出應用操作 finish();//關閉本頁面 break; case 1://"確認" break; default: break; } } }); dialog.show(); } /** * 監聽執行緒 (等待其它裝置來連線) */ public class ListenerThread extends Thread { private ServerSocket serverSocket = null;//用來監聽本機的某個埠,等待其他裝置的連線 private Handler handler;//用來將資料、資訊通知主執行緒 private Socket socket;//通訊socket private boolean thread_run;//用來控監聽執行緒的結束(不能立即結束,因為accpept阻塞等待) public ListenerThread(int port, Handler handler) { //setName("ListenerThread");//設定執行緒的名稱 thread_run = true; this.handler = handler; try { serverSocket = new ServerSocket(port);//監聽本機的port埠 } catch (IOException e) {} } @Override public void run() { while (thread_run) { try { //阻塞,等待裝置連線 socket = serverSocket.accept(); sendHandlerMsg("發現裝置"); //有裝置連線,使用Handler通知主執行緒 Message message = Message.obtain();//獲取訊息物件 message.what = MainActivity.DEVICE_CONNECTING;//訊息型別 message.obj = socket.getLocalAddress().getHostAddress();//自己的IP handler.sendMessage(message);//傳送訊息 //傳送一個“你好”資訊給連線的裝置 //outputStream = socket.getOutputStream(); //outputStream.write("你好!\n".getBytes()); } catch (IOException e) {} } } //退出執行緒 public void close() { thread_run = false; try { serverSocket.close(); } catch (IOException e) {} } //獲取通訊socket(主執行緒可以通過此方法獲取通訊的socket) public Socket getSocket() { return socket; } } /** * 連線執行緒 (用來通訊的執行緒) */ public class ConnectThread extends Thread{ private final Socket socket;//通訊socket private Handler handler;//用來更新UI等(傳送資訊給主執行緒) private InputStream inputStream;//資料輸入流 private OutputStream outputStream;//資料輸出流 public ConnectThread(Socket socket, Handler handler) { //setName("ConnectThread");//設定執行緒的名稱 this.socket = socket;//初始化通訊socket this.handler = handler;//初始化handler } //執行緒執行所執行的函式 @Override public void run() { if(socket==null){ return; } //傳送不帶資料的handler訊號 handler.sendEmptyMessage(MainActivity.DEVICE_CONNECTED);//傳送已連線 訊號 try { //獲取資料流 inputStream = socket.getInputStream();//用來接收訊息 outputStream = socket.getOutputStream();//用來發送訊息 byte[] buffer = new byte[1024]; int lens = 0; while (true) { //讀取資料 lens = inputStream.read(buffer); if (lens > 0) { final byte[] data = new byte[lens]; System.arraycopy(buffer, 0, data, 0, lens); //將資訊顯示到UI Message message = Message.obtain(); message.what = MainActivity.GET_MSG; Bundle bundle = new Bundle(); bundle.putString("MSG",new String(data)); message.setData(bundle); handler.sendMessage(message); } } } catch (IOException e) { e.printStackTrace(); } } /** * 傳送資料 */ public void sendData(String msg) { //將訊息新增上傳送者IP msg = socket.getLocalAddress().getHostAddress() + ":" + msg; if(outputStream!=null) { try { //傳送資訊 outputStream.write(msg.getBytes()); //提示傳送資訊成功 Message message = Message.obtain(); message.what = MainActivity.SEND_MSG_SUCCSEE; Bundle bundle = new Bundle(); bundle.putString("MSG",new String(msg)); message.setData(bundle); handler.sendMessage(message); } catch (IOException e) {} } } } }
WiFiListActivity.java檔案
package com.liang.wifi;
import java.io.Serializable;
import java.util.List;
import android.net.wifi.ScanResult;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import android.app.Activity;
import android.content.Intent;
public class WiFiListActivity extends Activity {
private ListView listView;//用來顯示搜尋到的WIFI熱點
private WifiListAdapter wifiListAdapter;//WIFI列表介面卡
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try
{
// 建立並顯示視窗
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); //設定視窗顯示模式為視窗方式,有個滾動圈
//設定視窗標題
setTitle("WiFi熱點列表");
//設定介面
setContentView(R.layout.activity_wifi_list);
//獲取listView控制元件控制代碼
listView = (ListView) findViewById(R.id.listView);
//設定ListView介面卡
wifiListAdapter = new WifiListAdapter(this, R.layout.wifi_list_item);
listView.setAdapter(wifiListAdapter);
//獲取從MainActivity傳遞過來的周邊熱點資料集合
List<ScanResult> scanResultList = getIntent().getParcelableArrayListExtra("SCANRESLIST");
wifiListAdapter.clear();//清空原來的資料
wifiListAdapter.addAll(scanResultList);//填充資料
//繫結列表資料項的點選響應事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//拿到選中的熱點資訊
ScanResult scanResult = wifiListAdapter.getItem(position);
// 設定返回資料 (返回選中的熱點資訊)
Intent intent = new Intent();
intent.putExtra("SSID", scanResult.SSID);
intent.putExtra("CAPABILITIES", scanResult.capabilities);
// 設定返回值並結束程式
setResult(Activity.RESULT_OK, intent);
finish();//關閉本頁面
}
});
//“取消”按鍵響應
Button btn_cancel = (Button) findViewById(R.id.btn_cancel);
btn_cancel.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
finish();//關閉本頁面
}
});
}
catch(Exception e){}
}
}
WifiListAdapter.java檔案
package com.liang.wifi;
import android.content.Context;
import android.net.wifi.ScanResult;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
//WiFi列表介面卡類 用來填充ListView
public class WifiListAdapter extends ArrayAdapter<ScanResult> {
private final LayoutInflater mInflater;//xml佈局載入器
private int mResource;//xml佈局ID號
public WifiListAdapter(Context context, int resource) {
super(context, resource);
mInflater = LayoutInflater.from(context);
mResource = resource;
}
//填充ListView的資料項
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(mResource, parent, false);
}
//列表中的一個數據項(有兩個TextView用來顯示WIFI熱點名稱和訊號的強弱)
TextView name = (TextView) convertView.findViewById(R.id.wifi_name);
TextView signl = (TextView) convertView.findViewById(R.id.wifi_signal);
//拿到所點選的熱點的相關資訊
ScanResult scanResult = getItem(position);
//顯示熱點的名稱
name.setText(scanResult.SSID);
//顯示熱點訊號的強弱
int level = scanResult.level;
if (level <= 0 && level >= -50) {
signl.setText("訊號很好");
} else if (level < -50 && level >= -70) {
signl.setText("訊號較好");
} else if (level < -70 && level >= -80) {
signl.setText("訊號一般");
} else if (level < -80 && level >= -100) {
signl.setText("訊號較差");
} else {
signl.setText("訊號很差");
}
return convertView;
}
}
佈局檔案
activity_main.xml檔案
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_state"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0.04"
android:text="未連線裝置" />
<ScrollView
android:id="@+id/sv_list"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="0.36"
android:scrollbars="vertical" >
<TextView
android:id="@+id/tv_rmsg"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="@+id/edt_smsg"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_weight="1"
android:hint="請輸入要傳送的資訊"
android:shape="rectangle"
android:inputType="text" >
</EditText>
<Button
android:id="@+id/btn_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.09"
android:text="@string/send_data" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="@+id/btn_create_hostspot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/create_hostspot" />
<Button
android:id="@+id/btn_close_hostspot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/close_hostspot" />
<Button
android:id="@+id/btn_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/search" />
<Button
android:id="@+id/btn_exit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="退出" />
</LinearLayout>
</LinearLayout>
activity_wifi_list.xml檔案
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ListView android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:stackFromBottom="true"
android:layout_weight="2"
/>
<Button
android:id="@+id/btn_cancel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="OnCancel"
android:text="取消" />
</LinearLayout>
wifi_list_item.xml檔案
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="58dp"
android:padding="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/wifi_name"
android:gravity="center_vertical"
android:background="#E6E6FA"
android:textColor="#000"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/wifi_signal"
android:gravity="center_vertical|right"
android:background="#E6E6FA"
android:textColor="#000"/>
</LinearLayout>
五、總結
1、程式通訊的大致流程圖
裝置A開啟熱點,裝置B搜尋熱點並進行連線。連線成功後,裝置B系統發出已連線網路的廣播資訊,裝置B收到後就可以獲取到熱點的相關資訊(IP),然後可以根據IP,埠號建立與熱點通訊的socket,然後裝置A收到socket通訊的請求,接收連線並取得與裝置B通訊的socket,這樣裝置A與裝置B可以相互收發訊息。
2、WIFI相關操作
//獲取WIFI管理助手物件 (WIFI重要操作類)
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
操作 | 程式碼 | 備註 |
WIFI是否開啟 | wifiManager.isWifiEnabled() | 返回true說明WIFI已經開啟,false:未開啟 |
開啟WIFI | wifiManager.setWifiEnabled(true); | 成功開啟返回true |
關閉WIFI | wifiManager.setWifiEnabled(false); | 成功關閉返回true |
開啟熱點 |
//熱點設定 Method method = wifiManager.getClass().getMethod( |
通過反射訪問隱藏的函式 |
關閉熱點 | Method method = wifiManager.getClass().getMethod("getWifiApConfiguration"); method.setAccessible(true); WifiConfiguration config = (WifiConfiguration) method.invoke(wifiManager); Method method2 = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class); method2.invoke(wifiManager, config, false); |
|
監聽 |
ServerSocket serverSocket = new ServerSocket(port);//監聽本機的port埠 //阻塞,等待裝置連線 |
通過socket 進行收發資料 |
搜尋熱點 | wifiManager.startScan();//掃描周邊熱點資訊 |
如果搜尋到可用的熱點系統就會發出廣播WifiManager.SCAN_RESULTS_AVAILABLE_ACTION 註冊廣播就可以接收到廣播。 |
自定義廣播接收者 |
//自定義廣播接收者 //dosomething() } } |
說明接收到廣播後的動作響應 |
註冊廣播 |
IntentFilter intentFilter = new IntentFilter(); registerReceiver(receiver, intentFilter);//註冊廣播 |
說明要接收什麼型別的廣播 (程式結束需要登出廣播註冊unregisterReceiver(receiver);//登出廣播註冊) |
獲取周邊熱點資訊 | // wifi已成功掃描到可用wifi熱點,獲取熱點集合 ArrayList<ScanResult> scanResultList = (ArrayList<ScanResult>)wifiManager.getScanResults(); |
有SSID為空的資料,需要自己處理 |
連線熱點 | int netID = wifiManager.addNetwork(config);//根據熱點配置新增網路,返回網路身份ID,如果是-1則新增失敗,config是熱點的相關資訊(熱點名稱,加密型別等) wifiManager.enableNetwork(netID, true);//連線網路(連線成功會有相應的廣播資訊NetworkInfo.State.CONNECTED) |
連線成功後就加入到區域網了,裝置在區域網中有唯一的身份IP |
與熱點建立通訊 | Socket socket = new Socket(ip, PORT);//通訊socket,用來與熱點收發資料 | ip是熱點的ip,PORT是開啟熱點裝置所監聽的埠 |
收發訊息 | //獲取資料流 InputStream inputStream = socket.getInputStream();//用來接收訊息 OutputStream outputStream = socket.getOutputStream();//用來發送訊息 |
3、多人聊天
多個裝置連線同一個熱點,熱點最為資訊中轉站 ,使得多個裝置之間可以互相通訊。(熱點可以指定傳送訊息給某個裝置,或者是廣播訊息)
4、Activity之間傳遞訊息 (例子中將掃描到的熱點資訊集合傳遞到另外一個頁面顯示,ScanResult已經實現了Parcelable介面)
傳遞資料(Intent的相關函式)
接收資料 (Intent的相關函式)
a) 傳遞字串資料 (Intent的putExtra()函式傳遞資料,getStringExtra()獲取資料)
Intent intent = new Intent(MainActivity.this, WiFiListActivity.class); //從主頁面跳轉到WIFI列表介面
intent.putExtra("NAME","liang");//將字串"liang"傳遞過去
startActivityForResult(intent , request_code); //開始跳轉,request_code:自定義請求碼
在WiFiListActivity中接收
String name = getIntent().getStringExtra("NAME);//根據鍵值接收
b) 傳遞物件集合資料
對於傳遞物件資料,需要將物件序列化,list集合資料類似
物件可序列化的前提就是實現了Serializable或者是Parcelable介面。
1 實現Serializable介面
用Serializable方式傳遞Object的語法:bundle.putSerializable(key,object);
用Serializable方式接收Object的語法:object=(Object) getIntent().getSerializableExtra(key);
實現Serializable介面就是把物件序列化,然後再傳輸。
2 需要實現Parcelable介面
用Parcelable方式傳遞Object的語法:bundle.putParcelable(key,object);
用Parcelable方式接收Object的語法:object=(Object) getIntent().getParcelableExtra(key);
實現Parcelable介面的類比較複雜,Parcelable是個什麼東西呢?
Android提供了一種新的型別:Parcel,被用作封裝資料的容器,封裝後的資料可以通過Intent或IPC傳遞。 除了基本型別以外,只有實現了Parcelable介面的類才能被放入Parcel中。
實現Parcelable介面需要實現三個方法: 1)writeToParcel方法。該方法將類的資料寫入外部提供的Parcel中。
宣告:writeToParcel(Parcel dest, int flags)。
2)describeContents方法。直接返回0就可以。
3)靜態的Parcelable.Creator<T>介面,本介面有兩個方法:createFromParcel(Parcel in) 實現從in中創建出類的例項的功能。
newArray(int size) 建立一個型別為T,長度為size的陣列, returnnew T[size];即可。本方法是供外部類反序列化本類陣列使用。
5、待完善:很多細節都內有考慮,程式容錯性不高,只是簡單的演示一個流程。包括資源釋放問題都沒有處理。