Android:BLE智能硬件开发详解

简介:

目录

  • 前言
  • BLE是个什么鬼
  • BLE中的角色分工
  • 主要的关键词和概念 
    • GATT(Generic Attribute Profile )
    • Characteristic
    • Service
  • Android如何使用BLE 
    • 蓝牙权限
    • APP和BLE外设交互流程
  • 后记 

本文作者MichaelX,博客地址:http://blog.csdn.net/xiong_it 转载请注明来源


前言

前些年,智能硬件炒的挺火的,那今天,咱就来说说智能硬件那些事。BLE是智能硬件的一种通讯方式,通过BLE连接,iOS & Android手机和智能硬件就可以进行自定义的交互了。交互的体验如何,很大程度上取决于智能硬件的驱动工程师驱动写的好不好,以及App的代码质量如何。

笔者曾参与过多款BLE智能硬件的开发,许久不用,怕忘了,把自己的整理的一些知识记录与此,同时也希望能够给一些同学带来帮助。本文将尽力向读者讲清楚BLE是什么,以及在实际Android开发中该如何使用BLE。

前方高能:文章有点长,笔者经历了好几次改版,也花费了好几个月的业余时间,读者可能需要点耐心。着急的读者可直接跳转至Android如何使用BLE

BLE是个什么鬼

BLE:Bluetooth Low Energy,低功耗蓝牙。Android官方介绍如下:

Android 4.3 (API Level 18) introduces built-in platform support for Bluetooth Low Energy in the central role and provides APIs that apps can use to discover devices, query for services, and read/write characteristics. In contrast to Classic Bluetooth, Bluetooth Low Energy (BLE) is designed to provide significantly lower power consumption. This allows Android apps to communicate with BLE devices that have low power requirements, such as proximity sensors, heart rate monitors, fitness devices, and so on.

什么意思呢?自从API18/Android4.3开始,Android开始支持低功耗蓝牙并给APP提供了一套api调用。相比传统蓝牙来说,BLE技术旨在降低蓝牙功耗。至于我们Android开发者来说,要做的就是调用这套api,和具备蓝牙的智能硬件沟通,通过蓝牙读写操控智能硬件。

BLE技术允许APP和那些有着低功耗需求的BLE设备进行通讯,这些设备包括但不限于:距离传感器设备,心跳率检测仪,健身器材等。

约定:文中提到的”外设”,”BLE外设”和”智能硬件”是等价的.请读者知悉.


角色分工

Once the phone and the activity tracker have established a connection, they start transferring GATT metadata to one another. Depending on the kind of data they transfer, one or the other might act as the server. For example, if the activity tracker wants to report sensor data to the phone, it might make sense for the activity tracker to act as the server. If the activity tracker wants to receive updates from the phone, then it might make sense for the phone to act as the server.

在Android APP和BLE外设进行交互时,他们分别扮演两个角色.这两个角色是不固定的. 
GATT server:发送数据的一方. 
GATT client:接收数据的一方. 
当APP向外设写入数据时,APP就是server,外设就是client;当APP读取外设数据时,APP就是client.外设就是server.


主要的关键词和概念

GATT(Generic Attribute Profile )

The GATT profile is a general specification for sending and receiving short pieces of data known as “attributes” over a BLE link. All current Low Energy APPlication profiles are based on GATT.

这个是BLE通讯的基本协议,这个协议定义了BLE发送和接收一小段数据的规范,这些被传输的小段数据被称为”attributes”.

Characteristic

A characteristic contains a single value and 0-n descriptors that describe the characteristic’s value. A characteristic can be thought of as a type, analogous to a class.

博主的理解中,”Characteristic”是BLE通讯之间的沟通”搬运工”,因为这是我们从智能硬件直接读写的东西,它依附于下文的Service存在,有自己的标志码:uuid。它『分为读取BLE外设数据的Characteristic & 向BLE外设写入数据的Characteristic』。 
下面章节中将用代码说话.

Service

A service is a collection of characteristics. For example, you could have a service called “Heart Rate Monitor” that includes characteristics such as “heart rate measurement.”

此Service非彼Android四大组件中的彼Service,而是BluetoothGattService.这个Service是一个characteristics的集合,它可以理解为针对某个信号的通讯线路。


Android如何使用BLE

蓝牙权限

使用BLE需要两个权限

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
  • 1
  • 2
  • 1
  • 2

如果你想要APP只适配具备BLE的手机,那个可以再添加一个硬件权限特性

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
  • 1
  • 1

APP和BLE外设交互流程

APP和BLE外设交互的一个大概流程就是:

  1. BLE外设打开电源
  2. APP初始化蓝牙
  3. APP扫描周边BLE外设
  4. APP连接到周边BLE外设
  5. APP读写BLE外设
  6. 交互完成,APP向BLE外设写入关机/待机指令(可选)
  7. BLE外设关机
  8. APP关闭本地蓝牙连接

以下将逐步利用代码进行讲解APP和BLE外设交互.

初始化BLE

Java代码判断当前手机是否支持BLE低功耗蓝牙

// 判断手机是否支持BLE
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();// 如果手机不支持BLE就关闭程序,仅供参考
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

初始化蓝牙管理者和适配器,这2个对象是ble通讯的基石.

// 初始化蓝牙管理者和适配器,这2个对象是ble通讯的基石.
private BluetoothAdapter mBluetoothAdapter;
...
final BluetoothManager bluetoothManager =
        (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

跳转到系统蓝牙设置界面

private BluetoothAdapter mBluetoothAdapter;
...
// 验证蓝牙是否已打开,如果没打开就提示用户跳转打开.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

 

APP扫描周边BLE外设

需要实现一个BluetoothAdapter.LeScanCallback回调接口,得到扫描结果。该接口只有一个回调方法:

/**
 * @param device 被手机蓝牙扫描到的BLE外设实体对象
 * @param rssi 大概就是表示BLE外设的信号强度,如果为0,则表示BLE外设不可连接。
 * @param scanRecord 被扫描到的BLE外围设备提供的扫描记录,一般没什么用
 */
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

由于扫描BLE设备比较消耗资源,官方推荐间歇性扫描,示例代码如下

    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler;

    // 每扫描10s休息一下 private static final long SCAN_PERIOD = 10000; private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { // TODO 这里可以进行连接操作,连接操作见下一小节 if (device != null && device.getName() != null && device.getName().contain("你的产品名称")){ // 连接设备 connectDevice(device); // 停止扫描 scanLeDevice(false); } } }); } }; ... /** * @param enable 是否进行扫描,false则停止扫描 */ private void scanLeDevice(final boolean enable) { if (enable) { // 利用Handler进行间歇性扫描,每次扫描时间:10s mHandler.postDelayed(new Runnable() { @Override public void run() { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } }, SCAN_PERIOD); mScanning = true; mBluetoothAdapter.startLeScan(mLeScanCallback); } else { // 停止扫描 mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } ... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

 

APP连接周边BLE外设

连接操作是进行手机和BLE外设交互的基础,请看下面connectDevice(BluetoothDevice)方法实现。

分两步走: 
1. 判断该设备是否连接过,连接过则首先尝试直接连接:BluetoothGatt.connect() 
2. 首次连接或者直连失败使用:BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)

public boolean connectDevice(final BluetoothDevice device) {
        if (mBluetoothAdapter == null || device == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return false; } String address = device.getAddress(); // 之前连接过的设备,尝试直接连接。mBluetoothDeviceAddress表示刚才连接过的设备地址 if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) { Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection."); if (mBluetoothGatt.connect()) {// 连接成功 // 修改连接状态变量 mConnectionState = STATE_CONNECTING; return true; } else { return false; } } final BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(address); if (remoteDevice == null) { Log.w(TAG, "Device not found. Unable to connect."); return false; } mBluetoothGatt = remoteDevice.connectGatt(context, false, mGattCallback); Log.d(TAG, "Trying to create a new connection."); // 将当前连接上的设备地址赋值给连接过的设备地址变量 mBluetoothDeviceAddress = address; // 改变连接状态变量 mConnectionState = STATE_CONNECTING; return true; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

连接BEL外设时,需要一个实现回调接口以得到连接状态,BluetoothGattCallback大概实现如下:

private final BluetoothGattCallback mGattCallback =
            new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { // 该方法在连接状态改变时回调,newState即代表当前连接状态 String intentAction; // 连接上了 if (newState == BluetoothProfile.STATE_CONNECTED) { intentAction = ACTION_GATT_CONNECTED; // 改变蓝牙连接状态变量 mConnectionState = STATE_CONNECTED; // 发送自定义广播:连接上了 broadcastUpdate(intentAction); // 当前外设相当于前面章节提到的Server角色:提供数据被手机读取 Log.i(TAG, "Connected to GATT server."); // 获取读/写服务:Service。该方法会触发下面的onServicesDiscovered()回调 mBluetoothGatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {// 断开连接了 intentAction = ACTION_GATT_DISCONNECTED; mConnectionState = STATE_DISCONNECTED; Log.i(TAG, "Disconnected from GATT server."); // 发送自定义广播:断开了连接 broadcastUpdate(intentAction); } } @Override // 该方法在蓝牙服务被发现时回调。由上述的mBluetoothGatt.discoverServices()触发结果。 public void onServicesDiscovered(BluetoothGatt gatt, int status) { // 发现服务。status表示发现服务的结果码 if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); // TODO 从发现的Service来找出读数据用的BluetoothGattCharacteristic和写数据用的BluetoothGattCharacteristic。 initReadAndWriteCharacteristic(gatt.getServices()); } else {// 未发现服务 Log.w(TAG, "onServicesDiscovered received: " + status); } } @Override // 读取操作的回调结果 public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } } @Override // 写入操作的回调结果 public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { }; ... }; ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57



找出读写”数据包”的”搬运工”

下面是找出读写”搬运工”BluetoothGattCharacteristic的initReadAndWriteCharacteristic()代码实现

BluetoothGattCharacteristic mReadCharacteristic;
BluetoothGattCharacteristic mWriteCharacteristic;

public void initReadAndWriteCharacteristic(
            List<BluetoothGattService> gattServices) {
        if (gattServices == null) return; // 遍历所有的 GATT Services. for (BluetoothGattService gattService : gattServices) { if (!gattService.getUuid().toString().trim().equalsIgnoreCase("这里是你期望的Service的uuid,由你司智能外色的驱动工程师决定")) continue; List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics(); // 遍历当前Service中所有的Characteristics. for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { if (gattCharacteristic.getUuid().toString().trim().equalsIgnoreCase(""这里是你期望的写数据的uuid,由你司驱动工程师决定"")) { mWriteCharacteristic = gattCharacteristic; } else if (gattCharacteristic.getUuid().toString().trim().equalsIgnoreCase("这里是你期望的读数据的uuid,由你司驱动工程师决定")) { mReadCharacteristic = gattCharacteristic; } } } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

至此,我们就拿到了可携带读写数据的“搬运工”-『mReadCharacteristic & mWriteCharacteristic』,下面就可以和智能硬件进行交互了。 

APP读取BLE外设蓝牙数据

想要读取BLE外设的数据时,比如:心跳速率,电量等等。可通过下面方式。

// 告诉”搬运工“我想知道BLE外设当前数据,将回调BluetoothGattCallback接口的onCharacteristicRead()方法
mBluetoothGatt.readCharacteristic(mReadCharacteristic);

// 读取BLE蓝牙数据操作的回调方法
 @Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); // ”搬运工“把”数据包“搬出来了 byte[] data = characteristic.getValue(); // 根据驱动工程师给的协议文档,解析该数组,该处假设数组0位上表示心跳速率 int heartRateR = data[0];// 得到心跳速率,做相应UI更新和操作 } } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

 

APP向BLE外设写入数据

比如说你想告诉BLE外设让他锁屏,或者进行某个动作,APP向操纵BLE外设时可通过以下方式

// 根据驱动工程师给的协议文档,组织一个数组命令
byte[] data = getData();
// 将该条命令“数据包”给“搬运工"
mWriteCharacteristic.setValue(data);

// ”搬运工“将数据搬到BLE外设里面了,将回调BluetoothGattCallback接口的onCharacteristicWrite()方法
mBluetoothGatt.writeCharacteristic(characteristic);

// 向BLE蓝牙外设写入数据操作的回调方法 @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if(status == BluetoothGatt.GATT_SUCCESS) { // 命令写入成功,数据包成功写入BLE外设中 } };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

多说一句,其实,手机关闭外设也是一条写入命令,外设得到该命令后即进入省电待机状态,一般外设也可以通过开/关机键彻底关机。

 

APP关闭蓝牙连接

交互完了,不需要了,还是把APP蓝牙连接给断掉吧

public void close() {
    if (mBluetoothGatt == null) { return; } mBluetoothGatt.close(); mBluetoothGatt = null; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

后记

Android官方在SDK中提供了许多demo供开发者参考(1年前左右),其实关于BLE api调用也是有的,不过只涉及了蓝牙外设的连接,未涉及蓝牙数据读写.BLE官方demo路径:User/AndroidSDK/samples/android-19/connectivity/BluetoothLeGatt 
以上路径是笔者举例的路径,如果你的SDK目录下没有samples目录,现在(20170308)SDK Manager已经不开放sample下载了,请点击下载:android-sample-api19 文件提取密码: y87g

====update==== 
根据评论区网友xinyang_code & weiyouren_c指正:

在Android 6.0+搜索蓝牙是需要定位权限的,还有BLE搜索在Android 5.0以前和以后是不一样的。最后你还会发现使用官方这套搜索在一些手机型号上也是搜不到的!只能通过传统蓝牙(非BLE方式)搜索然后过滤出BLE设备。

笔者当年做BLE开发时基于api 19,当时5.0还未风靡大陆,4.4大行其道,非常感谢以上两位网友指正赐教!

欢迎各位朋友评论区留言交流。

本文原创作者:MichaelX,博客地址:http://blog.csdn.net/xiong_it.转载请注明来源

欢迎光临:MichaelX’s Blog

参考链接

https://developer.android.com/guide/topics/connectivity/bluetooth-le.html#terms




    本文转自 一点点征服   博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/6773997.html,如需转载请自行联系原作者


相关文章
|
10天前
|
Android开发 Kotlin
kotlin开发安卓app,如何让布局自适应系统传统导航和全面屏导航
使用`navigationBarsPadding()`修饰符实现界面自适应,自动处理底部导航栏的内边距,再加上`.padding(bottom = 10.dp)`设定内容与屏幕底部的距离,以完成全面的布局适配。示例代码采用Kotlin。
47 15
|
1天前
|
前端开发 Android开发 iOS开发
探索安卓与iOS开发的差异性与互补性
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统各据一方,引领着市场潮流。它们在技术架构、开发环境及用户群体等方面展现出独特的差异性,同时也存在着潜在的互补性。本文将深入剖析这两种平台的开发细节,从不同角度揭示其各自优势及相互之间的协同潜力,为开发者提供全面而深刻的视角。
8 2
|
7天前
|
Java Android开发 iOS开发
探索安卓与iOS开发的差异性与互操作性
【7月更文挑战第17天】在移动应用开发的广阔天地中,安卓和iOS这两大操作系统如同双子星座般璀璨夺目。它们各自拥有独特的开发环境、编程语言和用户群体,为开发者提供了不同的挑战和机遇。本文将从多个维度深入剖析安卓与iOS开发的差异性,并探讨它们之间的互操作性如何实现,以期为开发者们提供一份实用的指南。
21 7
|
5天前
|
Java Android开发 Swift
探索iOS与安卓开发的差异与挑战
本文深入探讨了iOS和安卓两大移动操作系统在应用开发领域的不同点及其所面临的挑战。通过对开发环境、编程语言、用户界面设计、性能优化及市场策略的比较分析,揭示了各自平台的独特性以及开发者需要克服的技术与市场障碍。 【7月更文挑战第19天】
|
5天前
|
Java Android开发 iOS开发
探索安卓与iOS开发的差异:平台特性与用户体验的对比分析
【7月更文挑战第19天】在移动开发的广阔天地中,安卓与iOS两大阵营各据一方,它们在开发环境、用户界面设计、性能优化等方面展现出独特的魅力与挑战。本文旨在深入探讨这两个平台在技术开发和用户体验上的根本差异,并分析这些差异如何影响开发者的策略和最终用户的选择。通过比较两者的编程语言、工具、框架以及设计理念,我们将揭示各自平台的优势与局限,为开发者提供实用的参考,并为消费者呈现一个更加清晰的平台选择视角。
|
10天前
|
存储 API Android开发
kotlin开发安卓app,使用webivew 触发 onShowFileChooser, 但只能触发一次,第二次无法触发,是怎么回事。 如何解决
在Android WebView开发中,`onShowFileChooser`方法用于开启文件选择。当用户只能选择一次文件可能是因为未正确处理选择回调。解决此问题需确保:1) 实现`WebChromeClient`并覆写`onShowFileChooser`;2) 用户选择文件后调用`ValueCallback.onReceiveValue`传递URI;3) 传递结果后将`ValueCallback`设为`null`以允许再次选择。下面是一个Kotlin示例,展示如何处理文件选择和结果回调。别忘了在Android 6.0+动态请求存储权限,以及在Android 10+处理分区存储。
|
6天前
|
安全 Java Android开发
探索安卓与iOS开发的差异:构建未来应用的关键考量
【7月更文挑战第18天】在移动应用开发的广阔天地中,安卓和iOS两大平台各领风骚。本文将深入探讨这两个平台在开发过程中的主要差异,包括编程语言、用户界面设计、性能优化、安全性以及市场策略等方面。通过比较分析,旨在为开发者提供决策支持,帮助他们选择最适合自己项目需求的平台,同时考虑到用户体验和市场需求的变化,为未来的应用开发指明方向。
|
7天前
|
监控 开发工具 Android开发
探索安卓与iOS开发的差异:平台特性、工具和市场趋势
在移动应用开发的广阔舞台上,安卓与iOS两大操作系统扮演着主角。它们各自拥有独特的平台特性、开发工具和市场定位,这些差异深刻影响着开发者的决策和产品的最终形态。本文将深入分析这两大平台的关键技术差异,探讨各自的开发环境和工具集,以及它们在市场上的表现和未来的趋势,为开发者提供一个全面的视角,帮助他们在这两个平台上做出更明智的开发选择。
|
4天前
|
开发工具 Android开发 Swift
探索Android与iOS开发的差异与挑战
【7月更文挑战第20天】在移动应用开发的广阔天地中,Android和iOS两大平台如同双子星座,各自闪耀着独特的光芒。本文将深入探讨这两个平台在开发过程中的主要差异,以及开发者面临的技术挑战。我们将从开发环境、编程语言、用户界面设计、性能优化、安全性考量等多个维度展开讨论,旨在为那些即将踏入或已在这片星空下航行的开发者提供一盏明灯。
|
5天前
|
Java Android开发 iOS开发
探索安卓与iOS开发的差异:平台特性与用户体验的对比分析
在移动应用开发的广阔天地中,安卓与iOS两大阵营各自占据着半壁江山。本文将深入探讨这两个平台在开发环境、编程语言、用户界面设计以及性能优化方面的差异,并分析这些差异如何影响最终的用户体验。通过数据支持的案例分析和最新的市场研究,我们将揭示开发者如何在这两个不同的生态系统中做出战略决策,以及这些决策对应用成功的潜在影响。