iBeacon 最先是苹果的技术,使用android-beacon-library包可以在android上开发iBeacon 技术。
iBeacon的发明意义重大。它是一种基于蓝牙低功耗(Bluetooth Low Energy, BLE)技术的定位系统,通过向周围发送信号来标识其位置。这项技术的意义体现在以下几个方面:
-
室内定位与导航: iBeacon可用于在室内环境中提供定位和导航服务。传统的GPS技术在室内环境下往往效果不佳,而iBeacon通过布置在建筑物内部的信标,可以实现更精准的室内定位。
-
增强零售体验: 零售业是iBeacon应用的一个重要领域。商店可以利用iBeacon技术向顾客发送特定的优惠信息、促销活动或产品推荐,从而提升顾客体验并促进销售。
-
实时定位服务: iBeacon可以实现对移动设备的实时追踪,为企业和组织提供更有效的资产管理、人员定位和安全监控等服务。
-
交互式体验: 利用iBeacon技术,可以为用户提供更加个性化和交互式的体验。比如,在博物馆、展览或游乐园等场所,可以通过iBeacon向游客提供相关的解说信息或互动体验。
-
开发者创新: iBeacon开放的开发平台为开发者提供了广阔的创新空间,他们可以基于iBeacon技术开发各种各样的应用,从而为用户带来更丰富的体验和功能。
iBeacon的发明为室内定位和个性化服务领域带来了革命性的变革,极大地丰富了移动应用的功能和体验。
先加这句:implementation group: ‘org.altbeacon’, name: ‘android-beacon-library’, version: ‘2.20.6’
然后sync之后就能使用,xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Android 12以下才需要定位权限, Android 9以下官方建议申请ACCESS_COARSE_LOCATION -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Android 12在不申请定位权限时,必须加上android:usesPermissionFlags="neverForLocation",否则搜不到设备 -->
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="s" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
主java【搜不到我想要的设备,没有android自带的搜设备兼容性好,或许我用错了,但可以搜到设备!】:
package com.example.myapplication;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconConsumer;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.BeaconParser;
import org.altbeacon.beacon.MonitorNotifier;
import org.altbeacon.beacon.RangeNotifier;
import org.altbeacon.beacon.Region;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CODE = 100;
private static final long SCAN_PERIOD = 10000;
private static final String TAG = "TimeAttendantFast";
private BluetoothLeScanner mLEScanner;
private BluetoothManager btManager;
private BluetoothAdapter btAdapter;
private Handler scanHandler;
private Handler mHandler;
private Button checkInBtn;
private TextView tvEmpID;
private boolean isShowDialog;
private ScanSettings settings;
private ArrayList<ScanFilter> filters;
private String employeeID;
private List<String> beaconDeviceList = new ArrayList<>();
private static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
private static final int REQUEST_CHECK_SETTINGS = 14;
private static final String TAGx = "BluetoothSearch";
private BluetoothAdapter bluetoothAdapter;
private BroadcastReceiver bluetoothReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 请求权限
requestPermissions();
settingBlueTooth();
scanLeDevice(true);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// 停止蓝牙设备搜索并注销广播接收器
if (bluetoothAdapter != null) {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
return;
}
bluetoothAdapter.cancelDiscovery();
}
unregisterReceiver(bluetoothReceiver);
}
private void settingBlueTooth() {
// init BLE
btManager = (BluetoothManager) this.getSystemService(Context.BLUETOOTH_SERVICE);
btAdapter = btManager.getAdapter();
if (Build.VERSION.SDK_INT >= 21) {
mLEScanner = btAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
filters = new ArrayList<>();
}
}
/**
* bytesToHex method
*/
static final char[] hexArray = "0123456789ABCDEF".toCharArray();
private static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
private void foundBeacon(String uuid, int major, int minor) {
//LOG
Log.i(TAG, "UUID: " + uuid + "\\nmajor: " + major + "\\nminor" + minor);
}
private void findBeaconPattern(byte[] scanRecord) {
int startByte = 2;
boolean patternFound = false;
while (startByte <= 5) {
if (((int) scanRecord[startByte + 2] & 0xff) == 0x02 && //Identifies an iBeacon
((int) scanRecord[startByte + 3] & 0xff) == 0x15) { //Identifies correct data length
patternFound = true;
break;
}
startByte++;
}
if (patternFound) {
//Convert to hex String
byte[] uuidBytes = new byte[16];
System.arraycopy(scanRecord, startByte + 4, uuidBytes, 0, 16);
String hexString = bytesToHex(uuidBytes);
//UUID detection
String uuid = hexString.substring(0, 8) + "-" +
hexString.substring(8, 12) + "-" +
hexString.substring(12, 16) + "-" +
hexString.substring(16, 20) + "-" +
hexString.substring(20, 32);
// major
final int major = (scanRecord[startByte + 20] & 0xff) * 0x100 + (scanRecord[startByte + 21] & 0xff);
// minor
final int minor = (scanRecord[startByte + 22] & 0xff) * 0x100 + (scanRecord[startByte + 23] & 0xff);
Log.i(TAG, "UUID: " + uuid + "\\nmajor: " + major + "\\nminor" + minor);
foundBeacon(uuid, major, minor);
}
}
private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
findBeaconPattern(scanRecord);
}
};
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i(TAG, "callbackType " + callbackType);
// 获取ScanRecord对象
ScanRecord scanRecord = result.getScanRecord();
String MAC = result.getDevice().getAddress();
// 如果字符是以A4开头的,就是我们的设备
if (MAC.startsWith("A4")) {
Log.i(TAG, "MAC: " + MAC);
}
// 尝试直接从ScanRecord中获取设备名称
if (scanRecord != null) {
String deviceName = scanRecord.getDeviceName();
if (deviceName != null && !deviceName.isEmpty()) {
Log.i(TAG, "Device Name: " + deviceName);
} else {
// 如果getDeviceName返回null,可能需要从scanRecord的bytes中手动解析(虽然不常见)
// 但大多数情况下,直接使用getDeviceName应该足够
byte[] scanRecordBytes = scanRecord.getBytes();
// 这里可以调用您自定义的方法findBeaconPattern来进一步解析数据,如果需要的话
findBeaconPattern(scanRecordBytes);
Log.i(TAG, "Device Name not directly available, parsing scan record...");
}
}
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i(TAG, "ScanResult - Results" + sr.toString());
}
}
@Override
public void onScanFailed(int errorCode) {
Log.e(TAG, "Scan Failed Error Code: " + errorCode);
}
};
private void scanLeDevice(final boolean enable) {
Log.i(TAG, "BLE start scan");
if (Build.VERSION.SDK_INT < 21) {
Log.i(TAG, "start SDK_INT < 21");
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
btAdapter.startLeScan(leScanCallback);
} else {
Log.i(TAG, "start SDK_INT >= 21");
mLEScanner.startScan(filters, settings, mScanCallback);
}
}
private void requestPermissions() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
) {
// 如果没有获取到权限,请求权限
ActivityCompat.requestPermissions(this, new String[]{
android.Manifest.permission.BLUETOOTH,
android.Manifest.permission.BLUETOOTH_ADMIN,
android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
}, PERMISSION_REQUEST_CODE);
}
} else {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_ADVERTISE) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
) {
// 如果没有获取到权限,请求权限
ActivityCompat.requestPermissions(this, new String[]{
android.Manifest.permission.BLUETOOTH_SCAN,
android.Manifest.permission.BLUETOOTH_ADVERTISE,
android.Manifest.permission.BLUETOOTH_CONNECT,
android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
}, PERMISSION_REQUEST_CODE);
}
}
// 如果权限已经被授予,直接进行蓝牙功能检查和定位功能检查
checkBluetoothSupport();
}
private void checkBluetoothSupport() {
// 检查手机蓝牙是否打开
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
Toast.makeText(this, "本机不支持蓝牙功能, 无法蓝牙打卡", Toast.LENGTH_SHORT).show();
finish();
} else if (!bluetoothAdapter.isEnabled()) {
Toast.makeText(this, "蓝牙未打开, 请先打开蓝牙", Toast.LENGTH_SHORT).show();
finish();
}
// 检查定位是否打开
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Toast.makeText(this, "定位未打开, 请先打开定位", Toast.LENGTH_SHORT).show();
finish();
}
// 检查是否联网network
if (!locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
Toast.makeText(this, "网络未打开, 请先打开网络", Toast.LENGTH_SHORT).show();
finish();
}
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "本机不支持蓝牙功能, 无法蓝牙打卡", Toast.LENGTH_SHORT).show();
finish();
} else {
BluetoothManager bm = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bleAdapter = bm.getAdapter();
if (bleAdapter == null || !bleAdapter.isEnabled()) {
Toast.makeText(this, "本机不支持低功耗蓝牙功能, 无法蓝牙打卡", Toast.LENGTH_SHORT).show();
finish();
} else {
Toast.makeText(this, "手机支持蓝牙定位打卡", Toast.LENGTH_SHORT).show();
}
}
}
}
这个代码就完全没反应搜不到了:
import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconConsumer;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.RangeNotifier;
import org.altbeacon.beacon.Region;
public class MainActivity extends AppCompatActivity implements BeaconConsumer {
private BeaconManager beaconManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化 BeaconManager
beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.bind(this);
}
@Override
public void onBeaconServiceConnect() {
// 监听 iBeacon 的信号
beaconManager.addRangeNotifier(new RangeNotifier() {
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
if (beacons.size() > 0) {
for (Beacon beacon : beacons) {
String uuid = beacon.getId1().toString(); // 获取 iBeacon 的 UUID
Log.d("Beacon", "UUID: " + uuid);
}
}
}
});
try {
// 开始监测所有的 iBeacon
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
beaconManager.unbind(this);
}
}