Android Bluetooth BLE相關開發資源匯總

Android開啟藍牙開關

轉載自Android:Bluetooth 的打開和關閉

檢查系統藍牙是否開啟

BluetoothManager bluetoothManager = (BluetoothManager) this.
getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
mBluetoothAdapter.isEnabled();

開啟系統藍牙方式:

靜默打開:
  • 注冊權限:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  • 開啟方式:
mBluetoothAdapter.enable();

以上靜默打開 Bluetooth 開關是調用了 BluetoothAdapter.enable() 方法,首先需要獲取 BluetoothAdapter 對象,如果這個對象為 null 的話,說明當前設備不支持 Bluetooth 功能。還有以下幾點需要注意:
1, 在 Nexus 5 Android 4.4.4 原生系統中,在沒有任何其它管理 Bluetooth 權限的應用情況下,調用強制打開 Bluetooth 的方法,沒有任何提示就直接打開 Bluetooth 了。
2,在小米手機 MI 2SC / MIUI-4.7.11 (Android 4.1.1 JRO03L) 上系統自帶的 “安全中心” – “應用權限管理” – “開啟藍牙” 中,有三種設置:
允許:調用強制打開 Bluetooth 代碼,沒有任何提示,Bluetooth 被成功打開。
提示:會彈出提示框,提示安全警告 “ ***應用嘗試開啟藍牙”,可以選擇“拒絕”或“允許”,還有記住此次選擇(備注:如果不記住的話,下次還會彈出同樣的提示框,除非你自己去修改了應用開啟藍牙的權限)。
拒絕:調用強制打開 Bluetooth 代碼,沒有任何提示,Bluetooth 強制打開失敗。
備注:各種手機自帶的權限管理功能或者第三方權限管理應用略有不同。
3,對于 BluetoothAdapter.enable() 這個方法,API 中有以下說明 (備注:初始 API 中,如 Android 2.0 Eclair / API Level 5 中并沒有這段提示)
Bluetooth should never be enabled without direct user consent. If you want to turn on Bluetooth in order to create a wireless connection, you should use the ACTION_REQUEST_ENABLE Intent, which will raise a dialog that requests user permission to turn on Bluetooth. The enable() method is provided only for applications that include a user interface for changing system settings, such as a “power manager” app.
沒有直接的用戶的允許絕不要開啟 Bluetooth。如果你想要打開 Bluetooth 創建一個無線連接,你應當使用 ACTION_REQUEST_ENABLE Intent,這樣會彈出一個提示框提示用戶是否開啟 Bluetooth,enable() 方法僅提供給有 UI 、更改系統設置的應用來使用,例如“電源管理”應用。
從以上官方 API 提示可以看出:不建議你調用此方法來打開 Bluetooth,至少是在沒有任何用戶提醒的前提下!

調用系統彈出框提示用戶打開
  • 注冊權限:
<uses-permission android:name="android.permission.BLUETOOTH" />
  • 開啟方式:
Intent requestBluetoothOn = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
this.startActivityForResult(requestBluetoothOn, REQUEST_CODE_BLUETOOTH_ON);

對于以上彈出系統彈框提示用戶打開 Bluetooth 的代碼,有以下幾點需要注意:
1,這種調用系統的彈出框提示用戶打開 Bluetooth 的方式,一般不會受到系統或者第三方權限管理應用的阻止。只有當你不提示用戶的情況下,可以理解為“偷偷摸摸”的打開 Bluetooth ,這個是被認為侵犯用戶的知情權,系統或者第三方權限管理應用可能加以阻止:直接禁止不提示用戶的情況下打開 Bluetooth,或者提示用戶,又或者是讓用戶自己選擇哪些應用可以強制開啟 Bluetooth。而在 Nexus 5 / Android 4.4.4 原生系統強制打開 Bluetooth 是沒有任何提示,并且可以成功打開。
2,彈出系統的提示框提醒用戶打開 Bluetooth 的主要代碼:
this.startActivityForResult(requestBluetoothOn, REQUEST_CODE_BLUETOOTH_ON);
注意:這個方法是需要 Activity 的對象來調用的!并且需要在 Activity 中重寫 onActivityResult 方法來獲取用戶操作彈出提示框的結果!
3,這種彈出的系統彈框,根據系統的不同,UI 會有所不同。會導致用戶app視覺不統一。

跳轉到系統設置中讓用戶自己打開:
startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));

考慮到涉及用戶隱私和用戶體驗,推薦以下方式開啟 Bluetooth :
1,采用強制開啟 Bluetooth 的方式打開 Bluetooth ,但是調用強制開啟 Bluetooth 代碼之前,我們自己在應用中提示用戶,我們的應用需要開啟 Bluetooth ,讓用戶自己選擇是否開啟 Bluetooth 。自己在應用中提示用戶我們需要開啟 Bluetooth 相對于彈出系統的提示框提示用戶當前應用需要開啟 Bluetooth 的優勢在于我們可以控制提示的內容和提示的方式以及 UI。
2,假若用戶選擇了開啟 Bluetooth,但是強制開啟 Bluetooth 失敗,比如系統自帶的權限管理禁止你的應用開啟 Bluetooth ,我們不去提示用戶說當前系統禁止了應用開啟 Bluetooth,讓用戶自己去解除禁止。這樣顯然用戶體驗很差。這種情況下,我們再去調用彈出系統提示框提醒用戶打開 Bluetooth 即可。這種方式一般系統或者第三方應用不會禁止。
3,如果彈出系統提示框提醒用戶打開 Bluetooth 有問題的話,最后采用提示用戶自己去系統 Bluetooth 設置中打開 Bluetooth,跳轉到系統的 Bluetooth 設置界面。

PS:在目前Android手機中,是不支持在飛行模式下開啟藍牙的。如果藍牙已經開啟,那么藍牙的開關狀態會隨著飛行模式的狀態而發生改變。

BLE廣播數據解析

轉載自BLE 廣播數據解析

BLE中peripheral設備處于被發現狀態時會發送廣播包,peripheral設備通過廣播被中心設備發現,廣播中帶有peripheral設備自身的相關信息。

廣播包有兩種: 廣播包 (Advertising Data)和 響應包 (Scan Response),其中廣播包是每個設備必須廣播的,而響應包是可選的。 數據包的格式如下圖所示(圖片來自官方 Spec):
在這里插入圖片描述
每個包都是 31 字節,數據包中分為有效數據(significant)和無效數據(non-significant)兩部分。

  • 有效數據部分 :包含若干個廣播數據單元,稱為 AD Structure 。如圖中所示,AD Structure 的組成是:第一個字節是長度值 Len ,表示接下來的 Len 個字節是數據部分。數據部分的第一個字節表示數據的類型 AD Type ,剩下的 Len - 1 個字節是真正的數據 AD data 。其中 AD type 非常關鍵,決定了 AD Data 的數據代表的是什么和怎么解析,這個在后面會詳細講;
  • 無效數據部分 :因為廣播包的長度必須是 31 個 byte,如果有效數據部分不到 31 自己,剩下的就用 0 補全。這部分的數據是無效的,解釋的時候,忽略即可。

AD type

所有的 AD type 的定義在文檔 Core Specification Supplement 中。 AD Type 包括如下類型:

  • Flags: TYPE = 0x01。這個數據用來標識設備 LE 物理連接的功能。DATA 是 0 到多個字節的 Flag 值,每個 bit 上用 0 或者 1 來表示是否為 True。如果有任何一個 bit 不為 0,并且廣播包是可連接的,就必須包含此數據。各 bit 的定義如下:

    • bit 0: LE 有限發現模式
    • bit 1: LE 普通發現模式
    • bit 2: 不支持 BR/EDR
    • bit 3: 對 Same Device Capable(Controller) 同時支持 BLE 和 BR/EDR
    • bit 4: 對 Same Device Capable(Host) 同時支持 BLE 和 BR/EDR
    • bit 5…7: 預留
  • Service UUID: 廣播數據中一般都會把設備支持的 GATT Service 廣播出來,用來告訴外面本設備所支持的 Service。有三種類型的 UUID:16 bit, 32bit, 128 bit。廣播中,每種類型類型有有兩個類別:完整和非完整的。這樣就共有 6 種 AD Type。

    • 非完整的 16 bit UUID 列表: TYPE = 0x02;
    • 完整的 16 bit UUID 列表: TYPE = 0x03;
    • 非完整的 32 bit UUID 列表: TYPE = 0x04;
    • 完整的 32 bit UUID 列表: TYPE = 0x05;
    • 非完整的 128 bit UUID 列表: TYPE = 0x06;
    • 完整的 128 bit UUID 列表: TYPE = 0x07;
  • Local Name: 設備名字,DATA 是名字的字符串。 Local Name 可以是設備的全名,也可以是設備名字的縮寫,其中縮寫必須是全名的前面的若干字符。

    • 設備全名: TYPE = 0x08
    • 設備簡稱: TYPE = 0x09
  • TX Power Level: TYPE = 0x0A,表示設備發送廣播包的信號強度。DATA 部分是一個字節,表示 -127 到 + 127 dBm。

  • 帶外安全管理(Security Manager Out of Band):TYPE = 0x11。DATA 也是 Flag,每個 bit 表示一個功能:

    • bit 0: OOB Flag,0 表示沒有 OOB 數據,1 表示有
    • bit 1: 支持 LE
    • bit 2: 對 Same Device Capable(Host) 同時支持 BLE 和 BR/EDR
    • bit 3: 地址類型,0 表示公開地址,1 表示隨機地址
  • 外設(Slave)連接間隔范圍:TYPE = 0x12。數據中定義了 Slave 最大和最小連接間隔,數據包含 4 個字節:

    • 前 2 字節:定義最小連接間隔,取值范圍:0x0006 ~ 0x0C80,而 0xFFFF 表示未定義;
    • 后 2 字節:定義最大連接間隔,同上,不過需要保證最大連接間隔大于或者等于最小連接間隔。
  • 服務搜尋:外圍設備可以要請中心設備提供相應的 Service。其數據定義和前面的 Service UUID 類似:

    • 16 bit UUID 列表: TYPE = 0x14
    • 32 bit UUID 列表: TYPE = 0x??
    • 128 bit UUID 列表: TYPE = 0x15
  • Service Data: Service 對應的數據。

    • 16 bit UUID Service: TYPE = 0x16, 前 2 字節是 UUID,后面是 Service 的數據;
    • 32 bit UUID Service: TYPE = 0x??, 前 4 字節是 UUID,后面是 Service 的數據;
    • 128 bit UUID Service: TYPE = 0x??, 前 16 字節是 UUID,后面是 Service 的數據;
  • 公開目標地址:TYPE = 0x17,表示希望這個廣播包被指定的目標設備處理,此設備綁定了公開地址,DATA 是目標地址列表,每個地址 6 字節。

  • 隨機目標地址:TYPE = 0x18,定義和前一個類似,表示希望這個廣播包被指定的目標設備處理,此設備綁定了隨機地址,DATA 是目標地址列表,每個地址 6 字節。

  • Appearance:TYPE = 0x19,DATA 是表示了設備的外觀。

  • 廠商自定義數據: TYPE = 0xFF,廠商自定義的數據中,前兩個字節表示廠商 ID,剩下的是廠商自己按照需求添加,里面的數據內容自己定義。

還有一些其他的數據,我這里就不一一列舉了,有需要的可以從這個文檔查閱 Core Specification Supplement 。

GATT

BLE技術是基于GATT進行通信的,GATT是一種屬性傳輸協議,簡單的講可以認為是一種屬性傳輸的應用層協議。它的結構非常簡單:
在這里插入圖片描述
你可以把他看成xml來理解:

  • 每個GATT由完成不同功能的Service組成;
  • 每個Service由不同的Characteristic組成;
  • 每個Characteristic由一個value和一個或者多個Descriptor組成;
    Service、Characteristic相當于標簽(Service相當于他的類別,Characteristic相當于它的名字),而value才真正的包含數據,Descriptor是對這個value進行的說明和描述,當然我們可以從不同角度來描述和說明,因此可以有多個Descriptor.

這樣子理解可能不夠準確,下面我們來舉一個簡單的例子進行說明:

常見的小米手環是一個BLE設備,(假設)它包含三個Service,分別是提供設備信息的Service、提供步數的Service、檢測心率的Service;
而設備信息的service中包含的characteristic包括廠商信息、硬件信息、版本信息等;而心率Service則包括心率characteristic等,而心率characteristic中的value則真正的包含心率的數據,而descriptor則是對該value的描述說明,比如value的單位啊,描述啊,權限啊等。

作者:小時不識月z
鏈接:https://www.jianshu.com/p/29a730795294
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。

BluetoothAdapter類介紹

摘錄自Android中藍牙的基本使用----BluetoothAdapter類簡介

由于網絡上關于BluetoothAdapter的一些常用API函數都有了介紹,因此,我著重介紹一些BluetoothAdapter類疏忽的地方。

1,BluetoothAdapter STATE 狀態值 , 即開關狀態

int STATE_OFF                 藍牙已經關閉
int STATE_ON                  藍牙已經打開
int STATE_TURNING_OFF         藍牙處于關閉過程中 ,關閉ing
int STATE_TURNING_O           藍牙處于打開過程中 ,打開ing

2,BluetoothAdapter SCAN_MOD狀態值 ,即掃描狀態

首先說明,可以掃描其他設備的,當然它同時能被其他藍牙設備掃碼。

int  SCAN_MODE_CONNECTABLE               表明該藍牙可以掃描其他藍牙設備
int  SCAN_MODE_CONNECTABLE_DISCOVERABLE  表明該藍牙設備同時可以掃碼其他藍牙設備,并且可以被其他藍牙設備掃描到。
int  SCAN_MODE_NONE                      該藍牙不能掃描以及被掃描。

3,獲得藍牙適配器實例

public static synchronized BluetoothAdapter getDefaultAdapter ()
功能:獲得本設備的藍牙適配器實例。
返回值:如果設備具備藍牙功能,返回BluetoothAdapter 實例;否則,返回null對象。

4,掃描藍牙設備

public boolean startDiscovery () 
功能: 掃描藍牙設備
注意: 如果藍牙沒有開啟,該方法會返回false,即不會開始掃描過程。public  boolean cancelDiscovery ()
功能: 取消掃描過程。
注意: 如果藍牙沒有開啟,該方法會返回false。public boolean isDiscovering ()
功能: 是否正在處于掃描過程中。
注意: 如果藍牙沒有開啟,該方法會返回false。

5,獲取藍牙相關信息

public String getName ()
功能:獲取藍牙設備Namepublic String getAddress ()
功能:獲取藍牙設備的硬件地址(MAC地址),例如:00:11:22:AA:BB:CC  public boolean setName (String name)
功能:設置藍牙設備的Name,public Set<BluetoothDevice> getBondedDevices ()
功能:獲取與本機藍牙所有綁定的遠程藍牙信息,以BluetoothDevice類實例(稍后講到)返回。
注意:如果藍牙為開啟,該函數會返回一個空集合 。public static boolean checkBluetoothAddress (String address)
功能: 驗證藍牙設備MAC地址是否有效。所有設備地址的英文字母必須大寫,48位,形如:00:43:A8:23:10:F1 。
返回值: true 設備地址有效
false 設備地址無效public BluetoothDevice getRemoteDevice (String address)
功能:以給定的MAC地址去創建一個 BluetoothDevice 類實例(代表遠程藍牙實例)。即使該藍牙地址不可見,也會產生一個BluetoothDevice 類實例。
返回:BluetoothDevice 類實例 。注意,如果該藍牙設備MAC地址不能被識別,其藍牙Name為null。
異常:如果MAC  address無效,拋出IllegalArgumentException。

Android Bluetooth Low Energy (BLE) Example

轉載自Android Bluetooth Low Energy (BLE) Example

Since BLE was introduced in API 18 and cannot be used on old devices, due to change of Bluetooth specifications. I suggest you to specify the min SDK version to 18 in your app. Next add these permissions and feature tags in the manifest tag of your app manifest:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

Since I will be printing all the data in logs, there is no need for a layout file, lets have a look at the code for using Bluetooth low energy on Android:

package com.truiton.bleexample;import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.widget.Toast;import java.util.ArrayList;
import java.util.List;@TargetApi(21)
public class MainActivity extends ActionBarActivity {private BluetoothAdapter mBluetoothAdapter;private int REQUEST_ENABLE_BT = 1;private Handler mHandler;private static final long SCAN_PERIOD = 10000;private BluetoothLeScanner mLEScanner;private ScanSettings settings;private List<ScanFilter> filters;private BluetoothGatt mGatt;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mHandler = new Handler();if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {Toast.makeText(this, "BLE Not Supported",Toast.LENGTH_SHORT).show();finish();}final BluetoothManager bluetoothManager =(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);mBluetoothAdapter = bluetoothManager.getAdapter();}@Overrideprotected void onResume() {super.onResume();if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);} else {if (Build.VERSION.SDK_INT >= 21) {mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();filters = new ArrayList<ScanFilter>();}scanLeDevice(true);}}@Overrideprotected void onPause() {super.onPause();if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {scanLeDevice(false);}}@Overrideprotected void onDestroy() {if (mGatt == null) {return;}mGatt.close();mGatt = null;super.onDestroy();}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == REQUEST_ENABLE_BT) {if (resultCode == Activity.RESULT_CANCELED) {//Bluetooth not enabled.finish();return;}}super.onActivityResult(requestCode, resultCode, data);}private void scanLeDevice(final boolean enable) {if (enable) {mHandler.postDelayed(new Runnable() {@Overridepublic void run() {if (Build.VERSION.SDK_INT < 21) {mBluetoothAdapter.stopLeScan(mLeScanCallback);} else {mLEScanner.stopScan(mScanCallback);}}}, SCAN_PERIOD);if (Build.VERSION.SDK_INT < 21) {mBluetoothAdapter.startLeScan(mLeScanCallback);} else {mLEScanner.startScan(filters, settings, mScanCallback);}} else {if (Build.VERSION.SDK_INT < 21) {mBluetoothAdapter.stopLeScan(mLeScanCallback);} else {mLEScanner.stopScan(mScanCallback);}}}private ScanCallback mScanCallback = new ScanCallback() {@Overridepublic void onScanResult(int callbackType, ScanResult result) {Log.i("callbackType", String.valueOf(callbackType));Log.i("result", result.toString());BluetoothDevice btDevice = result.getDevice();connectToDevice(btDevice);}@Overridepublic void onBatchScanResults(List<ScanResult> results) {for (ScanResult sr : results) {Log.i("ScanResult - Results", sr.toString());}}@Overridepublic void onScanFailed(int errorCode) {Log.e("Scan Failed", "Error Code: " + errorCode);}};private BluetoothAdapter.LeScanCallback mLeScanCallback =new BluetoothAdapter.LeScanCallback() {@Overridepublic void onLeScan(final BluetoothDevice device, int rssi,byte[] scanRecord) {runOnUiThread(new Runnable() {@Overridepublic void run() {Log.i("onLeScan", device.toString());connectToDevice(device);}});}};public void connectToDevice(BluetoothDevice device) {if (mGatt == null) {mGatt = device.connectGatt(this, false, gattCallback);scanLeDevice(false);// will stop after first device detection}}private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {Log.i("onConnectionStateChange", "Status: " + status);switch (newState) {case BluetoothProfile.STATE_CONNECTED:Log.i("gattCallback", "STATE_CONNECTED");gatt.discoverServices();break;case BluetoothProfile.STATE_DISCONNECTED:Log.e("gattCallback", "STATE_DISCONNECTED");break;default:Log.e("gattCallback", "STATE_OTHER");}}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {List<BluetoothGattService> services = gatt.getServices();Log.i("onServicesDiscovered", services.toString());gatt.readCharacteristic(services.get(1).getCharacteristics().get(0));}@Overridepublic void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristiccharacteristic, int status) {Log.i("onCharacteristicRead", characteristic.toString());gatt.disconnect();}};
}
disconnect() 和close()的區別

在進行BLE開發過程中可能會遇到操作失敗等情況,這個時候可能需要斷開與BLE的連接或者清理相關資源.在BluetoothGatt類中有兩個相關的方法 。

  • disconnect()
  • close()

那么這個兩個方法有什么區別,又該如何使用呢。
disconnect()方法:如果調用了該方法之后可以調用connect()方法進行重連,這樣還可以繼續進行斷開前的操作。

close()方法:一但調用了該方法, 如果你想再次連接,必須調用BluetoothDevice的connectGatt()方法。 因為close()方法將釋放BluetootheGatt的所有資源。

需要注意的問題:
當你需要手動斷開時,調用disconnect()方法,此時斷開成功后會回調onConnectionStateChange方法,在這個方法中再調用close方法釋放資源。
如果在disconnect后立即調用close,會導致無法回調onConnectionStateChange方法。

常見問題:

BLE連接之后onServicesDiscovered不被調用

摘錄自ble連接之后onServicesDiscovered 不被調用
問題:onServicesDiscovered never called while connecting to GATT Server

Something that has been really useful for me is to wait for about 600ms after the connection has been established and then start the service discovery.

項目中出現藍牙連接上之后,始終不進onServicesDiscovered回調,mBluetoothGatt.discoverServices()做如下延時即可

if (newState == BluetoothProfile.STATE_CONNECTED) {intentAction = ACTION_GATT_CONNECTED;broadcastUpdate(intentAction);Log.i(TAG, "Connected to GATT server.");// Attempts to discover services after successful connection.//有時候發現服務不回調,需延時 https://stackoverflow.com/questions/41434555/onservicesdiscovered-never-called-while-connecting-to-gatt-server#comment70285228_41526267try {Thread.sleep(600);Log.i(TAG, "Attempting to start service discovery:"+ mBluetoothGatt.discoverServices());} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}
}

相關資源:

Android低功耗藍牙的那點事兒

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/449965.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/449965.shtml
英文地址,請注明出處:http://en.pswp.cn/news/449965.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

__invoke,try{}catch(){},microtime(),is_callable()

<?php /*1.對象本身不能直接當函數用&#xff0c;如果被當做函數用&#xff0c;會直接回調__invoke方法* 2.驗證變量的內容能否作為函數調用* 3.try{}catch(Exception $e){}catch(){}finally{}* 4.microtime()函數返回當前時間戳和微妙數* */ class httpException extends …

H.264中的I_PCM模式

H.264中的I_PCM模式 I_PCM是一種幀內編碼模式&#xff0c;在該模式下&#xff0c;編碼器直接傳輸圖像的像素值&#xff0c;而不經過預測和變換。在一些特殊的情況下&#xff0c;特別是圖像內容不規則或者量化參數非常低時&#xff0c;該模式比常規的操作&#xff08;幀內預測…

RxPermissions 源碼解析之舉一反三

[toc] RxPermissions 源碼解析 簡介 RxPermissions 是基于 RxJava 開發的用于幫助 在Android 6.0 中處理運行時權限檢測的框架。在 Android 6.0 中增加了對危險權限的動態申請&#xff0c;而不是像 Android 6.0 之前的默認全部獲取的方式。 原始動態權限的獲取 如果按照以往的獲…

總結Selenium WebDriver中一些鼠標和鍵盤事件的使用

在使用 Selenium WebDriver 做自動化測試的時候&#xff0c;會經常模擬鼠標和鍵盤的一些行為。比如使用鼠標單擊、雙擊、右擊、拖拽等動作&#xff1b;或者鍵盤輸入、快捷鍵使用、組合鍵使用等模擬鍵盤的操作。在 WebDeriver 中&#xff0c;有一個專門的類來負責實現這些測試場…

最快浮點數取絕對值

做視頻算法10多年&#xff0c;經常要算絕對值&#xff0c;整數的絕對值有快速算法&#xff0c;但浮點數的絕對值沒看到有快速算法&#xff0c;經常不段發現&#xff0c;得到如下浮點數的快速算法&#xff1a; 快6倍多&#xff0c; #include <Windows.h> #include <ios…

Linux ln命令、軟鏈接和硬鏈接的區別

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 Linux ln命令是一個非常重要命令&#xff0c;它的功能是為某一個文件在另外一個位置建立一個同步的鏈接。 當我們需要在不同的目錄&…

Android應用開發——文件目錄

Android 存儲位置及 API 一、內部存儲 應用安裝后都會在Android 根目錄生成 /data/data/packagename&#xff0c;當前應用讀取不需要讀寫權限 注意&#xff1a; 有些開發者可能看到過應用的根目錄為 /data/user/0/packagename 的情況&#xff0c;這里解釋一下&#xff0c;And…

git常用命令及沖突解決

2019獨角獸企業重金招聘Python工程師標準>>> git常用命令 git config --global user.name chenhongjiang git config --global user.email 123qq.com git init 建立廠庫 git status 查看狀態 git add . 添加當前目錄 git add a.php 添加文件…

C 語言常見問題集

從 http://c-faq-chn.sourceforge.net/ccfaq/index.html 轉載過來&#xff0c;學習C的好助手。 目錄1. 前言2. 聲明和初始化 2.1 我如何決定使用那種整數類型&#xff1f;2.2 64 位機上的 64 位類型是什么樣的&#xff1f;2.3 怎樣定義和聲明全局變量和函數最好&#xff1f;2.4…

【題解】quake

【題解】\(quake\) 題目大意 我們共有報酬\(f\)元&#xff0c;一條邊有它的價值\(w_i\),有它的建造時間\(t_i\)。要求建一些邊&#xff0c;生成一顆樹。求最大的利潤率。 數據范圍 \(n\le 400\) \(m\le10000\) \(Solution\) 實際上\(n,m\)出到\(\le 100000\)應該也是沒問題的。…

Android應用開發——service連接泄露異常:android.app.ServiceConnectionLeaked: that was originally bound here

在做service開發過程中&#xff0c;大部分可能會遇到以下異常&#xff0c;該異常僅通過log輸出&#xff0c;并不會導致app crash。 E/ActivityThread: Activity com.example.image.all_samples.Main2Activity has leaked ServiceConnection com.example.image.all_samples.Mai…

Linux more命令、Linux rhmask命令

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 Linux more 命令類似 cat &#xff0c;不過會以一頁一頁的形式顯示&#xff0c;更方便使用者逐頁閱讀&#xff0c;而最基本的指令就是按…

從零開始學習PYTHON3講義(二)把Python當做計算器

《從零開始PYTHON3》第二講 上一講我們說過了如何啟動Python IDLE集成開發學習環境&#xff0c;macOS/Linux都可以在命令行執行idle3。Windows則從開始菜單中去尋找IDLE程序的圖標。 上一講我們還見到了Python的兩種工作模式&#xff0c;交互模式和程序模式。 通常在一個大型的…

Tranquility

本頁目錄與Kafka集群交互Druid使用Tranquility Kafka本文以Kafka為例&#xff0c;介紹在E-MapReduce中如何使用Tranquility從Kafka集群采集數據&#xff0c;并實時推送至Druid集群。 Tranquility是一個以push方式向Druid實時發送數據的應用。它替用戶解決了分區、多副本、服務發…

Iot相關雜燴

人工智能就像人的大腦&#xff0c;而 IoT 就像人的神經網絡 1&#xff09;在天空中巨大的鳥群里&#xff0c;每一只鳥兒都實時判斷自己和四周同伴的距離。這時&#xff0c;它們各自都是一個物聯網節點。2&#xff09;這些“節點”并不是簡單地收集數據&#xff0c;而是在實時計…

水滴石穿C語言之指針、數組和函數

基本解釋   1、指針的本質是一個與地址相關的復合類型&#xff0c;它的值是數據存放的位置&#xff08;地址&#xff09;&#xff1b;數組的本質則是一系列的變量。   2、數組名對應著&#xff08;而不是指向&#xff09;一塊內存&#xff0c;其地址與容量在生命期內保持…

告訴你銀行在年底為存儲做的小動作

25年前&#xff0c;銀行的存款利率是10.98%&#xff0c;可謂巔峰時刻。15年前&#xff0c;銀行的存款利率開始下降&#xff0c;降到了8%的利率。 到了5年前&#xff0c;銀行的存款利率毫無回轉之勢&#xff0c;直線下降到了5%的利率。 而如今&#xff0c;我們無可奈何地接受了2…

爬蟲學習(五)——百度貼吧的爬取

import osimport timeimport urllib.requestimport urllib.parse# 輸入目標頁碼和吧名def header(): url "https://tieba.baidu.com/f?" baming input("請輸入要爬取的吧名") start_page int(input("請輸入起始頁")) end_page …

什么是嵌入式設備?/ 嵌入式設備的定義

什么是嵌入式設備&#xff1f;/ 嵌入式設備的定義 區別于通用計算機的其他設備都可以稱之為嵌入式設備 &#xff08;個人電腦&#xff0c;服務器&#xff09; 一段時期內&#xff0c;必備的硬件配置。 嵌入式開發包括哪些部分&#xff1a; 底層驅動開發&#xff1a; 關鍵字…

Linux mv命令、Linux cp命令、Linux scp命令

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 Linux mv命令用來為文件或目錄改名、或將文件或目錄移入其它位置。 語法 mv [options] source dest mv [options] source... director…