安卓 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相关操作
操作 代码 备注
WIFI是否开启 wifiManager.isWifiEnabled() 返回true说明WIFI已经开启,false:未开启
开启WIFI wifiManager.setWifiEnabled(true); 成功开启返回true
关闭WIFI wifiManager.setWifiEnabled(false); 成功关闭返回true
开启热点

//热点设置
        WifiConfiguration config = new WifiConfiguration();
        config.SSID = WIFI_HOTSPOT_SSID;//热点名称
        。。。

Method method = wifiManager.getClass().getMethod(
                    "setWifiApEnabled", WifiConfiguration.class, Boolean.TYPE);
 method.invoke(wifiManager, config, true);
            

通过反射访问隐藏的函数
关闭热点 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 socket = serverSocket.accept();

通过socket 进行收发数据
搜索热点 wifiManager.startScan();//扫描周边热点信息

如果搜索到可用的热点系统就会发出广播WifiManager.SCAN_RESULTS_AVAILABLE_ACTION

注册广播就可以接收到广播。

自定义广播接收者

//自定义广播接收者
    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)) 
            {
                //接收到这个广播之后,自定义动作响应

                //dosomething()
            } 

     }

}

说明接收到广播后的动作响应
注册广播

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);//注册广播

说明要接收什么类型的广播

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

猜你喜欢

转载自blog.csdn.net/nanfeibuyi/article/details/83473531