如何逆向Flutter应用(反编译)

目前大多数使用Flutter的应用都是采用add2app的方式,在APP中与Flutter相关的内容主要有FlutterEngine、APP产物、资源文件。我们可以在应用市场上寻找一个接入Flutter的应用做实验。(apk可在各大应用市场下载,ipa下载可以在mac上安装Apple Configurator 2进行),apk和ipa中flutter相关产物目录如下:

iOS包文件为ipa,下载后将其后缀重命名为zip进行解压,解压后Payload下即可看到应用文件夹,其中FlutterEngine、APP产物、资源文件分别在如下位置:

Android包文件为apk,下载后将其后缀重命名为zip进行解压,其中FlutterEngine、APP产物、资源文件分别在如下位置:

FlutterEngine各个app都是使用官方或者在官方基础上进行修改,差别不多,我们暂不关心这部分的逆向。资源文件多是图片,字体等无需逆向即可查看的资源。我们主要关心的是使用Dart编写的业务逻辑或者某些框架代码,这部分代码在APP产物中。即:App.framework/App 或 armeabi/libapp.o这两个文件都是动态库,我们先简单看看里面包含什么?

可以看到无论是Android还是iOS,Dart App产物中都包含4个程序段。(来自https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode

  • '_kDartVmSnapshotData': 代表 isolate 之间共享的 Dart 堆 (heap) 的初始状态。有助于更快地启动 Dart isolate,但不包含任何 isolate 专属的信息。

  • '_kDartVmSnapshotInstructions':包含 VM 中所有 Dart isolate 之间共享的通用例程的 AOT 指令。这种快照的体积通常非常小,并且大多会包含程序桩 (stub)。

  • '_kDartIsolateSnapshotData':代表 Dart 堆的初始状态,并包含 isolate 专属的信息。

  • '_kDartIsolateSnapshotInstructions':包含由 Dart isolate 执行的 AOT 代码。

看了上面可能还是一脸懵o((⊙﹏⊙))o,为什么分四块,Data与Instructions,Vm与Isolate是什么?为什么使用Snapshot(快照)命名。关于这些问题,推荐一篇博客https://mrale.ph/dartvm/ 。Data与Instructions,Vm与Isolate这些概念两两组合,正好对应上面四个段。也就是VmData、VmInstructions、IsolateData、IsolateInstructions。

先说一下Data与Instructions。首先我们知道的是Flutter编译运行在app上分为JIT和AOT模式,线上只能使用AOT模式,也就是Flutter引入的DartVM包含了执行AOT产物的功能。为了与JIT模式兼容,DartVM采用了所谓快照的方式,即JIT运行时编译后的基本结构与AOT编译的基本结构相同。将类信息、全局变量、函数指令直接以序列化的方式存在磁盘中,称为Snapshot(快照)。

由于快照的序列化格式针对性的为读取速率做了设计,从快照读取也大大提高代码的加载速度(创建所需类信息、全局数据等,可以类比OC Runtime启动加载元类、类信息等)。最开始快照中是不包含机器代码的(即函数体内部的执行逻辑),后来随着AOT模式的开发这部分被加入到快照中了,这些后来者也就是前面说的Instructions。

这里要补充的是,Instructions指的是可执行汇编指令,在.o文件中必须放在text段里,标记为可执行(否则iOS无法加载并执行它)。类信息、全局变量这些内容可以放在data端作为普通数据被加载。(字节的优化50%包体积也是基于此,有兴趣可以看一下文章:https://juejin.im/post/6844904014170030087)。

接着说DartVmSnapshot 与DartIsolateSnapshot。这就涉及Data虚拟机是如何运行业务代码。虚拟是Data代码运行的载体,VM中运行的逻辑都跑在一个抽象的叫做Isolate(隔离)的实体中。你可以把Isolate当做OC里一个带有Runloop的Thread看待(至于他们之间的关系又是一个令人头疼的面试题,这里不展开了)。简要来说Isolate中维护了堆栈变量,函数调用栈帧,用于GC、JIT等辅助任务的子线程等, 而这里的堆栈变量就是要被序列化到磁盘上的东西,即IsolateSnapshot。此外像dart预置的全局对象,比如null,true,false等等等是由VMIsolate管理的,这些东西需序列化后即VmSnapshot。

到这里大致了解Flutter APP产物中的结构。那如何读取他们呢?我们可以从clustered_snapshot.cc中的FullSnapshotReader:: 函数看起,看他是如何反序列化的。

要看懂这部分也是十分费力,另一个大神的分析文章可能会为我们带来很多启示:https://blog.tst.sh/reverse-engineering-flutter-apps-part-1/

我们要看如何读取RawObject对象

每个对象均以包含以下标记的uint32_t开头:

原则上我们自己可以写一个读取的程序进行分析,但是网上有一个使用Python写好的读取程序(只支持读取ELF格式文件,也就是只支持Android包产物的分析):https://github.com/hdw09/darter 基于这个读取工具提供的API我们可以写一个导出应用所有类定义的工具。

这个脚本最终会提取所有指定文件的源码,其中对友商app其中一个类的导出结果如下:

其中标注了类对象 与函数的索引,可以在控制台使用s.refs[xxxxx].x继续跟查。

参考链接


如何逆向Flutter应用(反编译)

Dart Mixin介绍

关于 Dart mixin 的一些理解。理解 mixin 概念的关键在于理解中间类。

Mixins are a way of reusing code in multiple class hierarchies

先来看一个简单例子:

PilotedCraft 拥有 astronauts 字段和 describeCrew() 方法。

mixin 是什么?

维基百科中这样定义 mixin:

In object-oriented programming languages, a Mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes.

即,mixin 是另外一个普通类,我们可以在不继承这个类的情况下从这个类”借用”方法和变量。

Support for the mixin keyword was introduced in Dart 2.1. Code in earlier releases usually used abstract class instead.

从这个角度来讲,mixin 不过是 abstract class

Java tries to make up for this by using Interfaces, but that is not as useful or flexible as mixins.

从这个角度来讲,可以认为 mixin 是带实现的接口。

小节

  • mixin 有点类似 abstract class
  • mixin 有点类似 interface
  • 不能继承 mixin
  • 可以使用 mixinabstract classclass 来作为 mixin

如何使用 mixin?

使用 mixin 的方法很简单:with 关键字后面跟上 mixin 的名字即可。

实现 mixin 的方法同样也很简单:创建一个继承自 Object 的类并且不要声明构造方法。如果想让 mixin 作为普通类使用,使用 class 关键字;如果不想让 mixin 作为普通类使用,使用 mixin 关键字代替 class

on 的用法

The keyword on is used to restrict our mixin’s use to only classes which either extends or implements the class it is declared on. In order to use the on keyword, you must declare your mixin using the mixin keyword

则有如下错误提示:

Error: 'Object' doesn't implement 'B' so it can't be used with 'Y'.

on 关键字限制了 Y 的使用范围:Y 只能用于继承或实现了 B 的类。修复方式是让 Q 继承自 B

mixin 解决了什么问题?

mixin 解决了多重继承中的 Deadly Diamond of Death(DDD) 问题。

多重继承问题简单描述。各个类的继承关系如下:

问题来了,当调用 Musician.perform() 时,到底会调用哪个 perform() 方法是模糊的

来看 mixin 如何解决这个问题。见 Dart for Flutter : Mixins in Dart - Flutter Community - Medium

现在,当调用 Musician.perform() 时,到底会调用哪个 perform() 方法是确定的。在这里是调用 Singer.perform()

mixin 有一套明确的机制来选择调用哪个方法。

假设 Musician 类使用多个 mixin (DancerSinger)。该类有个方法名为 perform()Musician 类继承自 Performer 类。

  • 首先,将 Performer 类置于栈顶
  • 其次,后声明的 mixin 优先于先声明的 mixin。按顺序将 mixin 置于栈中,在这里分别是 DancerSinger
  • 最后,将 Musician 类自己置于栈中。Musician 类中的 perform() 被优先调用

Dart 使用的是单重继承 (Java 也是单重继承,C++ 是多重继承)。多重继承更为强大,但会引起 Deadly Diamond of Death(DDD) 问题。

Java 使用接口(interface)来部分实现多重继承。多重继承的问题是需要在每个类中实现接口(interface),所以并不是一个好的方案。(实际上 Java 已经通过默认方法修复了这个问题)

所以 Dart 中就有了 mixin。

理解 mixin

Mixins in Dart work by creating a new class that layers the implementation of the mixin on top of a superclass to create a new class — it is not “on the side” but “on top” of the superclass, so there is no ambiguity in how to resolve lookups.
Mixins is not a way to get multiple inheritance in the classical sense. Mixins is a way to abstract and reuse a family of operations and state. It is similar to the reuse you get from extending a class, but it is compatible with single-inheritance because it is linear.
StackOverflow

以上这段代码输出 BA

从语义上讲以上这段代码等同于:

继承结构图是这样的:

Since each mixin application creates a new class, it also creates a new interface (because all Dart classes also define interfaces). As described, the new class extends the superclass and includes copies of the mixin class members, but it also implements the mixin class interface.
In most cases, there is no way to refer to that mixin-application class or its interface; the class for Super with Mixin is just an anonymous superclass of the class declared like class C extends Super with Mixin {}. If you name a mixin application like class CSuper = Super with Mixin {}, then you can refer to the mixin application class and its interface, and it will be a sub-type of both Super and Mixin.

理解 mixin 的关键在于它是线性的。

使用场景

  • 在没有共同父类的各个类中共享代码时
  • 在父类中实现某种方法无意义时

在 Dart 语言中,我们经常可以看到对 mixin 关键字的使用,根据字面理解,就是混合的意思。那么,mixin 如何使用,它的使用场景是什么呢。

继续阅读Dart Mixin介绍

布隆过滤器

什么是 BloomFilter

布隆过滤器(英语:Bloom Filter)是 1970 年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。主要用于判断一个元素是否在一个集合中。

通常我们会遇到很多要判断一个元素是否在某个集合中的业务场景,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间也会呈现线性增长,最终达到瓶颈。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为O(n)O(logn)O(1)

这个时候,布隆过滤器(Bloom Filter)就应运而生。

布隆过滤器原理

了解布隆过滤器原理之前,先回顾下 Hash 函数原理。

哈希函数

哈希函数的概念是:将任意大小的输入数据转换成特定大小的输出数据的函数,转换后的数据称为哈希值或哈希编码,也叫散列值。下面是一幅示意图:

所有散列函数都有如下基本特性:

  • 如果两个散列值是不相同的(根据同一函数),那么这两个散列值的原始输入也是不相同的。这个特性是散列函数具有确定性的结果,具有这种性质的散列函数称为单向散列函数
  • 散列函数的输入和输出不是唯一对应关系的,如果两个散列值相同,两个输入值很可能是相同的,但也可能不同,这种情况称为“散列碰撞(collision)”。

但是用 hash表存储大数据量时,空间效率还是很低,当只有一个 hash 函数时,还很容易发生哈希碰撞。

布隆过滤器数据结构

BloomFilter 是由一个固定大小的二进制向量或者位图(bitmap)和一系列映射函数组成的。

在初始状态时,对于长度为 m 的位数组,它的所有位都被置为0,如下图所示:

当有变量被加入集合时,通过 K 个映射函数将这个变量映射成位图中的 K 个点,把它们置为 1(假定有两个变量都通过 3 个映射函数)。

查询某个变量的时候我们只要看看这些点是不是都是 1 就可以大概率知道集合中有没有它了

  • 如果这些点有任何一个 0,则被查询变量一定不在;
  • 如果都是 1,则被查询变量很可能存在

为什么说是可能存在,而不是一定存在呢?那是因为映射函数本身就是散列函数,散列函数是会有碰撞的。

误判率

布隆过滤器的误判是指多个输入经过哈希之后在相同的bit位置1了,这样就无法判断究竟是哪个输入产生的,因此误判的根源在于相同的 bit 位被多次映射且置 1。

这种情况也造成了布隆过滤器的删除问题,因为布隆过滤器的每一个 bit 并不是独占的,很有可能多个元素共享了某一位。如果我们直接删除这一位的话,会影响其他的元素。(比如上图中的第 3 位)

特性
  • 一个元素如果判断结果为存在的时候元素不一定存在,但是判断结果为不存在的时候则一定不存在
  • 布隆过滤器可以添加元素,但是不能删除元素。因为删掉元素会导致误判率增加。
添加与查询元素步骤
添加元素
  1. 将要添加的元素给 k 个哈希函数
  2. 得到对应于位数组上的 k 个位置
  3. 将这k个位置设为 1
查询元素
  1. 将要查询的元素给k个哈希函数
  2. 得到对应于位数组上的k个位置
  3. 如果k个位置有一个为 0,则肯定不在集合中
  4. 如果k个位置全部为 1,则可能在集合中
优点

相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常数 O(K),另外,散列函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

布隆过滤器可以表示全集,其它任何数据结构都不能;

缺点

但是布隆过滤器的缺点和优点一样明显。误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。

另外,一般情况下不能从布隆过滤器中删除元素。我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加 1, 这样删除元素时将计数器减掉就可以了。然而要保证安全地删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面。这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。

在降低误算率方面,有不少工作,使得出现了很多布隆过滤器的变种。

布隆过滤器使用场景和实例

在程序的世界中,布隆过滤器是程序员的一把利器,利用它可以快速地解决项目中一些比较棘手的问题。

如网页 URL 去重、垃圾邮件识别、大集合中重复元素的判断和缓存穿透等问题。

布隆过滤器的典型应用有:

  • 数据库防止穿库。 Google Bigtable,HBase 和 Cassandra 以及 Postgresql 使用BloomFilter来减少不存在的行或列的磁盘查找。避免代价高昂的磁盘查找会大大提高数据库查询操作的性能。
  • 业务场景中判断用户是否阅读过某视频或文章,比如抖音或头条,当然会导致一定的误判,但不会让用户看到重复的内容。
  • 缓存宕机、缓存击穿场景,一般判断用户是否在缓存中,如果在则直接返回结果,不在则查询db,如果来一波冷数据,会导致缓存大量击穿,造成雪崩效应,这时候可以用布隆过滤器当缓存的索引,只有在布隆过滤器中,才去查询缓存,如果没查询到,则穿透到db。如果不在布隆器中,则直接返回。
  • WEB拦截器,如果相同请求则拦截,防止重复被攻击。用户第一次请求,将请求参数放入布隆过滤器中,当第二次请求时,先判断请求参数是否被布隆过滤器命中。可以提高缓存命中率。Squid 网页代理缓存服务器在 cache digests 中就使用了布隆过滤器。Google Chrome浏览器使用了布隆过滤器加速安全浏览服务
  • Venti 文档存储系统也采用布隆过滤器来检测先前存储的数据。
  • SPIN 模型检测器也使用布隆过滤器在大规模验证问题时跟踪可达状态空间。

Coding~

知道了布隆过滤去的原理和使用场景,我们可以自己实现一个简单的布隆过滤器

自定义的 BloomFilter

What?我们写的这些早有大牛帮我们实现,还造轮子,真是浪费时间,No,No,No,我们学习过程中是可以造轮子的,造轮子本身就是我们自己对设计和实现的具体落地过程,不仅能提高我们的编程能力,在造轮子的过程中肯定会遇到很多我们没有思考过的问题,成长看的见~~

实际项目使用的时候,领导和我说项目一定要稳定运行,没自信的我放弃了自己的轮子。

Guava 中的 BloomFilter

分布式环境中,布隆过滤器肯定还需要考虑是可以共享的资源,这时候我们会想到 Redis,是的,Redis 也实现了布隆过滤器。

当然我们也可以把布隆过滤器通过 bloomFilter.writeTo() 写入一个文件,放入OSS、S3这类对象存储中。

Redis 中的 BloomFilter

Redis 提供的 bitMap 可以实现布隆过滤器,但是需要自己设计映射函数和一些细节,这和我们自定义没啥区别。

Redis 官方提供的布隆过滤器到了 Redis 4.0 提供了插件功能之后才正式登场。布隆过滤器作为一个插件加载到 Redis Server 中,给 Redis 提供了强大的布隆去重功能。

在已安装 Redis 的前提下,安装 RedisBloom,有两种方式

直接编译进行安装

使用Docker进行安装

使用

布隆过滤器基本指令:

  • bf.add 添加元素到布隆过滤器
  • bf.exists 判断元素是否在布隆过滤器
  • bf.madd 添加多个元素到布隆过滤器,bf.add 只能添加一个
  • bf.mexists 判断多个元素是否在布隆过滤器

我们只有这几个参数,肯定不会有误判,当元素逐渐增多时,就会有一定的误判了,这里就不做这个实验了。

上面使用的布隆过滤器只是默认参数的布隆过滤器,它在我们第一次 add 的时候自动创建。

Redis 还提供了自定义参数的布隆过滤器,bf.reserve 过滤器名 error_rate initial_size

  • error_rate:允许布隆过滤器的错误率,这个值越低过滤器的位数组的大小越大,占用空间也就越大
  • initial_size:布隆过滤器可以储存的元素个数,当实际存储的元素个数超过这个值之后,过滤器的准确率会下降

但是这个操作需要在 add 之前显式创建。如果对应的 key 已经存在,bf.reserve 会报错

我是一名 Javaer,肯定还要用 Java 来实现的,Java 的 Redis 客户端比较多,有些还没有提供指令扩展机制,笔者已知的 Redisson 和 lettuce 是可以使用布隆过滤器的,我们这里用 Redisson

扩展

为了解决布隆过滤器不能删除元素的问题,布谷鸟过滤器横空出世。论文《Cuckoo Filter:Better Than Bloom》作者将布谷鸟过滤器和布隆过滤器进行了深入的对比。相比布谷鸟过滤器而言布隆过滤器有以下不足:查询性能弱、空间利用效率低、不支持反向操作(删除)以及不支持计数。

由于使用较少,暂不深入。

参考链接


布隆过滤器,这一篇给你讲的明明白白

RocksDB使用入门macOS Big Sur (11.6.6)

这篇文章就简单介绍怎么在macOS下使用RocksDB。

1. RocksDB简要介绍

RocksDB是一个可嵌入的、持久型的Key-Value存储。

不像MySQL,PostgreSQL这样数据库分客户端与服务器端。实际上,可以把它当作一个第三方库,在自己的代码中进行引用,调用相应的接口就可以使用,比如使用C/C++语言,直接在源代码中include相应的头文件就可以。

与嵌入式数据库SQLite的使用比较类似。

目前: RocksDB使用LSM存储引擎,纯C++编写。Java版本RocksJava也已经完美支持。

更多有关RocksDB的介绍参考:

关于RocksDB的详细使用介绍:

2. RocksDB 安装

macOS上安装RocksDB非常简单,不用单独安装其他任何依赖。直接使用brew工具安装即可。

可以运行一下命令来查看安装信息。

安装结果如下。

其他操作系统上的安装可以参考:

3. 使用示例

代码目录:

  • main.cpp
  • Makefile

main.cpp文件内容:

Makefile文件内容:

运行结果:

编译时需要注意两点:
  • 支持C11, 添加选项-std=c++11
  • 添加链接库: -lrocksdb

参考链接


RocksDB使用入门 Mac

丰田RAV4 2016款空调滤芯更换以及安装方向

丰田RAV4 2016款空调滤芯更换教程:

1. 不带智能进入和起动系统的车辆:将发动机开关切换至“LOCK”位置。带智能进入和起动系统的车辆:关闭发动机开关。

2. 提起并取出隔板。

3. 拆下手套箱盖。

4. 拆下滤清器盖。

5. 拆下空调滤清器并更换为新滤清器。滤清器上的“↑UP”标记应朝上。

■更换间隔

根据保养计划,检查并更换空调滤清器。在多尘或交通繁忙的地区,更换间隔

可能需要缩短。

■如果通风口的气流明显减弱

滤清器可能堵塞。检查滤清器,如有必要进行更换。

■使用空调系统时

确保滤清器已安装。

使用不带滤清器的空调系统可能会损坏系统。

原厂滤芯的实际安装朝向如下:

博世等标注气流方向的滤芯的实际安装朝向如下:

也就是与原厂的滤芯的箭头方向相反,保证文字都是正的即可。

注意滤芯是长方形的,原厂的文字是印刻在短边的一侧。买的滤芯文字可能印在了长边一侧,这个时候我们需要转一下方向,不要强行把有文字的一侧露在外面。滤芯是很容易插入的。

参考链接


树莓派3B+/4B报告“low voltage warning”

需求场景

用的好好的树莓派,有一天连接VNC,发现桌面任务栏一直提示

如下图:

看样子是供电不足,可是树莓派的负载并不高啊。

解决方案

搜了一大圈基本都是说电源的问题,现在使用的电源是以前小米2/小米Note手机自带的电源。经过反复测试,发现跟温度有关,冬天的时候问题不大,到了夏天,由于老式电源转换效率低,大量电能被转换成了热量,导致长时间充电后,温度上升很快,引起降压,断电后过一段时间就可以了,但是一旦温度上升到一定程度,问题依旧。

后来买了个努比亚大白20W氮化镓充电器之后,就不会报告这个错误了。氮化镓的充电器转化效率更高,升温更少,并且更耐高温。

由于这个提示只有在连接显示器或VNC连接时才能在GUI桌面任务栏看到,如果是SSH登录就看不到了,非常的不方便。最好是能够通过命令判断是否电压不足。

具体的脚本详见 https://gist.github.com/maxme/d5f000c84a4313aa531288c35c3a8887

通过此脚本可以查看CPU和电压不足的问题。不过每次登录之后需要手动运行,实在不方面。而且脚本中采用了while循环需要手动中断脚本。所以可以把代码中的while循环去掉,并且在SSH登录的时候执行命令病输出到登录信息上。

注意电压充足的情况下,输出电压为1.2V,而不是 5V,主板的5V是没办法通过脚本直接测量的。我们只能通过脚本测量CPU、内存控制器、内存输入输出、内存本身的电压,主板电压只能用万用表来测量。

效果如下,具体方式参考 ubuntu修改ssh登录提示信息

修改后的raspberry-power-supply-check.sh代码如下:

参考链接


Flutter构建Linux应用

从3.0之后的版本,flutter已经正式支持构建Linux应用

条件

  • ubuntu 22.04
  • Android Studio Chipmunk |  2021.2.1
  • flutter sdk 3.x

方法

1. 打开Linux的平台支持

输入如下指令(如下命令可以使用flutter config查看,2.10以上的版本默认开启

可以使用flutter doctor查看情况。

安装编译依赖

2. 在旧项目中添加Linux平台支持

在项目地址输入如下指令

注意: 项目的名称必须是全小写,如果出现大小写则会报错

3. 构建Linux项目

4. 报错

出现这个问题

参考链接


Flutter构建Windows应用

从2.10之后的版本,flutter已经正式支持构建Windows应用。不过距离实现全平台构建,还需要考虑很多问题,flutter构建的应用更加适合移动端的使用习惯,如果需要构建Windows应用,我的建议是使用一些适合桌面端的widget。

具体方法如下:

条件

  • 需要安装Visual Studio 2019或者Visual Studio 2022,简言之,需要安装Windows 10 SDK。
  • 2.10之前的版本是默认关闭的。Linux和Mac是默认关闭的,可以手动进行打开。

方法

1. 打开Windows的平台支持

PowerShell输入如下指令(如下命令可以使用flutter config查看,2.10以上的版本默认开启

可以使用flutter doctor查看情况

如果报错:

则需要更新Flutter SDK到3.0.2以上的版本即可,如下:

具体原因,参考:[windows] Exception: Bad UTF-8 encoding when running flutter doctor or building/running for Windows #102451

2. 在旧项目中添加Windows平台支持

在项目地址输入如下指令

注意: 项目的名称必须是全小写,如果出现大小写则会报错

3. 构建Windows项目exe

参考链接


Android系统中是否开启定位及定位模式的判断

Android系统中包括3中定位模式:

  使用GPS、WLAN和移动网络 使用WLAN和移动网络 仅使用GPS
特点

同时使用GPS、WIFI及基站定位,速度快、精度高,室内定位效果好。

缺点:耗流量、耗电量

只使用WIFI和基站定位,需要WIFI或者基站才行,室内效果好。

缺点:依赖WIFI或基站,精度一般

不依赖WIFI和基站,室内效果差,户外可靠性好。

缺点:室内效果差

代码 LocationManager.GPS_PROVIDER = true;
LocationManager.NETWORK_PROVIDER = true;
LocationManager.GPS_PROVIDER = false;
LocationManager.NETWORK_PROVIDER = true;
LocationManager.GPS_PROVIDER = true;
LocationManager.NETWORK_PROVIDER = false;
是否打开定位服务 Settings.Secure.LOCATION_MODE = 3 Settings.Secure.LOCATION_MODE = 2 Settings.Secure.LOCATION_MODE = 1

Android系统中检查是否开启了定位服务的代码,参考如下:

参考链接