android BLE Peripheral 手机模拟设备发出BLE广播 BluetoothLeAdvertiser

android 从4.3系统开始可以连接BLE设备,这个大家都知道了。iOS是从7.0版本开始支持BLE。android 进入5.0时代时,开放了一个新功能,手机可以模拟设备发出BLE广播, 这个新功能其实是 对标于 iOS系统的手机模拟iBeacon设备。先介绍一下BLE的广播, BLE设备之所以能被手机扫描到,是因为 BLE设备一直在每隔 一段时间广播一次,这个广播里面包含很多数据。手机扫描BLE设备代码如下:public void startScan(){bluetoothAdapter.startLeScan(leScanCallback);}public void stopScan(){bluetoothAdapter.stopLeScan(leScanCallback);}private LeScanCallback leScanCallback=new LeScanCallback() {@Overridepublic void onLeScan(BluetoothDevice bluetoothdeivce, int rssi, byte[] scandata) {//把byte数组转成16进制字符串,方便查看Log.e("TAG","scandata:"+ CYUtils.Bytes2HexString(scandata));}};
ok,这段代码大家在做连接BLE设备进行通讯的时候,已经很熟悉了。其中的 byte数组 scandata就是 BLE设备的广播数据。那么接下来,我们开始使用 手机1 模拟成BLE设备来发送广播,然后用手机2 来进行扫描查看广播数据 scandata首先获取 BluetoothAdapter, 熟悉的代码:BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);bluetoothAdapter = bluetoothManager.getAdapter();进行广播的时候需要用到BluetoothLeAdvertiser,进行实例化:mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
实例化好之后就可以进行广播数据了,开启广播方法是:BluetoothLeAdvertiser: 
public void startAdvertising(AdvertiseSettings settings,AdvertiseData advertiseData, final AdvertiseCallback callback)
其中, AdvertiseSettings 是广播的一些设置,比如,广播间隔,是否可以连接等等; AdvertiseData 就是广播数据了, AdvertiseCallback是广播回调,会告诉你广播成功还是失败。先给一段完整广播代码如下:public void startAction(View v){byte[] broadcastData ={0x34,0x56};mBluetoothLeAdvertiser.startAdvertising(createAdvSettings(true, 0), createAdvertiseData(broadcastData), mAdvertiseCallback);}public void stopAction(View v) {mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);}public AdvertiseSettings createAdvSettings(boolean connectable, int timeoutMillis) {AdvertiseSettings.Builder mSettingsbuilder = new AdvertiseSettings.Builder();mSettingsbuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);mSettingsbuilder.setConnectable(connectable);mSettingsbuilder.setTimeout(timeoutMillis);AdvertiseSettings mAdvertiseSettings = mSettingsbuilder.build();return mAdvertiseSettings;}public AdvertiseData createAdvertiseData(byte[] data) {AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder();mDataBuilder.addManufacturerData(0x01AC, data);AdvertiseData mAdvertiseData = mDataBuilder.build();return mAdvertiseData;}private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {@Overridepublic void onStartSuccess(AdvertiseSettings settingsInEffect) {super.onStartSuccess(settingsInEffect);ToastUtils.showToast(MainActivity.this, "开启广播成功", 2000);}@Overridepublic void onStartFailure(int errorCode) {super.onStartFailure(errorCode);ToastUtils.showToast(MainActivity.this, "开启广播失败 errorCode:" + errorCode, 2000);}};其中,广播数据broadcastData 我暂时直接先定死为2个字节 0x3456,同样在createAdvertiseData里面也有定死的数据 0x01AC . 开启成功之后我们使用手机2 来扫描看下广播的数据是什么:E/TAG: scandata:02011A05FFAC0134560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
log打印出来的scandata 有效数据是 02011A05FFAC013456 .  给大家解释一下这个数据的意思为了看清楚,我分段如下: 02011A  05  FF AC01 3456   (注,这里的都是16进制数字)02011A这3个字节,02表示后面一段数据长度为2字节,01表示数据类型是flag ,1A就是flag的数据了05 表示后面的一段数据长度为 5个字节, FF一个字节,AC01 两个字节,3456两个字节,加起来一共5个字节,老铁没毛病FF,是一个数据类型,这是我们通过代码mDataBuilder.addManufacturerData(0x01AC, data); 添加广播数据时候设置的ManufacturerData 是指设备厂商自定义数据,FF 就是代表下面的数据实体是厂商数据.第一个参数0x01AC,是厂商id,id长度为2个字节,不足2个字节系统会补0,可以看到log打印出来的是 AC01,顺序是倒过来的,这点要注意!如果我代码是这样写的 :mDataBuilder.addManufacturerData(0xAC, data); //只写了一个字节的id
那么使用手机2 扫描出的scandata是:E/TAG: scandata:02011A05FFAC0034560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
可以看到,AC后面系统自动补了00广播数据出除了可以添加ManufacturerData,还可以添加ServerUUID, 代码如下:public AdvertiseData createAdvertiseData(byte[] data) {AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder();mDataBuilder.addManufacturerData(0x01AC, data);mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));AdvertiseData mAdvertiseData = mDataBuilder.build();return mAdvertiseData;}
代码添加了一个 AE8F的 server uuid, 使用手机2 扫描的scandata 如下:E/TAG: scandata:02011A05FFAC01345603038FAE00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
直接看03038FAE 这一段,第一个03 表示后面的一段数据长度为3个字节  第二个03 表示这个数据类型是 server uuid类型,uuid的数据就是8FAE,顺序是倒过来的!有人会问: 如果 我把addServiceUuid代码放在 addManufacturerData 前面,扫描的数据顺序是什么样的呢?答案 还是:E/TAG: scandata:02011A05FFAC01345603038FAE00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
可以添加多个 server uuid吗? 可以,代码如下:public AdvertiseData createAdvertiseData(byte[] data) {AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder();mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));mDataBuilder.addManufacturerData(0x01AC, data);AdvertiseData mAdvertiseData = mDataBuilder.build();return mAdvertiseData;}
扫描的结果是:E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
直接看0503 8FAE E1FF 这一段, 05 表示后面的一段数据长度是5个字节,03表示数据类型是 server uuid, 8FAE是第一个uuid, E1FF是第二个uuid这个ServerUUID 有什么用呢? 不知大家在扫描BLE设备的时候,有没有注意到这个方法:BluetoothAdapter
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback)这个方法也可以用来扫描BLE设备,但是多了一个参数, UUID数组, 这个扫描方法是用来过滤BLE设备用的,比如 你公司开发一个 蓝牙防丢器APP,你使用 startLeScan(callback)这个方法扫描的话,你会发现你扫描到周围的所有的BLE设备,同事戴的小米手环可能也被你扫描到,这样让用户来选择设备进行连接的话可能就比较迷糊,startLeScan(serviceUuids,callback) 这个方法在扫描的时候会过滤广播里的数据,只有符合的BLE设备才会被扫描回调。 所以,你们公司的蓝牙防丢器设备可以在广播字段里加入特定的server uuid, app扫描的时候可以过滤其他设备。我们来实现一下这个功能, 修改 手机2 的扫描代码:public void startScan(){UUID[] serviceUuids = new UUID[] { UUID    .fromString("0000ae8f-0000-1000-8000-00805f9b34fb") };bluetoothAdapter.startLeScan(serviceUuids, leScanCallback);}public void stopScan(){bluetoothAdapter.stopLeScan(leScanCallback);}private LeScanCallback leScanCallback=new LeScanCallback() {@Overridepublic void onLeScan(BluetoothDevice bluetoothdeivce, int rssi, byte[] scandata) {//把byte数组转成16进制字符串,方便查看Log.e("TAG","scandata:"+ CYUtils.Bytes2HexString(scandata));}};
扫描结果是这样的:05-23 16:13:30.522 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:30.625 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:30.735 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:30.847 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:30.955 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:31.061 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:31.192 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:31.283 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:31.369 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
05-23 16:13:31.480 10197-10197/jiqi.blescandemo E/TAG: scandata:02011A05FFAC01345605038FAEE1FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
可以发现,扫描结果里面只会出现拥有  AE8F 这个uuid的 BLE设备,搜索不到其他设备注意:部分手机使用startLeScan(serviceUuids,callback)这个方法过滤设备 会扫描不到设备,即使这个设备UUID符合过滤条件,我归结为手机/系统问题,如三星手机这样,我们知道,广播数据可以添加ManufacturerData,还可以添加ServerUUID, 还有吗? 有,代码如下:public AdvertiseData createAdvertiseData(byte[] data) {AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder();mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceData( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"),new byte[]{0x64,0x12});mDataBuilder.addManufacturerData(0x01AC, data);AdvertiseData mAdvertiseData = mDataBuilder.build();return mAdvertiseData;}扫描结果如下:E/TAG: scandata:02011A05FFAC01345605038FAEE1FF05168FAE64120000000000000000000000000000000000000000000000000000000000000000000000000000000000
直接看05168FAE6412 这一段,05依然表示下面一段数据长度为5个字节,16表示数据类型为 server data, 8FAE表示这个数据的uuid是AE8F, 6412就是数据本体了.那么这个 server data能做什么呢?比如有这样一个 产品:温度计,温度计硬件在广播字段里的server data里面加入它测量的温度,这样APP可以不连接温度计设备 只通过扫描就知道温度了,是不是很方便.以下有几个坑请大家注意一下:情况1:public AdvertiseData createAdvertiseData(byte[] data) {AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder();// mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceData( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"),new byte[]{0x64,0x12});mDataBuilder.addManufacturerData(0x01AC, data);AdvertiseData mAdvertiseData = mDataBuilder.build();return mAdvertiseData;}我注释了一句代码,广播字段里我没有 添加 ae8f这个 uuid,而直接添加了 ae8f的data 为 0x6412,那么扫描结果如何?使用startLeScan(serviceUuids,callback)过滤 ae8f这个uuid,没有扫描结果;使用startLeScan(callback),扫描结果如下:E/TAG: scandata:02011A05FFAC0134560303E1FF05168FAE641200000000000000000000000000000000000000000000000000000000000000000000000000000000000000可以看到是有 ae8f对应的数据 6412,但是server uuid里面是没有 ae8f的.情况2:代码顺序1:mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceData( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"),new byte[]{0x22,0x44});mDataBuilder.addServiceData( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"),new byte[]{0x54,0x12});//扫描结果
E/TAG: scandata:02011A05FFAC01345605038FAEE1FF05168FAE54120000000000000000000000000000000000000000000000000000000000000000000000000000000000代码顺序2:mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceData( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"),new byte[]{0x54,0x11});mDataBuilder.addServiceData( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"),new byte[]{0x22,0x43});//扫描结果
E/TAG: scandata:02011A05FFAC01345605038FAEE1FF05168FAE54110000000000000000000000000000000000000000000000000000000000000000000000000000000000代码顺序3:mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceData( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"),new byte[]{0x54,0x1A});mDataBuilder.addServiceData( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"),new byte[]{0x22,0x47});//扫描结果
E/TAG: scandata:02011A05FFAC0134560503E1FF8FAE05168FAE541A0000000000000000000000000000000000000000000000000000000000000000000000000000000000代码顺序4:mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceUuid( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"));mDataBuilder.addServiceData( ParcelUuid.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"),new byte[]{0x22,0x48});mDataBuilder.addServiceData( ParcelUuid.fromString("0000ae8f-0000-1000-8000-00805f9b34fb"),new byte[]{0x54,0x1B});//扫描结果
E/TAG: scandata:02011A05FFAC0134560503E1FF8FAE05168FAE541B0000000000000000000000000000000000000000000000000000000000000000000000000000000000情况2总结:从上面4个代码顺序的结果来看,总是扫描到 ae8f这个uuid对应的数据,没有第二个 server data,但是为什么每次都是ae8f?我TM也不知道!!AdvertiseData介绍完毕,下面再稍微介绍一下 AdvertiseSettingsAdvertiseSettings.Builder mSettingsbuilder = new AdvertiseSettings.Builder();mSettingsbuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);mSettingsbuilder.setConnectable(connectable);mSettingsbuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);mSettingsbuilder.setTimeout(0);AdvertiseSettings mAdvertiseSettings = mSettingsbuilder.build();setAdvertiseMode(int advertiseMode)设置广播的模式,低功耗,平衡和低延迟三种模式;对应  AdvertiseSettings.ADVERTISE_MODE_LOW_POWER  ,ADVERTISE_MODE_BALANCED ,ADVERTISE_MODE_LOW_LATENCY从左右到右,广播的间隔会越来越短 setConnectable(boolean connectable)设置是否可以连接。广播分为可连接广播和不可连接广播,一般不可连接广播应用在iBeacon设备上,这样APP无法连接上iBeacon设备setTimeout(int timeoutMillis)设置广播的最长时间,最大值为常量AdvertiseSettings.LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;  180秒设为0时,代表无时间限制会一直广播setTxPowerLevel(int txPowerLevel)设置广播的信号强度常量有AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,ADVERTISE_TX_POWER_LOW,ADVERTISE_TX_POWER_MEDIUM,ADVERTISE_TX_POWER_HIGH 从左到右分别表示强度越来越强. 举例:当设置为ADVERTISE_TX_POWER_ULTRA_LOW时,手机1和手机2放在一起,手机2扫描到的rssi信号强度为-56左右,当设置为ADVERTISE_TX_POWER_HIGH  时, 扫描到的信号强度为-33左右,信号强度越大,表示手机和设备靠的越近好了,关于BluetoothLeAdvertiser 的用法介绍完毕!!!!可能有人会说,bluetoothAdapter.startLeScan(leScanCallback); 这个方法过时了怎么办,那可以看一下我的另一篇文章《android BLE 扫描BLE设备 BluetoothLeScanner》源码附件:模拟BLE广播源码:http://pan.baidu.com/s/1bptOQyb

手机2扫描打印的源码就不放出了,很简单。android BLE Peripheral 手机模拟设备发出BLE广播 BluetoothLeAdvertiser

 

转载于:https://www.cnblogs.com/Free-Thinker/p/9292183.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/251295.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端后台管理系统梳理

再梳理一遍 一、商品后台管理系统 1. 功能 1.1 服务端情况 开启了CORS跨域支持需要授权的 API ,必须在请求头中使用 Authorization 字段提供token 令牌(axios拦截器)baseUrl,接口地址:http://localhost:8888/api/…

操作系统 --- 使用套接字进行网络通信

一个套接字就是一个通信标识类型的数据结构,包含了通信目的的地址、通信使用的端口号、通信网络的传输协议、进程所在的网络地址,以及针对客户或服务器程序提供的不同系统调用等,是进程通信和网络通信的基本构件。套接字是为客户/服务器模型而设计的,通常分为以下两类: 1.基于…

构造器执行顺序

转载于:https://www.cnblogs.com/a6948076/p/8045801.html

Java08-java语法基础(七)构造方法

Java08-java语法基础(七)构造方法 一、构造方法 1、什么是构造方法? 构造方法(类方法)是一个方法名和类名相容的特殊的成员方法。 2、构造方法的作用? 当使用new关键字创建一个对象时,为新建对象…

安装mysql8.0.20,报错“找不到VCRUNTIME140_1.dll”

写在最前,指令集合 以管理员身份运行cmd mysql -uroot -p 【进入】mysql mysql > exit 【退出】 net stop mysql 【暂停】 net start mysql 【启动】 mysql -u root -p(命令后输入临时密码,进入mysql) ALTER USER USER() …

操作系统 --- 线程与进程的比较

如果说,在操作系统中引入进程的目的是为了使多个程序能并发执行,以提高资源利用率和系统吞吐量,那么在操作系统中再引入线程,则是为了减少程序在并发执行时所付出的时空开销,使OS(操作系统)具有更好的并发性… 我们再回顾一下进程的两个基本属性: 1.进程是一个可拥拥有资源的独…

24种吸引人的营销文章标题写法,总有一个适合你!

在如今信息爆炸的互联网时代下,如何提高提高文章的阅读(新闻稿、软文宣传稿、微信公众号),成为从业者们共同研究的课题?首先你得有一个足够吸引的标题,尤其是定向推送的时候,这将是由一个质变带来量变的过程。小编虽然不提倡标题…

[js] 处理字符串换行造成的json解析失败

需求:从数据库某个字段取出字符串出来,转为json,结果发现报错为 解析失败,发现是因为取出的字符串换行导致,现在需要将字符串里面的换行替换为,使字符串可依成功解析成json对象。 技术:依靠repl…

使用mockjs模拟数据

一、安装 简单粗暴 npm install mockjs 二、引入 CommonJS引入 let Mock require(mockjs) let userInfo Mock.mock({data: {responseCode: 200,responseMessage: success,userMessage: {name: "cname",email: "email",msg: cparagraph(2)}} })或者ES…

angular --- ngDialog关闭当前层

今天做项目,使用ngDialog 弹出了一层,并且在弹出层上又弹出了第二层. 现在想点击确定按钮关闭第二层. 使用以前的ngDialog.close()会关闭掉全部ngDialog.open方法弹出来的层. 在网上查了一堆,找了好多,最后发现就一句话.$scope.closeThisDialog(); 注意, 要在controller:中传递…

lodash源码分析之compact中的遍历

小时候, 乡愁是一枚小小的邮票, 我在这头, 母亲在那头。 长大后,乡愁是一张窄窄的船票, 我在这头, 新娘在那头。 后来啊, 乡愁是一方矮矮的坟墓, 我在外头, 母亲在里头。…

[HAOI2008]移动玩具

这又是一道神奇的搜索题。。。只要记录每种状态。。。然后暴力判断这种状态往后一步的情况。。。 广搜出最优解即可。。。 呆码&#xff1a; #include<iostream> #include<cstdio> #include<queue> #include<cstring> using namespace std;int dx[5]{0…

javascript --- Sortable一个拖拽的接口

最近项目里面要实现需要实现一个拖拽功能,自己实现很麻烦,就在网上找到了一个封装好的sortable函数,github(https://github.com/SortableJS/Sortable). 首先是依赖的引入:(也可以自己下载源代码导入,方式很多呢) <body><!-- Latest compiled and minified CSS -->…

个人发展战略6天课

第一课 冰山模型 【知道自己为什么会不喜欢一份工作】 衡量和一个岗位是否匹配、匹配程度如何 从上到下的要素 知识技能&#xff08;30%&#xff09;特定领域 - 可以后天学习的&#xff0c;显性的、容易看出来的 和工作的关系&#xff1a; 工作中有陌生的内容&#xff0c;每天…

《Web全栈工程师的自我修养》

1. 什么是全栈工程师 Full-Stack Engineer Facaebook只招全栈工程师&#xff1f; Web开发流程 产品经理-->交互设计-->视觉设计-->开发&#xff08;前端、后端&#xff09;-->测试-->发布 流水线的优势 “各司其职”的弊端 工程师职责不清导致效率低工程师缺乏主…

div设置overflow-scroll滚动之后,jq获取其子元素的offset.top出现问题。

先上个图&#xff1a; 布局很简单&#xff0c;左右超过屏幕的部分自行滚动。 1. html <div class"ce-container"><div class"ce-leftBox">//左边的内容</div><div class"ce-rightBox">//右边的内容 </div…

javascript --- 利用Sortable实现一个可视化公式编辑器

Sortable的依赖引入和简单使用参见:https://blog.csdn.net/piano9425/article/details/90437182 先简单的介绍一下可视化公式编辑器的功能(样式没有调,毕竟每个人的需求都不一样): 首先会有2个需要计算的(我称为操作数)A_XiangRaoZuWenSheng和AYKZQ_CS,以及两个操作符ADD(加法…

使用uni-app搭建微信小程序

0 问题待解决 import { } 与否为什么要封装对齐问题每次重启项目&#xff0c;sitemap就会消失动态修改标题失效图片问题多多 &#xff1a;高度自适应 改成image固定高度&#xff0c;mode&#xff1a;aspectFill微信小程序文档b站视频链接后端接口文档 一、知识点 uni-app 是…

HttpTomcat

本节内容&#xff1a; Http协议Tomcat服务器下面开始的一系列内容都是JavaEE的内容&#xff0c;主要的内容见下图。JavaEE主要是做服务器端开发。 JavaEE全部规范&#xff1a;有13门技术&#xff0c;主要做web开发的需要学习servlet和jsp。其他技术&#xff0c;像EJB、 JNDI、J…

python---线程与进程

一 线程 1.1 概述 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流&#xff0c;一个进程中可以并发多个线程&#xff0c;每条线程并行执行不同的任务。 Threading用于提供线程相关…