安卓 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、待完善:很多细节都内有考虑,程序容错性不高,只是简单的演示一个流程。包括资源释放问题都没有处理。