精华内容
下载资源
问答
  • Android WIFI通信scoket通信聊天室WiFi传文件WiFi热点开启,通过建立WiFi热点,启动scoket服务器和服务器进行互相传输文件聊天等,服务器、客户端再同一APP内,可装在2个手机里分别启动服务器和客户端。
  • iOS 实现 WiFi 局域网传输文件App

    千次阅读 2018-03-16 16:50:02
    我经常使用「多看」和「掌阅」App 看书,其中有一个共同的功能就是 WiFi 书,根据 App 的提示在电脑浏览器打开指定的地址,传入文件就可以直接发送到手机上阅读了。虽然这个功能需求不是很多,但是也对其进行了...

    我经常使用「多看」和「掌阅」App 看书,其中有一个共同的功能就是 WiFi 传书,根据 App 的提示在电脑浏览器打开指定的地址,传入文件就可以直接发送到手机上阅读了。

    虽然这个功能需求不是很多,但是也对其进行了一下研究,使用 CocoaHTTPServer 框架对其进行实现。



    多看」和「掌阅」的 WiFi 传书页面

    先看下最后的实现结果:



    进入 App 内展示传输数据,默认是没有传输任何文件的,当点击添加按钮在浏览器进行文件传输后,关闭弹框就会发现传入的 2 个文件了。

    原理

    CocoaHTTPServer 框架能够在 iOS 上建立起一个本地服务器,只要电脑和移动设备连入同一局域网,即可使用电脑访问 iOS 服务器的指定页面,利用 POST 实现文件的上传。

    功能实现

    导入 CocoaHTTPServer 框架

    这里推荐大家使用 cocospods 进行集成,只需在 “Podfile” 填写以下代码:

    
         
    pod 'CocoaHTTPServer'

    如图所示,导入成功

    20170505149397075130489.png

    配置 HttpConnectManager 类

    创建基于 HTTPConnection 的 MyHTTPConnection 类,用来管理 HttpConnection。

    找到 - (void) processStartOfPartWithHeader:(MultipartMessageHeader*) header 方法,修改传入文件的地址为 Document 文件夹

    
         
    NSString *uploadDirPath = [ NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

    创建 IPAdress 管理类

    创建基于 NSObject 的 DHIPAdress 类,用于获取手机的 IP 地址:

    DHIPAdress.h

    
         
    /*!
    * get device ip address
    */
    + ( NSString *)deviceIPAdress;

    DHIPAdress.m

    
         
    + ( NSString *)deviceIPAdress {
    NSString *address = @"an error occurred when obtaining ip address";
    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;
    int success = 0;
    success = getifaddrs(&interfaces);
    if (success == 0) { // 0 表示获取成功
    temp_addr = interfaces;
    while (temp_addr != NULL) {
    if( temp_addr->ifa_addr->sa_family == AF_INET) {
    // Check if interface is en0 which is the wifi connection on the iPhone
    if ([[ NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString: @"en0"]) {
    // Get NSString from C String
    address = [ NSString stringWithUTF8String:inet_ntoa((( struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
    }
    }
    temp_addr = temp_addr->ifa_next;
    }
    }
    freeifaddrs(interfaces);
    return address;
    }

    传输文件和读取文件

    默认进入 App 读取本地 Document 文件夹内的文件,首次进入 App 没有任何数据展示。

    点击添加按钮,弹出浏览器的 POST 地址,http://192.168.10.192:58818 端口号为每次随机生成。

    浏览器访问 index.html 和 upload.html 来传输文件,传输成功后点击关闭按钮,再次读取本地数据,可以查看到通过浏览器传输到手机的文件列表。


    展开全文
  • 一款基于wifi模块的局域网实时聊天以及文件的安卓app,能实现热点创建,热点连接,文件传输,实时通讯等功能。
  • 文件助手是一个简单的系统工具,它可以方便快捷查看手机的本地文件,还提供远程管理,可以让你在电脑上更方便的管理手机上的文件,是一个比较好的文件工具
  • 小米系统miui免root查看wifi密码安卓app,可以在miui系统没有root的情况下,查看已经连接wifi的密码,非常的好用。
  • wifi-密码查看,兼容6.0以上,只要用本app登录的WiFi,会永久的记录WiFi密码到本地wifi_info.txt文件
  • WIFI温湿度读取APP源码

    2018-01-12 20:52:01
    通过WIFI的通信方式,实现对温湿度数据的读取和存储的APP源码文件
  • Android端WifiDirect文件传输Demo,基于Google官方WifiDirect修改,实现了文件传输。
  • 之前做了一个andriod通过Wifi控制单片机的app,最近没事上来,顺便赚点积分 app主要是界面更加友好,界面上有一个触控摇杆,四个拖动条(0-100),四个开关和四个按钮,作为控制,做遥控小车或者其他的手机端控制...
    之前做了一个andriod通过Wifi控制单片机的app,最近没事传上来,顺便赚点积分
    app主要是界面更加友好,界面上有一个触控摇杆,四个拖动条(0-100),四个开关和四个按钮,作为控制,做遥控小车或者其他的手机端控制的小东西都比较方便,硬件基于ESP8266
    单片机端也可以向app端发消息,app会把消息推到textview上
    多的不说,直接上图
    andriod界面





    单片机驱动(范例程序)



    用的之前比赛的一个最小系统,就像这样~


    全览图~

    其他手机的渣像素,还是oppo手机好啊。。。

    链接http://download.csdn.net/detail/csdn1344789841/9627730 点击打开链接


    展开全文
  • wifi数据传输APP操作小结

    万次阅读 2017-07-31 09:24:33
    前期操作的项目一直是通过串口进行有线连接。目前客户客户需求,需要进行WIFI通讯,故总结技术要点如下。

    前期操作的项目一直是通过串口进行有线连接。目前客户客户需求,需要进行WIFI通讯,故总结技术要点如下。


    1.EEPROM (Electrically Erasable Programmable Read-Only Memory),电可擦可编程只读存储器--一种掉电后数据不丢失的存储芯片。 EEPROM 可以在电脑上或专用设备上擦除已有信息,重新编程。一般用在即插即用。


    2.DSP 数字信号处理,英文:Digital Signal Processing,

    《数字信号处理》这门课介绍的是:将事物的运动变化转变为一串数字,并用计算的方法从中提取有用的信息,以满足我们实际应用的需求。

    DSP芯片,也称数字信号处理器,是一种特别适合于进行数字信号处理运算的微处理器,其主要应用是实时快速地实现各种数字信号处理算法。


    3.ZigBee是基于IEEE802.15.4标准的低功耗局域网协议。根据国际标准规定,ZigBee技术是一种短距离、低功耗的无线通信技术。

    ZigBee协议从下到上分别为物理层(PHY)、媒体访问控制层(MAC)、传输层(TL)、网络层(NWK)、应用层(APL)等。其中物理层媒体访问控制层遵循IEEE 802.15.4标准的规定。


    4.Arduino是一款便捷灵活、方便上手的开源电子原型平台。


    5.AT指令是应用于终端设备与PC应用之间的连接与通信的指令。AT 即Attention。每个AT命令行中只能包含一条AT指令;对于AT指令的发送,除AT两个字符外,最多可以接收1056个字符的长度(包括最后的空字符)。


    6.WIFI无线串口服务器

    USR-WIFI232-610 V2是一款高性能WIFI无线串口联网服务器,将RS232串口设备和RS485设备转换成WIFI无线网络,支持通过手机或者笔记本电脑进行控制,也可以连到远程服务器。

    WIFI串口服务器可以作AP(WIFI热点)支持其他设备联入,也可以做为Station联入普通的无线WIFI网络。

    • 可同时实现RS232或者RS485转WIFI 转RJ45,保证数据传输的稳定性。
    • 可实现以太网转WIFI透明传输,模块可以实现无线网卡的有线转无线的数据转化功能。
    • 支持AP+STA同时运行模式,模块既可以作为AP供无线终端加入,又可以同时作为STA加入到其他无线路由设备

    7.c语言的源文件后缀是“.c”,c语言头文件的后缀是".h",c++的源文件后缀是“cpp”。

    a:archive.归档的意思,是把很多库打包形成的,里面有函数可以用
    .h是头文件。里面声明了一些常量和函数,包含之后可以使用那些常量和函数,

    在windows下
    .c C语言源文件(在写好的源代码后缀加.c其实等于编译预处理)
    .obj 汇编后程序
    .exe 链接后程序
    在Linux下
    .c C语言源文件
    .o(.ko) 汇编后程序
    可执行程序无后缀


    8.Cygwin是一个在windows平台上运行的类UNIX模拟环境,是cygnus solutions公司开发的自由软件(该公司开发的著名工具还有eCos,不过现已被Redhat收购)。它对于学习UNIX/Linux操作环境,或者从UNIX到Windows的应用程序移植,或者进行某些特殊的开发工作,尤其是使用GNU工具集在Windows上进行嵌入式系统开发,非常有用。


    9.ESP8266模块是一种集成模块,且有透传特性。但无论是否透传,只要硬件方面烧录了相关的集成程序,即让ESP8266做为中间站传输采集器与手机或PC终端的数据,即可实现无线连接操作。即上位机需要操作的功能,无线模块与有线模块一致,只需调用相关的函数即可。但下位机亦一样,即亦需调用相关的功能函数并进行烧录即可。所不同的是,上下位机需要相互配合,且需了解相关的功能函数。


    10.XAMARIN的异线程更改UI界面问题,一般有以下几种方法:

    10.1 通过消息机制进行传递,此为最基本的方法。后面几种或许是对该方法的集成。

    10.2    View.Post(() => {  });   View.postDelay(() => {  });

    10.3      runOnUiThread(() => {  });


    11.对于ANDROID编程中布局的设置,一般可以通过以下几种方式实现。

    11.1在Activity中进行设置. 思路,获取屏幕的宽高,拿到控件的布局参数,之后进行宽高设置

    1. TextView tv = (TextView) findViewById(R.id.tv);  
    2.         WindowManager wm = getWindowManager();  
    3.         Display d = wm.getDefaultDisplay();  
    4.           
    5.         //拿到布局参数  
    6.         LayoutParams l = tv.getLayoutParams();  
    7.         l.width=d.getWidth()/3;  
    8.         l.height=d.getHeight()/3;  
    using Android.Util;

    DisplayMetrics dm = new DisplayMetrics();
                WindowManager.DefaultDisplay.GetMetrics(dm);
                int _width = dm.WidthPixels;//分辨率宽度
                int _height = dm.HeightPixels;//分辨率高度
                float _density = dm.Density;屏幕密度(像素比例0.75、1.0、1.5、2.0)
                var _densityDPI = dm.DensityDpi; //屏幕密度(每寸像素120、160、240、320)


    11.2 使用LinearLayout的权重,进行适配屏幕的宽度:

    1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     android:layout_width="fill_parent"  
    3.     android:layout_height="fill_parent"  
    4.     android:background="#FFFFFF"  
    5.     android:gravity="center"  
    6.     android:orientation="horizontal"  
    7.     android:weightSum="1">  
    8.     <Button  
    9.         android:layout_width="0dp"  
    10.         android:layout_height="wrap_content"  
    11.         android:layout_weight="0.5"  
    12.         android:text="Click me"/>  
    13. </LinearLayout>  


    12.Finish()函数用于关闭一个ACTIVITY,即关闭程序。


    13.SOCKET特性总结:

    13.1 打开后必须正确关闭CLOSE(),否则重复打开会出现错误。即服务器根据自身的配置,只能限制有限的连接。

    13.2若本地客户端SOCKET已关闭,则无法使用该变量,不能发送数据。

    13.3若服务器端SOCKET关闭,即端口关闭,则本地可以发送数据,但无回应,因此,判断服务器端是否关闭,需要进行所谓的心跳测试。

    13.4一般本地客户端的接收操作在一个线程中进行(可以与主线程不同的线程),若打开多个线程分别进行接收,则容易出现错误。

    13.5在客户端连续发送命令后,若上一个的返回数据仍没有接收,则容易出现异常,故最好在发送数据之间确定一个最小的时间间隔,即时是200MS即可。即也有可能时缓存问题。即两条命令分别存入缓存后成一条命令发送了。

    13.6通讯时注意逻辑及时间先后关系。


    14.获取本机IP地址的常用操作。

    string name= Dns.GetHostName();IPAddress[] addd = Dns.GetHostAddresses(name); string ip2 = addd[0].ToString();



    15.编程出现问题时一定要对症下药,不然会浪费时间。入某个类不可访问时,需要查看该类的定义及访问级别,然后进行相应的解决。


    16.对于WIFI的搜索与连接,若调用API,则进行封装后可以在手机,PC等不同平台使用,但若借助命令行,则只能在PC端,且是WINDOS系统时能用,手机端不能使用。


    17.若引用某个DLL时出现异常,可以尝试更改之,引用DLL的目的无非是引用其内部封装好的函数而已。


    18.在XAMARIN编程中,在程序启动时,即布局时,若未寻找BTN,而定义了CLICK事件,则System.NullReferenceException: Object reference not set to an instance of an object. 即 注意各个内容不能为空。


    19.C#中的DLL与WINDOWS中的DLL不一样,即托管类的DLL可以直接在C#工程中添加引用。而非托管类的只能用DLLIMPORT的标签等操作。目前理解如此。


    20.没有添加wifi访问权限,则提示java.lang.SecurityException: Requires READ_PHONE_STATE: Neither user 10088 nor current process has android.permission.READ_PHONE_STATE.

    需要在manifest.xml中添加权限。且不同的权限需要分别添加。

    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
      <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> 



    21.要操作WIFI设备,需要先获取Context.getSystemService(Context.WIFI_SERVICE)来获取WifiManager对象,并通过这个对象来管理WIFI设备。

     
    addNetwork(WifiConfiguration config) 添加一个config描述的WIFI网络,默认情况下,这个WIFI网络是DISABLE状态的。
    enableNetwork(int netId, Boolean disableOthers) 连接netId所指的WIFI网络,并是其他的网络都被禁用。





    22.C#子线程更新UI控件的方法实例总结。
    22.1 使用控件自身的invoke/BeginInvoke方法

    22.2.使用SynchronizationContext的Post/Send方法更新

    SynchronizationContext类在System.Threading命令空间下,可提供不带同步的自由线程上下文,其中Post方法签名如下:

    public virtual void Post(SendOrPostCallback d,Object state)    //将异步消息调度到一个同步上下文

    可以看出我们要异步更新UI控件,第一是要获取UI线程的上下文了,第二就是调用post方法了,

    SynchronizationContext _syncContext = null;

    //窗体构造函数  

    public Form1()

    {

      InitializeComponent();

       //获取UI线程同步上下文

      _syncContext = SynchronizationContext.Current;

    }

     _syncContext.Post(SetLabelText,"修改后的文本");//子线程中通过UI线程上下文更新UI


    23.addressList = Dns.GetHostAddresses( Dns.GetHostName() );获取的是一个数组,且正确的IPv4地址因网络的不同,及电脑配置的不同而在数组中的位置不同。在获取正确的IP地址时较为麻烦。但增加筛选条件后,则可以获得唯一值。

    IPAddress addd = Dns.GetHostAddresses(name).Where(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).First();


    24."%"为取余号,不用多说。   

    "/"号现在整形运算是取整,浮点运算时为除法运算,如54/10结果为5,54.0/10.0结果为5.4而且取整时不进行四舍五入只取整数部分,如54/10和56/10是5.   

    Math.Celling()取整数的较大数,相当于不管余数是什么都会进一位。如Math.Celling(54.0/10.0)结果为6.   

    Math.Floor()取整数的较小数,相当于"/"号,即不管余数部分是什么都不进行进位。如Math.Floor(56.0/10.0)的结果是5.

    代码如下: using System;


    25.C#追加文件 方法 注意要在加入 using System.io;
    StreamWriter sw = File.AppendText(Server.MapPath(".")+"\\myText.txt");
    sw.WriteLine("追逐理想");
    sw.WriteLine("kzlll");
    sw.WriteLine(".NET笔记");
    sw.Flush();
    sw.Close();


    26. 在进行布局设置时,往往出现不能自动生成ID的现象,网上解释较多,主要观点有控件命名不规范,语法格式不正确,生成版本不对等等。因此,主要y原因仍不清晰,但通过重新布局可以解决相关问题,但重新布局时最好注意一下命名规范等,且注意问题开始前做了什么更改,以便进行逐步查找原因。而此次,我是在添加TEXTVIEW时,不能同时添加两个,不知在属性设置上有什么纰漏。

    27.读写文件

     var documentsPath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads);  

               //var p = System.Environment.GetFolderPath( System.Environment.SpecialFolder.Personal);  

              var filePath = Path.Combine(documentsPath.AbsolutePath, filename);  
            System.IO.File.WriteAllText(filePath, text


    28.XAMARIN中TEXTVIEW换行问题:可以看到这样并没有实现换行,但是这里换行的关键点在于  需要将   android:inputType ="text"   改成  android:inputType="textMultiLine"  ,并设置lines属性。


    29.this.RequestedOrientation = Android.Content.PM.ScreenOrientation.Portrait;//竖屏,禁止横屏

    30.this.RequestedOrientation = Android.Content.PM.ScreenOrientation.Nosensor;//横屏,禁止竖屏

    31.在SCROWVIEW中定位到某个位置时,需要使用函数SCROW.scrowto(x,y).

    32.今天进行串口通讯时犯了一个逻辑上的小错误,即在串口发送指令前后线程休息的问题。正确的应该是在指令发送前进行休息有效,指令发送后休息往往无效,即达不到缓解通讯压力的目的,当然,也不是绝对的,主要看线程何时忙碌。


    33.LinkAssemblies任务意外失败

    两种解决办法:

    1、将android options 中的linking属性设置为:none。

    2、部署版本设置为sdk最新版本(最好随时更新sdk版本)


    34. error MSB6006: “aapt.exe”已退出,代码为-1073741819
    这个问题是生成工具版本选择的问题,似乎是Xamarin和某些指定的生成工具版本之间的问题,我的是24,看网上也有人用21.0.1也是报这个错误,具体生成的时候用的是什么版本可以看生成日志,也可以在Windows的事件查看器中查看,毕竟报错了嘛,具体在“Windows日志”—> “应用程序”中查找即可

    两种处理方式,一种直接卸载不好使的版本,然后再生成时就会自动选择其他版本的生成工具,不过毕竟人家生成工具并没有犯错,这样直接卸了真是。。。。所以小生选择了另一种处理方式,为项目指定生成工具,用文本编辑器打开项目的.csprj文件,在不含有任何Condition属性的PropertyGroup下添加AndroidSdkBuildToolsVersion节点,配置相应版本即可,如下:

      1 <AndroidSdkBuildToolsVersion>23.0.3</AndroidSdkBuildToolsVersion>
    
    
    35.对于打包文件,目前在DEBUG模式可以暂时操作。若进行RELEASE操作,需要重新调配一下开发平台。


    36.Excel中提供了“数据有效性”这样一个验证功能,可以设定输入的规则,如果输入的数据不符合规则则不允许录入。我们可以利用这个功能来实现让表格使用者只能填写或选择符合规则的内容,从而达到多人分别填写也能使表格内容标准化的目的。

    2007版以后的数据有效性在功能区【数据】选项卡下的【数据有效性】按钮中。在数据有效性中,除了可以选择限制单元格输入内容外,还可以“圈释/清除无效数据”。


    37.



    展开全文
  • WiFi Connect通过本地WiFi网络(WLAN)建立对等通信(呼叫/聊天/共享文件)。 同一无线网络中的用户可以使用各自智能手机中维护的联系人列表来发现彼此。 发现后,用户可以相互通信(呼叫/聊天)或共享多个文件,而...
  • Android WiFiapp到driver详解

    千次阅读 2017-08-02 15:49:00
    (1) Wifi模块相关文件的解析 (2) Wpa_supplicant解析 (3) Wifi的启动流程(有代码供参考分析)   一,Wifi模块相关文件解析 1) wifisettings.Java packages/apps/Settings/src/...

    分三大部分:

    (1)    Wifi模块相关文件的解析

    (2)    Wpa_supplicant解析

    (3)    Wifi的启动流程(有代码供参考分析)

     

    一,Wifi模块相关文件解析

    1)     wifisettings.Java

    packages/apps/Settings/src/com/android/settings/wifiwifisettings.java

           该类数据部分主要定义了下面几个类的变量:

    [java]  view plain  copy
    1. {  
    2.   
    3. private final IntentFilter mFilter;  
    4.   
    5.    
    6.   
    7. //广播接收器,用来接收消息并做响应的处理工作  
    8.   
    9. privatefinal BroadcastReceiver mReceiver;  
    10.   
    11.    
    12.   
    13. //这是一个扫描类,会在用户手动扫描   AP时被调用  
    14.   
    15. privatefinal Scanner mScanner;                  
    16.   
    17. private WifiInfo mLastInfo;  
    18.   
    19.    
    20.   
    21.        //服务代理端,作为WifiService对外的接口类呈现  
    22.   
    23. privateWifiManager mWifiManager;  
    24.   
    25.    
    26.   
    27. //这个类主要实现Wifi的开闭工作  
    28.   
    29. privateWifiEnabler mWifiEnabler;  
    30.   
    31.    
    32.   
    33. //AP  
    34.   
    35. private AccessPoint mSelected;  
    36.   
    37. private WifiDialog mDialog;  
    38.   
    39. ……  
    40.   
    41. }  


           wifiSettings类的构造函数的主要工作:定义了一个IntentFilter(Intent过滤器)变量,并添加了六个动作,(了解Android的intent机制的同学都知道什么意思,不明白的同学参考Intent机制的资料)接着定义一个广播接收器,并有相应的消息处理函数,下面是该构造函数的定义:

     

    [java]  view plain  copy
    1. public WifiSettings() {  
    2.   
    3. mFilter = new IntentFilter();  
    4.   
    5. //intent机制中的intent消息过滤器,下面添加可以处理的动作  
    6.   
    7.     mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);  
    8.   
    9.       mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);  
    10.   
    11.     mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);  
    12.   
    13.    mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);  
    14.   
    15.        mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);  
    16.   
    17.     mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);  
    18.   
    19.    
    20.   
    21. //注册了广播接收器,用来处理接收到的消息事件  
    22.   
    23.        mReceiver = new BroadcastReceiver() {  
    24.   
    25.            @Override  
    26.   
    27.             public void onReceive(Context context,Intent intent) {  
    28.   
    29.                handleEvent(intent); //事件处理函数  
    30.   
    31.             }  
    32.   
    33.         };  
    34.   
    35.    
    36.   
    37.         mScanner= new Scanner();     //手动扫描类  
    38.   
    39. }  


           在广播接收器中的相应函数onReceive函数中有个handleEvent函数,它就是用来处理广播接收器接受到的intent消息的,它的功能是根据intent消息中的动作类型,来执行相应的操作,每一种动作对应了activity的一项消息处理能力。

           在oncreate函数中实例化了mWifiManager和mWifiEnabler两个类,这两个类对wifiSettings来说至关重要,它后面的定义的一系列函数都是通过调用这两个类的相应接口来实现的。

    [java]  view plain  copy
    1. ……  
    2.   
    3. mWifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);  
    4.   
    5. mWifiEnabler = new WifiEnabler(this,  
    6.   
    7.                    (CheckBoxPreference) findPreference("enable_wifi"));  
    8.   
    9. ……  


           WifiSettings中还定义了显示菜单和响应菜单键的函数,即onCreateOptionsMenu()和onOptionsItemSelected();还有响应配置对话框中按键的onClick()函数;最后定义了Scanner类,它是一个handler的继承类,实现了消息处理函数,用于处理手动扫描的动作。

     

    2)     WifiEnabler.java:

    packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java

     

    [java]  view plain  copy
    1. private final Context mContext;   
    2.   
    3. private final CheckBoxPreference mCheckBox;  
    4.   
    5.    
    6.   
    7. //两个重要成员  
    8.   
    9. private final WifiManager mWifiManager;  
    10.   
    11. private final IntentFilter mIntentFilter;  


     

           wifienabler类中定义了四个成员变量很重要,mContext,mCheckBox,mWifiManager和mReceiver,其中mContext用于获取mwifiManager实例,mReceiver用来接收底层发来的消息,mCheckBox用来改变UI的状态。

    该类中定义了几个重要的函数onPreferenceChange,handleWifiStateChanged和handleStateChanged,onPreferenceChange用来处理按下的Enbler键,它会调用mWifiManager.setWifiEnabled(enable),另外两个用来处理接受的消息事件。

     

           在类的构造函数中,主要做了一下工作:初始化了mContext,mCheckBox,mWifimanager,并且初始化了一个mIntentFilter变量,添加了三个动作,在构造函数的上面定义了一个广播接收器,用来接收下层传来的消息,并根据intent动作的类型调用相应的处理函数,这个广播接收器在onResum函数中被注册。

    [java]  view plain  copy
    1. public WifiEnabler(Context context, CheckBoxPreferencecheckBox) {  
    2.   
    3.         mContext= context;  
    4.   
    5.        mCheckBox = checkBox;  
    6.   
    7.        mOriginalSummary = checkBox.getSummary();  
    8.   
    9.        checkBox.setPersistent(false);  
    10.   
    11.    
    12.   
    13.        mWifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);  
    14.   
    15.         mIntentFilter= new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);  
    16.   
    17.         // Theorder matters! We really should not depend on this. :(  
    18.   
    19.        mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);  
    20.   
    21.        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);  
    22.   
    23. }  


    这里可以总结为:如果上层需要监听或收到下层的消息,那么就要通过定义一个BroadcastReciever,并将它注册,当然在接受到消息后应该有处理消息的函数,然后在onReciever函数中根据消息调用相应的处理函数,这里的消息通知机制是Intent,在BroadcastReciever类的onReciever函数的参数中可以看出。

    该类成员函数的也是通过调用mWifimanager的接口来实现的。

     

     3) WifiManager:

           frameworks/base/wifi/java/android/net/wifi/WifiManager.java

           两个重要的数据成员:

    //WifiService

    IWifiManager mService;

        HandlermHandler;

    IWifiManager mService和HandlermHandler,这个类拥有了一个WifiService实例,就可以通过它进行一系列的调用;WifiManager中定义了的wifi和ap的状态,这些状态会在其他很多类中有使用;然后定义了大量的函数,这些函数几乎都是对WifiService接口函数的封装,直接调用WifiService的函数。

    该类的构造函数很简单:

    [java]  view plain  copy
    1. public WifiManager(IWifiManager service,Handler handler) {  
    2.   
    3.        mService = service;  
    4.   
    5.        mHandler = handler;  
    6.   
    7.     }  


    该类中还定义了一个WifiLock类,这个类用来保证在有应用程序使用Wifi无线电传输数据时,wifiradio可用,即当一个应用程序使用wifi的radio进行无线电数据传输时,就要先获得这个锁,如果该锁已被其他程序占有,就要等到该锁被释放后才能获得,只用当所有持有该锁的程序都释放该锁后,才能关闭radio功能。

     

     4)WifiService:

    frameworks/base/services/java/com/android/server/WifiService.java

    [java]  view plain  copy
    1. private final WifiStateTrackermWifiStateTracker;  
    2.   
    3. private Context mContext;  
    4.   
    5. private WifiWatchdogServicemWifiWatchdogService = null;  
    6.   
    7. private final  WifiHandler mWifiHandler;  


    这是WifiService中的几个重要的数据成员。

    在接下来的构造函数中初始化了mWifiStateTracker,mContext,然后动态生成mWifiThread子线程并启动,在主线程里用mWifiThread调用getLooper()函数获得线程的looper,来初始化创建一个mWifiHandler对象,这个WifiHandler在WifiService类的后面有定义,并重载了Handler类的handlermessage()函数,这样消息就可以在主线程里被处理了,这是android的handlerthread消息处理机制,可参考相关资料,这里不予详述。在构造函数的最后,注册了两个广播接收器,分别用来ACTION_AIRPLANE_MODE_CHANGED和ACTION_TETHER_STATE_CHANGED这两个动作,这里是android的intent消息通知机制,请参考相关资料,代码如下:

     

    [java]  view plain  copy
    1. mContext = context;  
    2.   
    3. mWifiStateTracker = tracker;  
    4.   
    5. mWifiStateTracker.enableRssiPolling(true);  
    6.   
    7. ……  
    8.   
    9. HandlerThread wifiThread = newHandlerThread("WifiService");  
    10.   
    11. wifiThread.start();  
    12.   
    13. mWifiHandler = newWifiHandler(wifiThread.getLooper());  
    14.   
    15. ……  


    随后定义了一系列的函数,其中有服务器要发送的命令的系列函数,它通过mWifiStateTracker成员类调用自己的的发送命令的接口(其实就是对本地接口的一个封装),最后通过适配层发送命令给wpa_supplicant,而事件处理只到WifiStateTracker层被处理。

    要注意的是,在WifiService中,定义了一些函数来创建消息,并通过mWifiHandler将消息发送到消息队列上,然后在mHandlerThread线程体run()分发\处理消息,在主线程中被mWifiHandler的handlerMessage()函数处理,最后调用mWifiStateTracker的对应函数来实现的。这里我也不明白为什么WifiService不直接调用mWifiStateTracker对应的函数,还要通过消息处理机制,绕了一圈在调用,当然Google这么做肯定是有它道理的,忘高手指点。

     

     5) WifiStateTracker类

    frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

    NetworkStateTracker继承了handler类,而WifiStateTracker继承了NetworkStateTracker类,就是说WifiStateTracker间接继承了handler类,属于一个事件处理类。

    WifiStateTracker类首先定义了事件日志和事件码(这里包含了所有可能的事件类型),还定义了如下的重要成员数据:

    [java]  view plain  copy
    1.           //几个重要的数据成员  
    2.   
    3. private WifiMonitor mWifiMonitor;    //被启动用来监听supplicant传来的消息  
    4.   
    5. private WifiInfo mWifiInfo;           
    6.   
    7. private WifiManager mWM;             //服务代理  
    8.   
    9. private DhcpHandler mDhcpTarget;  //IP地址获取线程  
    10.   
    11. private DhcpInfo mDhcpInfo;            //Dhcp的相关信息都在这里  


                   

    类的构造函数中,初始化了系列成员变量,包括生成了WifiMonitor的实例,在构造函数中,因为WifiStateTracker是一个handler间接子类,所以他会自动调用handler的无参构造函数,获得looper和Queue消息队列。

    然后定义了一些设置supplicant和更新网络信息的辅助函数。

           startEventLoop()函数很重要,用来启动WifiMonitor线程,进入消息循环检测。接着定义了系列通知函数,被WifiMonitor调用来向WifiStateTracker传递从wpa_supplicant接收到的消息,他会调用消息发送函数到消息队列,并被WifiStateTracker的handlermessage()函数处理。这个handlermessage()函数就是在随后被定义的,它主要是调用相应的辅助函数完成动作,并可能会将消息封装后,通过intent机制发送出去,被上层的UI活动接收处理。

    这里也定义了很多的WfiNative接口函数,这是JNI的本地接口;类DhcpHandler extends Handler{}也是在该类中定义的,它也是一个handler的子类,用来处理DHCP相关的消息EVENT_DHCP_START,可以想到它和WifiStateTracker不是共用一个looper。

           注意:handleMessage是在该文件中定义的,用来处理经WifiMonitor转换过的消息。

     

     6) WifiMonitor

           frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

    声明了一个重要的成员变量:mWifiStateTracker,并在构造函数中由参数提供初始化,还定义了一系列的可能从wpa_supplicant层接收的事件类型及其名字,这些是消息处理机制的基础。

    startMonitoring()函数,这是一个线程启动的封装函数,WifiStateTracker就是通过这个函数启动的WifiThread。

    这个重要的类classMonitorThreadextends Thread{};它是一个监控进程类,里面有一系列的事件处理函数和一个重要的Run()函数,run函数主要流程:connectToSupplicant()连接精灵进程wpa_supplicant,这里有一个mWifiStateTracker.notifySupplicantXXX()的调用,通知上层是否连接成功,然后就是一个轮询过程,其中调用了WifiNative.waitForEvent()本地轮询函数接口,并从返回的事件字符串类型中提取事件的名称,最后通过事件的名称调用相应的事件处理函数,并将事件转换成mWifiStateTracker能识别的类型上报。

           注意:这里的每个事件的捕获(由wifimonitor完成),最终都会调用相应的mWifiStateTracker的消息通知函数上报消息;轮询和事件处理都是在Monitor线程类中实现的。

     

     7)WifiNative

           frameworks/base/wifi/java/android/net/wifi/WifiNative.java

    里面定义了一个类WifiNative:其中声明了许多本地接口,可由native的标志看出,这是Java代码和本地库之间的联系接口;

     

     8) android_net_wifi_Wifi.java

    frameworks/base/core/jni/

           里面定义了许多的JNI的本地代码实现,每个实现中都会调用wpa_supplicant适配层的接口,通过包含适配层的头文件wifi.h获取适配层定义的接口;后面是一个JNINativeMethod数组,定义了每个本地接口和本地实现的对应关系;最后有一个注册函数regester_XXX_XX(),用以把上面的方法类数组注册到系统中。

           该类实现了本地接口的相关功能,并通过调用wpa的适配层的相关函数和wpa_supplicant通信,所以JNI是连接Java框架层和wpa适配层的桥梁.

     

     9)wpa_supplicant适配层,wifi.c:目录libhardware/wifi/

           里面定义很多字符串变量和适配层的接口实现,是对wpa_supplicant程序通信的接口封装,用来完成上层和wpa_supplicant的通信,头文件在libhardware/include/hardware下,这里的函数用来向JNI的本地实现提供调用接口。

           这里的函数,我把它们分为三类函数:

    一类是命令相关的(控制)函数,就是在JNI层android_XXX_Command()函数所调用的::Wifi_Command()函数,调用流程:android_XXX_command()=>docommand()=>wifi_command()=>wifi_send_command()=>wpa_ctrl_require()。

    二类是监听函数,即Wifi_wait_for_event()函数,调用流程:android_net_wifi_Waitforevent()=>wifi_wait_for_event()=>wpa_ctrl_recv()。

    三类是剩下的函数。

     

    10)wpa_supplicant与上层的接口,wpa_ctrl.c:external/wpa_supplicant

    定义了三类套接字,并分别实现了和wpa_supplicant的通信,因此wpa_supplicant适配层和wpa_supplicant层是通过socket通讯的。

    要是从wifi.c中真的很难看出它和wpa_supplicant有什么关系,和它联系密切的是wpa_ctrl.h文件,这里面定义了一个类wpa_ctrl,这个类中声明了两个Socket套接口,一个是本地一个是要连接的套接口,wpa_ctrl与wpa_supplicant的通信就需要socket来帮忙了,而wpa_supplicant就是通过调用wpa_ctrl.h中定义的函数和wpa_supplicant进行通讯的,wpa_ctrl类(其实是其中的两个socket)就是他们之间的桥梁。

     

     11)wpa_supplicant和driver_wext驱动接口的联系:

           driver.h:该文件定义了系列结构,首先是一个wpa_scan_result结构,这是一个扫描结果的通用格式,里面包含了扫描的所有信息(如BSSID,SSID,信号质量,噪音水平,支持的最大波特率等等信息),每个驱动接口实现负责将从驱动中上传的扫描信息的格式转换到该通用的扫描信息格式;然后是一些宏定义和枚举定义,最后也是最重要的是wpa_driver_ops结构,该结构是wpa driver的操作函数集合,里面有驱动接口的名称和很多的函数指针。

           drviers.c:该文件很简单,首先是一些外部变量的引用声明,都是不同驱动操作接口的集合wpa_driver_XXX_ops变量;然后就是定义一个驱动操作接口集合的数组,根据宏定义添加对应的驱动操作接口集合的变量。

           drvier_XXX.h:这是不同驱动接口头文件,主要声明了操作接口

           drvier_XXX.c:实现操作接口,定义一个操作集合变量,并用上面定义的函数初始化其中的函数指针

           注意:下面要搞清楚wpa_supplicant守护进程是如何操作,最后调用驱动接口集合中的函数的;要知道wpa_supplicant是为不同驱动和操作系统具有更好移植性而被设计的,以便在wpa_supplicant层不用实现驱动的具体接口就可以添加新的驱动程序;在wpa_supplicant结构中有一个wpa_drv_ops类型的drvier成员,在wpa_supplicant进程中,经常通过Wpa_supplicant_XXX函数传递wpa_supplicant实例指针wpa_s参数给wpa_drv_XXX函数来调用它,在wpa_drv_XX中会通过wpa_s->driver->XXX()的流程来调用通用驱动接口,简单才是生活的真相,可android始终让我感到真相还是遥不可及。

     

     12)WifiWatchdogService:

    首先声明了两个主要变量mWifiStateTracker,mWifiManager,需要这两个类对象来完成具体的控制工作,在WifiWatchdogService的构造函数中,创建了这两个类,并通过regesterForWifiBroadcasts()注册了BroadcastReceiver,BroadcastReceiver是用来获取网络变化的,然后启动了一个WatchdogTread线程,用来处理从WifiStateTracker接收到的消息。

     

    frameworks/base/services/java/com/android/server/WifiWatchdogService.java

    [java]  view plain  copy
    1. WifiWatchdogService(Context context,WifiStateTracker wifiStateTracker) {  
    2.   
    3.        mContext = context;  
    4.   
    5.        mContentResolver = context.getContentResolver();  
    6.   
    7.        mWifiStateTracker =wifiStateTracker;  
    8.   
    9.         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);  
    10.   
    11.          
    12.   
    13.        createThread();  
    14.   
    15.          
    16.   
    17.        // The content observer to listen needs a handler, which createThreadcreates  
    18.   
    19.        registerForSettingsChanges();  
    20.   
    21.        if (isWatchdogEnabled()) {  
    22.   
    23.            registerForWifiBroadcasts();  
    24.   
    25.        }  
    26.   
    27.          
    28.   
    29.        if (V) {  
    30.   
    31.            myLogV("WifiWatchdogService: Created");  
    32.   
    33.        }  
    34.   
    35.     }  


           WifiWatchdogHandler继承了handler类,成为一个消息处理类,定义handlemessage()函数,处理消息队列上的消息。

     

    二,wpa_supplicant再解析

    1)wpa_ctrl.h:

           该文件中并没有定义structwpa_ctrl结构,因为在其他包含该文件的.c文件中不能直接使用该结构的成员,这里主要声明了几个用于使用socket通信的函数接口,包括:wpa_ctrl_open,wpa_ctrl_close,wpa_ctrl_attach,wpa_ctrl_detach,wpa_ctrl_cleanup,wpa_ctrl_recv,wpa_ctrl_request, wpa_ctrl_pending, wpa_ctrl_get_fd 等函数。

           这些函数的功能从名字上能看出,open函数就是创建一个socket接口,并绑定连接wpa_supplicant,attach函数用于定义一个控制接口为监听接口,pending函数用于查询有无消息可读处理,request和recv分别用来发送和读取消息。

           其实,这里就是一个使用socket通信的封装,具体内容可以参考socket编程。

     

    2)wpa_ctrl.c:

           这里首先定义了一个wpa_ctrl结构,里面根据UDP,UNIX和命名管道三种domain类型来定义通信实体:

    [cpp]  view plain  copy
    1. struct wpa_ctrl {  
    2.   
    3. #ifdefCONFIG_CTRL_IFACE_UDP  
    4.   
    5.        int s;  
    6.   
    7.        struct sockaddr_in local;  
    8.   
    9.        struct sockaddr_in dest;  
    10.   
    11.        char *cookie;  
    12.   
    13. #endif /*CONFIG_CTRL_IFACE_UDP */  
    14.   
    15. #ifdefCONFIG_CTRL_IFACE_UNIX  
    16.   
    17.        int s;  
    18.   
    19.        struct sockaddr_un local;  
    20.   
    21.        struct sockaddr_un dest;  
    22.   
    23. #endif /*CONFIG_CTRL_IFACE_UNIX */  
    24.   
    25. #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE  
    26.   
    27.        HANDLE pipe;  
    28.   
    29. #endif /*CONFIG_CTRL_IFACE_NAMED_PIPE */  
    30.   
    31. };  


           然后是根据上面三个类型分别实现了wpa_ctrl.h中声明的接口函数,这里就不做介绍了。

     

    3)wpa_supplicant.h:

           首先,定义了一个枚举wpa_event_type,罗列了系列wpa的事件类型,然后就是wpa_event_data类型,随后是两个函数:wpa_supplicant_event和wpa_supplicant_rx_eapol。

    wpa_supplicant.c:首先定义一个驱动操作数组externstruct wpa_driver_ops *wpa_supplicant_drivers[],然后是系列wpa_supplicant_XXX()函数,很多函数里面调用wpa_drv_XXX()函数,这些函数是wpa_supplicant_i.h中实现的函数。几乎每个函数都需要一个wpa_supplicant结构,对其进行所有的控制和通信操作。

     

    4)wpa_supplicant_i.h:

           开头定义了几个结构,如:wpa_blacklist,wpa_interface,wpa_params,wpa_global,wpa_client_mlme和wpa_supplicant等结构,其中wpa_supplicant最为重要,wpa_supplicant结构里有一个重要的driver成员,它是wpa_driver_ops类型,可以被用来调用抽象层的接口。

           接下来是系列函数声明,这些函数声明在wpa_supplicant.c中实现,然后就是wpa_drv_XXX函数,这些函数就是在wpa_supplicant.c中被wpa_supplicant_xxx函数调用的,而这些wpa_drv_xxx函数也都有一个wpa_supplicant结构的变量指针,用来调用封装的抽象接口。

           这里要注意的是:在wpa_suppliant.c文件中定义的很多函数是在该头文件中声明的,而不是在wpa_supplicant.h中声明的。

     

    5)driver.h:

           该文件中定义了一个重要的数据结构:wpa_scan_result,这是一个从驱动发来的数据被封装成的通用的扫描结果数据结构,每个驱动结构的实现都要遵循的扫描结果格式,比如driver_wext.c中的实现。后面还有定义了很多的数据结构,这里不具表。

           文件中最重要的一个数据结构是:wpa_driver_ops,这是所有驱动接口层必须为之实现的API,是所有驱动类型的一个接口封装包,wpa_supplicant就是通过该接口来和驱动交互的。

     

    6)driver_wext.h:

           该文件很简单,就是声明了该驱动的一些对应驱动API接口的函数。

    driver_wext.c:

           此文件就是实现了上面的一些函数,最后初始化了一个wpa_drv_ops变量。

     

    三,Wifi模块解析

    1)框架分析

                                 

     

    图示1:Wifi框架

           首先,用户程序使用WifiManager类来管理Wifi模块,它能够获得Wifi模块的状态,配置和控制Wifi模块,而所有这些操作都要依赖Wifiservice类来实现。

           WifiService和WifiMonitor类是Wifi框架的核心,如图所示。下面先来看看WifiService是什么时候,怎么被创建和初始化的。

           在systemServer启动之后,它会创建一个ConnectivityServer对象,这个对象的构造函数会创建一个WifiService的实例,代码如下所示:

     

    framework/base/services/java/com/android/server/ConnectivityService.java

    [java]  view plain  copy
    1. {  
    2.   
    3. ……  
    4.   
    5. case ConnectivityManager.TYPE_WIFI:  
    6.   
    7.                if (DBG) Slog.v(TAG, "Starting Wifi Service.");  
    8.   
    9.                WifiStateTracker wst = new WifiStateTracker(context, mHandler);                             //创建WifiStateTracker实例  
    10.   
    11.                 WifiService wifiService = newWifiService(context, wst);//创建WifiService实例  
    12.   
    13.                ServiceManager.addService(Context.WIFI_SERVICE, wifiService);           //向服务管理系统添加Wifi服务  
    14.   
    15.                wifiService.startWifi();     //启动Wifi  
    16.   
    17.                mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;  
    18.   
    19.                 wst.startMonitoring(); //启动WifiMonitor中的WifiThread线程  
    20.   
    21. ……  
    22.   
    23. }  


           WifiService的主要工作:WifiMonitor和Wpa_supplicant的启动和关闭,向Wpa_supplicant发送命令。

           WifiMonitor的主要工作:阻塞监听并接收来自Wpa_supplicant的消息,然后发送给WifiStateTracker。

           上面两个线程通过AF_UNIX套接字和Wpa_supplicant通信,在通信过程中有两种连接方式:控制连接和监听连接。它们创建代码如下:

    [java]  view plain  copy
    1. ctrl_conn =wpa_ctrl_open(ifname);  
    2.   
    3. .. .. ..  
    4.   
    5.  monitor_conn = wpa_ctrl_open(ifname);  


     

     

    2)Wifi启动流程

           (1)使能Wifi

           要想使用Wifi模块,必须首先使能Wifi,当你第一次按下Wifi使能按钮时,WirelessSettings会实例化一个WifiEnabler对象,实例化代码如下:

     

    packages/apps/settings/src/com/android/settings/WirelessSettings.java

    [java]  view plain  copy
    1. protected void onCreate(Bundle savedInstanceState) {  
    2.   
    3.        super.onCreate(savedInstanceState);  
    4.   
    5. ……  
    6.   
    7.               CheckBoxPreferencewifi = (CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI);  
    8.   
    9.               mWifiEnabler= new WifiEnabler(this, wifi);  
    10.   
    11. ……  
    12.   
    13. }  


           WifiEnabler类的定义大致如下,它实现了一个监听接口,当WifiEnabler对象被初始化后,它监听到你按键的动作,会调用响应函数onPreferenceChange(),这个函数会调用WifiManager的setWifiEnabled()函数。

    [java]  view plain  copy
    1. public class WifiEnabler implementsPreference.OnPreferenceChangeListener {  
    2.   
    3. ……  
    4.   
    5. public boolean onPreferenceChange(Preference preference,Object value) {  
    6.   
    7.         booleanenable = (Boolean) value;  
    8.   
    9. ……  
    10.   
    11. if (mWifiManager.setWifiEnabled(enable)) {  
    12.   
    13.                 mCheckBox.setEnabled(false);  
    14.   
    15. ……  
    16.   
    17. }  
    18.   
    19. ……  
    20.   
    21. }  


           我们都知道Wifimanager只是个服务代理,所以它会调用WifiService的setWifiEnabled()函数,而这个函数会调用sendEnableMessage()函数,了解android消息处理机制的都知道,这个函数最终会给自己发送一个MESSAGE_ENABLE_WIFI的消息,被WifiService里面定义的handlermessage()函数处理,会调用setWifiEnabledBlocking()函数。下面是调用流程:

     

    mWifiEnabler.onpreferencechange()=>mWifiManage.setWifienabled()=>mWifiService.setWifiEnabled()=>mWifiService.sendEnableMessage()=>mWifiService.handleMessage()=>mWifiService.setWifiEnabledBlocking().

     

    在setWifiEnabledBlocking()函数中主要做如下工作:加载Wifi驱动,启动wpa_supplicant,注册广播接收器,启动WifiThread监听线程。代码如下:

    [java]  view plain  copy
    1. ……  
    2.   
    3. if (enable) {  
    4.   
    5.            if (!mWifiStateTracker.loadDriver()) {  
    6.   
    7.                Slog.e(TAG, "Failed toload Wi-Fi driver.");  
    8.   
    9.                setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);  
    10.   
    11.                 return false;  
    12.   
    13.            }  
    14.   
    15.            if (!mWifiStateTracker.startSupplicant()) {  
    16.   
    17.                 mWifiStateTracker.unloadDriver();  
    18.   
    19.                 Slog.e(TAG, "Failed tostart supplicant daemon.");  
    20.   
    21.                setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);  
    22.   
    23.                 return false;  
    24.   
    25.            }  
    26.   
    27.    
    28.   
    29.            registerForBroadcasts();  
    30.   
    31.            mWifiStateTracker.startEventLoop();  
    32.   
    33. ……  


           至此,Wifi使能结束,自动进入扫描阶段。

     

    (2) 扫描AP

           当驱动加载成功后,如果配置文件的AP_SCAN = 1,扫描会自动开始,WifiMonitor将会从supplicant收到一个消息EVENT_DRIVER_STATE_CHANGED,调用handleDriverEvent(),然后调用mWifiStateTracker.notifyDriverStarted(),该函数向消息队列添加EVENT_DRIVER_STATE_CHANGED,handlermessage()函数处理消息时调用scan()函数,并通过WifiNative将扫描命令发送到wpa_supplicant。

     

    Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

    [java]  view plain  copy
    1. private void handleDriverEvent(Stringstate) {  
    2.   
    3.            if (state == null) {  
    4.   
    5.                 return;  
    6.   
    7.            }  
    8.   
    9.            if (state.equals("STOPPED")) {  
    10.   
    11.                mWifiStateTracker.notifyDriverStopped();  
    12.   
    13.            } else if (state.equals("STARTED")) {  
    14.   
    15.                 mWifiStateTracker.notifyDriverStarted();  
    16.   
    17.            } else if (state.equals("HANGED")) {  
    18.   
    19.                 mWifiStateTracker.notifyDriverHung();  
    20.   
    21.            }  
    22.   
    23.        }  


     

    Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

    [java]  view plain  copy
    1. case EVENT_DRIVER_STATE_CHANGED:  
    2.   
    3.            
    4.   
    5.                 switch(msg.arg1) {  
    6.   
    7.                 case DRIVER_STARTED:  
    8.   
    9.                     /** 
    10.  
    11.                      *Set the number of allowed radio channels according 
    12.  
    13.                      *to the system setting, since it gets reset by the 
    14.  
    15.                      *driver upon changing to the STARTED state. 
    16.  
    17.                      */  
    18.   
    19.                     setNumAllowedChannels();  
    20.   
    21.                    synchronized (this) {  
    22.   
    23.                        if (mRunState == RUN_STATE_STARTING) {  
    24.   
    25.                            mRunState = RUN_STATE_RUNNING;  
    26.   
    27.                            if (!mIsScanOnly) {  
    28.   
    29.                                 reconnectCommand();  
    30.   
    31.                            } else {  
    32.   
    33.                                 // In somesituations, supplicant needs to be kickstarted to  
    34.   
    35.                                 // start thebackground scanning  
    36.   
    37.                                 scan(true);  
    38.   
    39.                            }  
    40.   
    41.                        }  
    42.   
    43.                     }  
    44.   
    45.                    break;        


           

     

    上面是启动Wifi时,自动进行的AP的扫描,用户当然也可以手动扫描AP,这部分实现在WifiService里面,WifiService通过startScan()接口函数发送扫描命令到supplicant。

     

    Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

    [java]  view plain  copy
    1. public boolean startScan(booleanforceActive) {  
    2.   
    3.        enforceChangePermission();  
    4.   
    5.    
    6.   
    7.        switch (mWifiStateTracker.getSupplicantState()) {  
    8.   
    9.            case DISCONNECTED:  
    10.   
    11.            case INACTIVE:  
    12.   
    13.            case SCANNING:  
    14.   
    15.            case DORMANT:  
    16.   
    17.                 break;  
    18.   
    19.            default:  
    20.   
    21.                mWifiStateTracker.setScanResultHandling(  
    22.   
    23.                        WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);  
    24.   
    25.                 break;  
    26.   
    27.        }  
    28.   
    29.        return mWifiStateTracker.scan(forceActive);  
    30.   
    31.     }  


           然后下面的流程同上面的自动扫描,我们来分析一下手动扫描从哪里开始的。我们应该知道手动扫描是通过菜单键的扫描键来响应的,而响应该动作的应该是WifiSettings类中Scanner类的handlerMessage()函数,它调用WifiManager的startScanActive(),这才调用WifiService的startScan()。

     

    packages/apps/Settings/src/com/android/settings/wifiwifisettings.java

    [java]  view plain  copy
    1. public boolean onCreateOptionsMenu(Menu menu) {  
    2.   
    3.        menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)  
    4.   
    5.                .setIcon(R.drawable.ic_menu_scan_network);  
    6.   
    7.        menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)  
    8.   
    9.                .setIcon(android.R.drawable.ic_menu_manage);  
    10.   
    11.        return super.onCreateOptionsMenu(menu);  
    12.   
    13.     }  


     

           当按下菜单键时,WifiSettings就会调用这个函数绘制菜单。如果选择扫描按钮,WifiSettings会调用onOptionsItemSelected()。

     

    packages/apps/Settings/src/com/android/settings/wifiwifisettings.java

    [java]  view plain  copy
    1. public booleanonOptionsItemSelected(MenuItem item) {  
    2.   
    3.        switch (item.getItemId()) {  
    4.   
    5.            case MENU_ID_SCAN:  
    6.   
    7.                 if(mWifiManager.isWifiEnabled()) {  
    8.   
    9.                     mScanner.resume();  
    10.   
    11.                 }  
    12.   
    13.                 return true;  
    14.   
    15.            case MENU_ID_ADVANCED:  
    16.   
    17.                 startActivity(new Intent(this,AdvancedSettings.class));  
    18.   
    19.                 return true;  
    20.   
    21.        }  
    22.   
    23.        return super.onOptionsItemSelected(item);  
    24.   
    25. }  
    26.   
    27.    
    28.   
    29. private class Scanner extends Handler {  
    30.   
    31.        private int mRetry = 0;  
    32.   
    33.    
    34.   
    35.        void resume() {  
    36.   
    37.            if (!hasMessages(0)) {  
    38.   
    39.                 sendEmptyMessage(0);  
    40.   
    41.            }  
    42.   
    43.        }  
    44.   
    45.    
    46.   
    47.        void pause() {  
    48.   
    49.            mRetry = 0;  
    50.   
    51.             mAccessPoints.setProgress(false);  
    52.   
    53.            removeMessages(0);  
    54.   
    55.        }  
    56.   
    57.    
    58.   
    59.        @Override  
    60.   
    61.        public void handleMessage(Message message) {  
    62.   
    63.            if (mWifiManager.startScanActive()){  
    64.   
    65.                 mRetry = 0;  
    66.   
    67.            } else if (++mRetry >= 3) {  
    68.   
    69.                 mRetry = 0;  
    70.   
    71.                Toast.makeText(WifiSettings.this, R.string.wifi_fail_to_scan,  
    72.   
    73.                        Toast.LENGTH_LONG).show();  
    74.   
    75.                 return;  
    76.   
    77.            }  
    78.   
    79.            mAccessPoints.setProgress(mRetry != 0);  
    80.   
    81.            sendEmptyMessageDelayed(06000);  
    82.   
    83.        }  
    84.   
    85.     }  


          

    这里的mWifiManager.startScanActive()就会调用WifiService里的startScan()函数,下面的流程和上面的一样,这里不赘述。

    当supplicant完成了这个扫描命令后,它会发送一个消息给上层,提醒他们扫描已经完成,WifiMonitor会接收到这消息,然后再发送给WifiStateTracker。

     

    Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

    [java]  view plain  copy
    1. void handleEvent(int event, String remainder) {  
    2.   
    3.             switch (event) {  
    4.   
    5.                 caseDISCONNECTED:  
    6.   
    7.                    handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED,remainder);  
    8.   
    9.                     break;  
    10.   
    11.    
    12.   
    13.                 case CONNECTED:  
    14.   
    15.                    handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder);  
    16.   
    17.                     break;  
    18.   
    19.    
    20.   
    21.                 case SCAN_RESULTS:  
    22.   
    23.                     mWifiStateTracker.notifyScanResultsAvailable();  
    24.   
    25.                     break;  
    26.   
    27.    
    28.   
    29.                 case UNKNOWN:  
    30.   
    31.                     break;  
    32.   
    33.             }  
    34.   
    35.         }  


    WifiStateTracker将会广播SCAN_RESULTS_AVAILABLE_ACTION消息:

     

    Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

    [java]  view plain  copy
    1. public voidhandleMessage(Message msg) {  
    2.   
    3.         Intent intent;  
    4.   
    5. ……  
    6.   
    7. case EVENT_SCAN_RESULTS_AVAILABLE:  
    8.   
    9.                 if(ActivityManagerNative.isSystemReady()) {  
    10.   
    11.                     mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));  
    12.   
    13.                 }  
    14.   
    15.                 sendScanResultsAvailable();  
    16.   
    17.                 /** 
    18.  
    19.                  * On receiving the first scanresults after connecting to 
    20.  
    21.                  * the supplicant, switch scanmode over to passive. 
    22.  
    23.                  */  
    24.   
    25.                 setScanMode(false);  
    26.   
    27.                 break;  
    28.   
    29. ……  
    30.   
    31. }  


           由于WifiSettings类注册了intent,能够处理SCAN_RESULTS_AVAILABLE_ACTION消息,它会调用handleEvent(),调用流程如下所示。

     

    WifiSettings.handleEvent() =>WifiSettings.updateAccessPoints() => mWifiManager.getScanResults() => mService.getScanResults()=> mWifiStateTracker.scanResults() => WifiNative.scanResultsCommand()……

     

    将获取AP列表的命令发送到supplicant,然后supplicant通过Socket发送扫描结果,由上层接收并显示。这和前面的消息获取流程基本相同。

     

    (3)配置,连接AP

    当用户选择一个活跃的AP时,WifiSettings响应打开一个对话框来配置AP,比如加密方法和连接AP的验证模式。配置好AP后,WifiService添加或更新网络连接到特定的AP。

     

    packages/apps/settings/src/com/android/settings/wifi/WifiSetttings.java

    [java]  view plain  copy
    1. public booleanonPreferenceTreeClick(PreferenceScreen screen, Preference preference) {  
    2.   
    3.        if (preference instanceof AccessPoint) {  
    4.   
    5.            mSelected = (AccessPoint) preference;  
    6.   
    7.            showDialog(mSelected, false);  
    8.   
    9.        } else if (preference == mAddNetwork) {  
    10.   
    11.            mSelected = null;  
    12.   
    13.            showDialog(nulltrue);  
    14.   
    15.        } else if (preference == mNotifyOpenNetworks) {  
    16.   
    17.            Secure.putInt(getContentResolver(),  
    18.   
    19.                    Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,  
    20.   
    21.                    mNotifyOpenNetworks.isChecked() ? 1 : 0);  
    22.   
    23.        } else {  
    24.   
    25.            return super.onPreferenceTreeClick(screen, preference);  
    26.   
    27.        }  
    28.   
    29.        return true;  
    30.   
    31.     }  
    32.   
    33.    


           配置好以后,当按下“Connect Press”时,WifiSettings通过发送LIST_NETWORK命令到supplicant来检查该网络是否配置。如果没有该网络或没有配置它,WifiService调用addorUpdateNetwork()函数来添加或更新网络,然后发送命令给supplicant,连接到这个网络。下面是从响应连接按钮到WifiService发送连接命令的代码:

     

    packages/apps/settings/src/com/android/settings/wifi/WifiSetttings.java

    [java]  view plain  copy
    1. public void onClick(DialogInterfacedialogInterface, int button) {  
    2.   
    3.        if (button == WifiDialog.BUTTON_FORGET && mSelected != null) {  
    4.   
    5.            forget(mSelected.networkId);  
    6.   
    7.        } else if (button == WifiDialog.BUTTON_SUBMIT && mDialog !=null) {  
    8.   
    9.            WifiConfiguration config = mDialog.getConfig();  
    10.   
    11.    
    12.   
    13.            if (config == null) {  
    14.   
    15.                 if (mSelected != null&& !requireKeyStore(mSelected.getConfig())) {  
    16.   
    17.                     connect(mSelected.networkId);  
    18.   
    19.                 }  
    20.   
    21.            } else if (config.networkId != -1) {  
    22.   
    23.                 if (mSelected != null) {  
    24.   
    25.                     mWifiManager.updateNetwork(config);  
    26.   
    27.                     saveNetworks();  
    28.   
    29.                 }  
    30.   
    31.            } else {  
    32.   
    33.                 int networkId =mWifiManager.addNetwork(config);  
    34.   
    35.                 if (networkId != -1) {  
    36.   
    37.                    mWifiManager.enableNetwork(networkId, false);  
    38.   
    39.                     config.networkId =networkId;  
    40.   
    41.                     if (mDialog.edit || requireKeyStore(config)){  
    42.   
    43.                         saveNetworks();  
    44.   
    45.                     } else {  
    46.   
    47.                         connect(networkId);  
    48.   
    49.                     }  
    50.   
    51.                 }  
    52.   
    53.            }  
    54.   
    55.        }  
    56.   
    57.     }  


          

          

    Frameworks\base\wifi\java\android\net\wifi\WifiManager.java

        

    [java]  view plain  copy
    1.    public intupdateNetwork(WifiConfiguration config) {  
    2.   
    3.         if(config == null || config.networkId < 0) {  
    4.   
    5.            return -1;  
    6.   
    7.         }  
    8.   
    9.         return addOrUpdateNetwork(config);  
    10.   
    11. }  
    12.   
    13. private intaddOrUpdateNetwork(WifiConfiguration config) {  
    14.   
    15.        try {  
    16.   
    17.            return mService.addOrUpdateNetwork(config);  
    18.   
    19.        } catch (RemoteException e) {  
    20.   
    21.            return -1;  
    22.   
    23.        }  
    24.   
    25.     }  


     

    WifiService.addOrUpdateNetwork()通过调用mWifiStateTracker.setNetworkVariable()将连接命令发送到Wpa_supplicant。

     

    (4)获取IP地址

    当连接到supplicant后,WifiMonitor就会通知WifiStateTracker。

     

    Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

    [java]  view plain  copy
    1. Public void Run(){  
    2.   
    3. if (connectToSupplicant()) {  
    4.   
    5.                 // Send a message indicatingthat it is now possible to send commands  
    6.   
    7.                 // to the supplicant  
    8.   
    9.                 mWifiStateTracker.notifySupplicantConnection();  
    10.   
    11.            } else {  
    12.   
    13.                mWifiStateTracker.notifySupplicantLost();  
    14.   
    15.                 return;  
    16.   
    17.            }  
    18.   
    19. ……  
    20.   
    21. }  


     

    WifiStateTracker发送EVENT_SUPPLICANT_CONNECTION消息到消息队列,这个消息有自己的handlermessage()函数处理,它会启动一个DHCP线程,而这个线程会一直等待一个消息事件,来启动DHCP协议分配IP地址。

     

    frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

    [java]  view plain  copy
    1. void notifySupplicantConnection() {  
    2.   
    3.        sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION);  
    4.   
    5. }  
    6.   
    7.    
    8.   
    9. public void handleMessage(Message msg) {  
    10.   
    11.        Intent intent;  
    12.   
    13.    
    14.   
    15.        switch (msg.what) {  
    16.   
    17.            case EVENT_SUPPLICANT_CONNECTION:  
    18.   
    19.              ……  
    20.   
    21.              HandlerThread dhcpThread = newHandlerThread("DHCP Handler Thread");  
    22.   
    23.                 dhcpThread.start();  
    24.   
    25.                 mDhcpTarget = newDhcpHandler(dhcpThread.getLooper(), this);  
    26.   
    27. ……  
    28.   
    29. ……  
    30.   
    31. }  


    当Wpa_supplicant连接到AP后,它会发送一个消息给上层来通知连接成功,WifiMonitor会接受到这个消息并上报给WifiStateTracker。

     

    Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

    [java]  view plain  copy
    1. void handleEvent(int event, String remainder) {  
    2.   
    3.            switch (event) {  
    4.   
    5.                case DISCONNECTED:  
    6.   
    7.                    handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED,remainder);  
    8.   
    9.                    break;  
    10.   
    11.    
    12.   
    13.                case CONNECTED:  
    14.   
    15.                    handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder);  
    16.   
    17.                    break;  
    18.   
    19.                 ……  
    20.   
    21. }  
    22.   
    23.    
    24.   
    25. private voidhandleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {  
    26.   
    27.         StringBSSID = null;  
    28.   
    29.         intnetworkId = -1;  
    30.   
    31.         if(newState == NetworkInfo.DetailedState.CONNECTED) {  
    32.   
    33.            Matcher match = mConnectedEventPattern.matcher(data);  
    34.   
    35.             if(!match.find()) {  
    36.   
    37.                if (Config.LOGD) Log.d(TAG, "Could not find BSSID in CONNECTEDevent string");  
    38.   
    39.             }else {  
    40.   
    41.                BSSID = match.group(1);  
    42.   
    43.                try {  
    44.   
    45.                    networkId = Integer.parseInt(match.group(2));  
    46.   
    47.                } catch (NumberFormatException e) {  
    48.   
    49.                    networkId = -1;  
    50.   
    51.                 }  
    52.   
    53.             }  
    54.   
    55.         }  
    56.   
    57.         mWifiStateTracker.notifyStateChange(newState,BSSID, networkId);  
    58.   
    59. }  
    60.   
    61.          
    62.   
    63. void notifyStateChange(DetailedState newState, StringBSSID, int networkId) {  
    64.   
    65.         Messagemsg = Message.obtain(  
    66.   
    67.            this, EVENT_NETWORK_STATE_CHANGED,  
    68.   
    69.             newNetworkStateChangeResult(newState, BSSID, networkId));  
    70.   
    71.        msg.sendToTarget();  
    72.   
    73.     }  
    74.   
    75.    
    76.   
    77. caseEVENT_NETWORK_STATE_CHANGED:  
    78.   
    79. ……  
    80.   
    81. configureInterface();  
    82.   
    83. ……  
    84.   
    85.    
    86.   
    87. private void configureInterface() {  
    88.   
    89.        checkPollTimer();  
    90.   
    91.         mLastSignalLevel = -1;  
    92.   
    93.         if(!mUseStaticIp) {          //使用DHCP线程动态IP  
    94.   
    95.             if(!mHaveIpAddress && !mObtainingIpAddress) {  
    96.   
    97.                mObtainingIpAddress = true;  
    98.   
    99.    
    100.   
    101.                                    //发送启动DHCP线程获取IP  
    102.   
    103.                 mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);  
    104.   
    105.             }  
    106.   
    107.         } else {        //使用静态IP,IP信息从mDhcpInfo中获取  
    108.   
    109.             intevent;  
    110.   
    111.             if(NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {  
    112.   
    113.                mHaveIpAddress = true;  
    114.   
    115.                event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;  
    116.   
    117.                if (LOCAL_LOGD) Log.v(TAG, "Static IP configurationsucceeded");  
    118.   
    119.             }else {  
    120.   
    121.                mHaveIpAddress = false;  
    122.   
    123.                event = EVENT_INTERFACE_CONFIGURATION_FAILED;  
    124.   
    125.                if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");  
    126.   
    127.             }  
    128.   
    129.            sendEmptyMessage(event);           //发送IP获得成功消息事件  
    130.   
    131.         }  
    132.   
    133.     }  


                  DhcpThread获取EVENT_DHCP_START消息事件后,调用handleMessage()函数,启动DHCP获取IP地址的服务。

     

    [java]  view plain  copy
    1. public void handleMessage(Message msg) {  
    2.   
    3.             intevent;  
    4.   
    5. switch (msg.what) {  
    6.   
    7.                case EVENT_DHCP_START:  
    8.   
    9.    
    10.   
    11. ……  
    12.   
    13. Log.d(TAG, "DhcpHandler: DHCP requeststarted");  


     

    //启动一个DHCPclient的精灵进程,为mInterfaceName请求分配一个IP地//址

       

    [java]  view plain  copy
    1.  if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {  
    2.   
    3.      event= EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;  
    4.   
    5.          if(LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded");  
    6.   
    7.     } else {  
    8.   
    9.            event= EVENT_INTERFACE_CONFIGURATION_FAILED;  
    10.   
    11.            Log.i(TAG,"DhcpHandler: DHCP request failed: " +  
    12.   
    13.                             NetworkUtils.getDhcpError());  
    14.   
    15.         }  
    16.   
    17. ……  
    18.   
    19. }  


    这里调用了一个NetworkUtils.runDhcp()函数,NetworkUtils类是一个网络服务的辅助类,它主要定义了一些本地接口,这些接口会通过他们的JNI层android_net_NetUtils.cpp文件和DHCP client通信,并获取IP地址。

    至此,IP地址获取完毕,Wifi启动流程结束。

    展开全文
  • WIFI智能插座设计,APP控制(原理图、PCB、MCU源码、APP源码),ad 设计的工程文件,包括原理图及PCB印制板图,可以用Altium Designer(AD)软件打开或修改,可作为你产品设计的参考。
  • server功能都在[WebService.java](./app/src/main/java/com/baidusoso/wifitransfer/WebService.java)这个文件中。下面简单说一下本项目中用到一些传输API: 获取x-www-form-urlencoded请求体 UrlEncodedFormBody ...
  • 背景近日在做一个代码阅读器,其中涉及到代码文件的上传,之前看到过许多app支持局域网传文件,因此就通过查询和研究实现了此功能,我是用的框架是CocoaHTTPServer。原理CocoaHTTPServer框架能够在iOS上建立起一个...
  • STM32单片机通过ESP8266WiFi模块与Android APP实现数据传输,包含STM32单片机的源代码和手机APP的源代码,里面也有apk文件可以直接安装使用。代码全部测试过,都可以正常的运行和使用。
  • iOS APPWiFi配置相关

    千次阅读 2017-04-12 09:37:56
    本人做智能家居的APP,智能硬件设备自带WiFi(局域网)。如果用户没有连接设备的WiFi进入APP时,需要提示用户去设置界面连接WiFi。 需求一、APP内部跳转到系统WIFi 界面手动连接 实现 info里面设置 在项目中的...
  • 上篇中实现了访问APP中内置的静态网页 移动端搭建Http Server(六)—— 实现APP中内置静态网页,本篇会继续实现另一个功能——wifi传图1.实现思路 ImageUploadHandler中读取图片二进制数据并保存到文件 将图片路径...
  • [Android app] 本地wifi密码查看器源码

    千次阅读 2017-01-25 12:34:25
    ZXing生成二维码给别的手机链接二、原理获取本地保存密码的文件进行解析,本地保存wifi密码的路径为: /data/misc/wifi/*.conf 生成二维码连接的字符串是根据小米的方式来生成的,格式为: WIFI:T:WPA;P:”密码”;...
  • android和电脑通过wifi进行文件传输源码,pc端使用swing开发的客户端
  • 在apk检测更新后,下载服务器最新apk,4G下的话 会下载以前旧版本的apk(当然就不能覆盖安装了正常升级了),而用wifi下就没问题,会下载到最新的apk,这是为什么?同一个URL啊明明
  • 实现的功能:  1、实时检测 6个温度传感器... 2、通过 WIFI 发射,实现手机 APP 接收;  3、可以根据设计要求,增减温度传感器数量;  4、传感器采用单总线接入,方便实用;  5、可以在实物和仿真上实现效果;
  •   本文介绍,在同一Wifi环境下,Android手机和PC实现文件共享功能的APPAPP我取名为 WiFi Share。 前言:通常,我们在手机和电脑之间传输文件,最先想到的就是通过USB数据线进行文件传输,这样在已经插上usb数据...
  • Wifi万能钥匙去广告显密码,搬运分享 平台:安卓 文件格式:apk MD5:0887fea1f229d2bf40915bd48c722e0c
  • 一张ESP8266WIFI模块(某宝买很便宜) 一根支持数据传输的安卓数据线 一台PC 二.项目所需软件 1.Arduino IDE 开发软件 这里大概说一下windows下的安装, 下载地址 如果你是windows系统选择好对应的操作系统版本,...
  • 自建WIFI热点传输应用评测: 还在用蓝牙传文件 你是不是遇到过这样的情况:朋友聚会的时候,手机中有好的游戏、音乐或者电影想要和朋友分享,在手边没有电脑没有数据线时你首先想到的应该就是蓝牙,但是...
  • wifidog 配置文件

    千次阅读 2017-09-08 20:30:53
    下面回到路由器,编辑wifidog.conf,一般情况下,我们...下面是我的配置:opk安装包:luci-app-wifidog-all.ipk链接:http://pan.baidu.com/s/1dFfD4M9 密码:qgoo还有安装完插件后会在/etc/文件目录下释放一个wif...
  • 破解某Wifi APP,无需Root也可查看密码

    万次阅读 热门讨论 2016-03-24 17:06:54
    这款蹭网神器也是用了一段时间了,遗憾的就是不能看到别人的Wifi密码。 虽说小米手机可以扫描二维码看到wifi密码,但每次都这么操作还是比较蛋疼。 于是想能不能让软件自己主动把密码显示出来。 从官网上下载最新...
  • 文件为博主的嵌入式课设文件WIFI控制灯的亮灭,包含PCB工程文件、参考资料,以及源程序代码、元器件清单。亲测可用。
  • uni-appwifi环境下,实现视频文件指定目录下载并重命名

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 46,516
精华内容 18,606
关键字:

wifi传文件app