安卓安卓开发百度地图定位开发,部分手机不能定位,部分手机耗电量大。是什么原因造成?

Android - 百度地图打包之后出现的问题 - 博客频道 - CSDN.NET
分类:Android&&Pack
百度地图开发调试的应用程序正常,打包后显示授权key失败
这是由于调试生成的应用程序使用的是eclipse默认的SHA1的值
我们需要使用自己创建的数字签名来解决这一问题。以下是解决方案
设置android 环境变量
如图所示 打开Cmd输入以下指令
1:cd&C:\Program Files\Java\jdk1.7.0_13\bin
2:keytool -genkey -alias android.keystore -keyalg RSA
-validity 20000 -keystore android.keystore
3:输入密钥口令(因为是密文,不要以为键盘没反应, 例如输入111111后直接回车) 其他信息依照提示输入。结束按Y
会生成C:\Program Files\Java\jdk1.7.0_13\bin\android.keystore文件
4:右键工程android tool-》export signed&application package
输入C:\Program Files\Java\jdk1.7.0_13\bin\android.keystore和密钥
5:如下图所见产生新的SHA1,替换百度控制台的的SHA1 打包以后的百度地图开发 &生成的apk不会提示授权Key失败
二、Android - 百度地图打包混淆代码后地图崩溃的解决方法
之前开发项目用到百度地图,测试的时候没有问题;但是,一打包加混淆之后就出现地图崩溃的问题;项目已经完结,趁有闲暇时间赶紧分享一下自己的解决方案。
问题现象:
之前做百度地图开发时出现一些小问题、一搜发现大家似乎都遇到过这样的问题、大家知道百度地图API需要申请Key,填写安全码。
安全码的组成规则为:Android签名证书的sha1值 “;” packagename(即:数字签名 分号 包名)
debug的话、代码没有经过混淆、百度地图正常使用、只有在打包之后才有这个问题。
但是呢、基本上发布到应用市场的apk一般都是经过的、有点知识产权保护意识的都会有代码混淆、
就会用一套自己的keystore,重新设定“安全码后”、导出apk时没有报任何错误
但是当我装载手机上使用百度地图定位时、地图绘制了一大半、就死掉了、留下log控制台有这么句话
com.api.map.MapRenderer.nativeRender(Native Method)
分析原因:
这里只贴了一句、还有很多异常信息哈、经过google查找、然后把百度地图官网又仔细读了便、果然功夫不负有心人、还是让我找到了、其实官方已经明确指出了
3.6.5 为何我的程序在不混淆之前可以运行,混淆之后不能运行了?
栅格版SDK(即1.3.5及之前的版本):
如果您使用proguard进行混淆,请在您的混淆配置文件中添加如下语句:
-keep class
com.baidu.mapapi.** {*;}
矢量版SDK(即2.0.0及之后的版本):
如果是用proguard ,以下配置供参考:
#-libraryjars libs/baidumapapi_v3_1_0.jar 替换成自己所用版本的jar
-keep class
com.baidu.** { *; }
-keep class
vi.com.gdi.bgl.android.**{*;}
其他混淆工具也请进行与此类似的配置,指定对mapapi中的类和方法不进行混淆。
在proguard-project.txt中添加如下(引用的是百度baidumapapi_v3_1_0.jar这版的jar包、换成自己的包就好了)、
-libraryjars libs/baidumapapi_v3_3_0.jar
-keep class
com.baidu.** { *; }
-keep class
vi.com.gdi.bgl.android.**{*;}
其实最后找到原因就是代码混淆时少了这行、加上之后再次导出、地图功能就可以正常使用了
-keep class
vi.com.gdi.bgl.android.**{*;}
排名:第10826名
(5)(1)(1)(1)(1)(3)(11)(1)(1)(2)(1)(1)(1)(1)(1)(1)(1)(166)(2)(7)(1)(1)(1)(1)(4)(1)(1)(1)(1)(1)(1)(3)(1)(5)(2)(1)(5)(1)(1)(2)(1)(1)(1)(1)(1)(2)(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)(3)(1)(1)(1)(2)(1)(2)(4)(32)(1)(22)(9)(1)(4)(1)(2)(1)(1)(1)(72)(4)(3)(2)(2)(1)(2)(1)后使用快捷导航没有帐号?
暂时没有人问过相似的问题,你可以做第一个提问题的人
查看: 5582|回复: 23
我的百度地图在有的手机上正常,个别手机显示网格怎么办?
我的百度地图在有的手机上正常,个别手机显示网格怎么办?
哪些手机显示网格&&还有使用的SDK 版本是多少 ?
本帖最后由 aiqingego 于
09:12 编辑
哪些手机显示网格&&还有使用的SDK 版本是多少 ?
我现在也遇到这个问题,我使用的定位SDK3,在三星I9082I上测试的很好用,地图能准确显示,定位点也很准确;在星星一号ZTES2002上测试的不好用:地图显示不出来,只显示网格呢,但定位显示准确。很纠结,和手机有关吗?什么问题呢?为什么不同手机地图显示不出来呢?急需解决……
我现在也遇到这个问题,我使用的定位SDK3,在三星I9082I上测试的很好用,地图能准确显示,定位点也很准确 ...
在线等……
你好 是这样的& &这是两个问题 定位只是获取一个经纬度和地图的显示没有直接关系。
你现在有些手机显示网格有一些显示地图正常&&我怀疑你使用的是比较老版本的地图SDK& &由于android系统4.4开始的一个新策略 我们的老版本地图SDK 没法在android 4.4 系统 带有SD卡的手机上显示地图& &这个问题我们早就在版本升级的时候修复了 具体请参考一下我们的更新日志 谢谢 ~
你好 是这样的& &这是两个问题 定位只是获取一个经纬度和地图的显示没有直接关系。
你现在有些手机显示网 ...
嗯 果然是android4.4 谢谢 我下载最新的sdk试试……
你好 是这样的& &这是两个问题 定位只是获取一个经纬度和地图的显示没有直接关系。
你现在有些手机显示网 ...
我已经下载最新地图sdk3.3.0并使用了,但问题没有解决,android4.4地图还是显示不出来,只有网格呢。
我看了你们的更新日志,说是修复部分4.4或以上系统手机获取缓存路径报空指针的bug;但是我的问题还是没有解决呢,我使用的是libBaiduMapSDK_v3_3_0_15.so应该是最新的吧!
请帮忙看下什么问题呢,很纳闷……
直接跑我们的demo& &注意一下key 验证要正确才行
直接跑我们的demo& &注意一下key 验证要正确才行
??? 我已经下载最新的demo和sdk了,key也是正确的,在三星I9082I机器上运行地图显示是完整的,在ZTES2002上运行地图显示不出来显示的网格,实在不知道什么bug啊啊 啊 ……
直接跑我们的demo& &注意一下key 验证要正确才行
key验证是正确的呀 ???
本帖最后由 aiqingego 于
14:27 编辑
直接跑我们的demo& &注意一下key 验证要正确才行
终于看出点问题了,直接在ZTES2002上运行程序地图是显示完整的,但是程序打包在ZTES2002上运行后地图是显示不出来的只显示网格。但这又是什么原因呢,和key有关吗?那又是什么关系呢?怎么解决呢?这个问题困的我好煎熬………………
终于看出点问题了,直接在ZTES2002上运行程序地图是显示完整的,但是程序打包在ZTES2002上运行后地图是显 ...
有没有人能解决啊……?
直接跑我们的demo& &注意一下key 验证要正确才行
技术人员可不可以出来回复一下!!!回复好慢啊!!!!
技术人员可不可以出来回复一下!!!回复好慢啊!!!!
咋就就没人回答呢?
终于看出点问题了,直接在ZTES2002上运行程序地图是显示完整的,但是程序打包在ZTES2002上运行后地图是显 ...
真是跪了!这个问题能不能出来回答一下啊!给一个关键性的解决方案啊!在线等呢……
如果只有 ZTES2002 这个机型不行的话,有可能是显示适配的问题& &我们需要进一步测试一下&&感谢反馈~
如果只有 ZTES2002 这个机型不行的话,有可能是显示适配的问题& &我们需要进一步测试一下&&感谢反馈~&&...
有点着急了,刚才静下心来看了看,还是打包后SHA1不同导致的,已经解决,谢谢~
有点着急了,刚才静下心来看了看,还是打包后SHA1不同导致的,已经解决,谢谢~ ...
怎么看打包前后SHA1的值的变化的,,求解啊
有点着急了,刚才静下心来看了看,还是打包后SHA1不同导致的,已经解决,谢谢~ ...
大神,怎么解决的啊
三星 s5 小米4 都是4.4以上版本 都是只显示网格 怎么办
三星 s5 小米4 都是4.4以上版本 都是只显示网格 怎么办
请问你的问题解决了吗?在三星S6曲面屏那款手机原来地图显示是正确的,现在也变成网格了,定位的准确的
请问你的问题解决了吗?在三星S6曲面屏那款手机原来地图显示是正确的,现在也变成网格了,定位的准确的 ...
看下鉴权成功了吗,参考demo中,在鉴权完成之后会有广播的。可以看到鉴权是否成功
看下鉴权成功了吗,参考demo中,在鉴权完成之后会有广播的。可以看到鉴权是否成功 ...
鉴权是什么呢?不清楚鉴权
不能显示地图的话,重新安装一下app吧,我刚才一个朋友用的oppo手机安装了我的app,也是只显示网格不显示网络,然后重新安装了一下就ok了。建议试试~
Powered by百度地图API解决 android 6.0定位不了的问题 - 博客频道 - CSDN.NET
Vasil的博客
分类:android软件开发
首先是搭建环境,这就不多说了,官方传送门&
接着 初始化地图
void init(){
// 地图初始化
mMapView.setVisibility(View.VISIBLE);
mBaiduMap = mMapView.getMap();
// 开启定位图层
mBaiduMap.setMyLocationEnabled(true);
// 定位初始化
mLocClient = new LocationClient(this);
mLocClient.registerLocationListener(myListener);
LocationClientOption option = new LocationClientOption();
option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
option.setOpenGps(true); // 打开gps
option.setCoorType(&bd09ll&); // 设置坐标类型
option.setScanSpan(1000);
mLocClient.setLocOption(option)
mLocClient.start();
mCurrentMode = LocationMode.COMPASS;
.setMyLocationConfigeration(new MyLocationConfiguration(
mCurrentMode, true, null));
3定位SDK的监听函数:
* 定位SDK监听函数
public class MyLocationListenner implements BDLocationListener {
public void onReceiveLocation(BDLocation location) {
// map view 销毁后不在处理新接收的位置
if (location == null || mMapView == null) {
MyLocationData locData = new MyLocationData.Builder()
.accuracy(location.getRadius())
// 此处设置开发者获取到的方向信息,顺时针0-360
.direction(100).latitude(location.getLatitude())
.longitude(location.getLongitude()).build();
mBaiduMap.setMyLocationData(locData);
if (isFirstLoc) {
isFirstLoc =
LatLng ll = new LatLng(location.getLatitude(),
location.getLongitude());
MapStatus.Builder builder = new MapStatus.Builder();
builder.target(ll).zoom(18.0f);
mBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
public void onReceivePoi(BDLocation poiLocation) {
到这里,如果我们直接运行的话,在android 6以下是运行成功的,在android 6以上会出现运行失败的情况,运行失败的原因在于android 6.0采用了运行时权限(android-RuntimePermissions),6.0的权限一般分为两种,一种时普通权限,如下图:其他的为运行时权限
在百度地图API中,用到的权限主要有以下几个
&uses-permission android:name=&com.android.launcher.permission.READ_SETTINGS& /&
&!-- 这个权限用于进行网络定位 --&
&uses-permission android:name=&android.permission.ACCESS_COARSE_LOCATION& /&
&!-- 这个权限用于访问GPS定位 --&
&uses-permission android:name=&android.permission.ACCESS_FINE_LOCATION& /&
&!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位 --&
&uses-permission android:name=&android.permission.ACCESS_WIFI_STATE& /&
&!-- 获取运营商信息,用于支持提供运营商信息相关的接口 --&
&uses-permission android:name=&android.permission.ACCESS_NETWORK_STATE& /&
&uses-permission android:name=&android.permission.WRITE_SETTINGS& /&
&!-- 用于读取手机当前的状态 --&
&uses-permission android:name=&android.permission.READ_PHONE_STATE& /&
&!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据 --&
&uses-permission android:name=&android.permission.WRITE_EXTERNAL_STORAGE& /&
&uses-permission android:name=&android.permission.MOUNT_UNMOUNT_FILESYSTEMS& /&
&!-- 访问网络,网络定位需要上网 --&
&uses-permission android:name=&android.permission.INTERNET& /&
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_PHONE_STATE
这几个为运行时权限,需要在运行时得到用户的授权。
在Activity中进行授权如下:判断用户的android版本,如果为23(6.0),执行showcontacts,提醒用户授权
if (Build.VERSION.SDK_INT&=23){
showContacts(mMapView);
判断用户是否已经授权,如果已经授权,直接开始定位,如果没有授权,
requestContactsPermissions(v);
提示用户进行授权
public void showContacts(View v) {
Log.i(TAG, &Show contacts button pressed. Checking permissions.&);
// Verify that all required contact permissions have been granted.
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
// Contacts permissions have not been granted.
Log.i(TAG, &Contact permissions has NOT been granted. Requesting permissions.&);
requestContactsPermissions(v);
// Contact permissions have been granted. Show the contacts fragment.
Log.i(TAG,
&Contact permissions have already been granted. Displaying contact details.&);
private void requestContactsPermissions(View v) {
// BEGIN_INCLUDE(contacts_permission_request)
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_COARSE_LOCATION)
|| ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION)
|| ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
|| ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_PHONE_STATE)
// Provide an additional rationale to the user if the permission was not granted
// and the user would benefit from additional context for the use of the permission.
// For example, if the request has been denied previously.
Log.i(TAG,
&Displaying contacts permission rationale to provide additional context.&);
// Display a SnackBar with an explanation and a button to trigger the request.
Snackbar.make(v, &permission_contacts_rationale&,
Snackbar.LENGTH_INDEFINITE)
.setAction(&ok&, new View.OnClickListener() {
public void onClick(View view) {
ActivityCompat
.requestPermissions(MainActivity.this, PERMISSIONS_CONTACT,
REQUEST_CONTACTS);
// Contact permissions have not been granted yet. Request them directly.
ActivityCompat.requestPermissions(this, PERMISSIONS_CONTACT, REQUEST_CONTACTS);
// END_INCLUDE(contacts_permission_request)
最后,对授权的结果进行判定:
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode==REQUEST_CONTACTS){
if (PermissionUtil.verifyPermissions(grantResults)) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
其中 permissonutil代码如下
* Utility class that wraps access to the runtime permissions API in M and provides basic helper
* methods.
public abstract class PermissionUtil {
* Check that all given permissions have been granted by verifying that each entry in the
* given array is of the value {@link PackageManager#PERMISSION_GRANTED}.
* @see Activity#onRequestPermissionsResult(int, String[], int[])
public static boolean verifyPermissions(int[] grantResults) {
// At least one result must be checked.
if(grantResults.length & 1){
// Verify that each required permission has been granted, otherwise return false.
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
最后,运行结果
排名:千里之外
(17)(4)(1)(6)(1)安卓版本的百度地图,为什么不能只用GPS定位? - 知乎8被浏览7516分享邀请回答0添加评论分享收藏感谢收起0添加评论分享收藏感谢收起更多后使用快捷导航没有帐号?
暂时没有人问过相似的问题,你可以做第一个提问题的人
查看: 28691|回复: 69
百度地图SDK只有网络不出图问题的追查方法
本帖最后由 lqzcc 于
20:23 编辑
一般发生此种情况只有两种原因:1. 网络连接不正常。
& & 此种情况较容易追查,看一下其他app能不能正常联网就行。
2. Key认证错误(最常见)。
& &由于key配置与打包apk时用的签名是一一对应的,所以打包环境变更(包括更换签名,切换开发环境,degug/release模式切换等),都会导致key验证不通过, key认证问题追查步骤如下:
1) 在Logcat中查看Tag为auth info的log , 查看其中ak , mcode 与您在Key配置页面所配置的是否一致,如果不一致则说明是key配置错误。
2) 查看LogCat 中有没有 &Authentication Error& 字样的Log, 如果有,请反馈此Log.
3) GeneralListener 的 onGetPermissionResult(int state) 有没有回调? state 值是多少?非0值表示key错误
建议参考下面链接的帖子(自己就能解决):
运行BaiduMapsApiDemo时认证出错,错误log如下。
E/baidumapsdk( 2963): Authentication Error errorcode: 102 uid: null appid -1 msg: IP/SN/SCODE/REFERER Illegal:
我的编译环境是虚拟机跑的unbuntu,编译方式是在源码下,通过mm 编译的,运行环境是真实的android设备,网络没有问题。sha1通过adt和keytool在~/.android下获取的是一致的。运行时报102的错误码。adb logcat |grep&&&auth info& 获取到的mcode:和我之前获取到的sha1不一样。尝试用mcode的值再申请了一个key,还是报102的错误。
昨天运行demo时获取过一次地图,但是只有当前屏幕的一块,拖动到其他位置后显示的还是方格,今天连一块地图都出不来了,完全都是方格。
在windows下通过虚拟设备跑这个demo一切正常。
xiangzhiwu55
& &我也有这个错误,请问这个你解决了吗???
我申请的Key核对了很多遍,也重新申请了Key,但总是报以下错误
01-09 08:40:32.816: E/baidumapsdk(5222): Authentication Error,status: 102 message: IP/SN/SCODE/REFERER Illegal:
事实上网络状态都良好,只有定位能够正常定位,可是地图就是网格不显示。
地图SDK V2.3.1 定位V4.0 SDK
“2) 查看LogCat 中有没有 &Authentication Error& 字样的Log, 如果有,请反馈此Log. ”
请问这个跟谁反馈?
跟你反馈可以吗?
& &102 是key配置错误, 请按以下仔细检查:&&1) 在Logcat中查看Tag为auth info的log , 查看其中ak , mcode 与您在Key配置页面所配置的是否一致,如果不一致则说明是key配置错误。
学习了。。。。
用的SDK2.3.1 用AndroidSDK 4.0编译,在4.2.2的机器上运行,key验证通过,普通地图只有网格,卫星地图可以加载成功,用SDK里面的demo,改了key也一样只能显示卫星地图,但是放在安卓2.3的手机上却可以显示北京地图,不过地图范围很小,缩小也就变成只有网格了。这应该是SDK缺陷吧
我也遇到了,和你的情况一样
hahabaker 我也是,很久就还没搞定&&法克
问一下,怎么搞定啊!
我今天碰到,你说key配置错误,是哪里错误呢,我弄的安全码也是&&shA1值+“;”+包名啊!还有什么地方会错,就这么点东西
我也遇到了这个问题,总算找到原因了,原因就是:在设置当前中心点的经纬度时,把经纬度写反了,导致定位的位置在北极以北很远,自然就没法出图了。这个解决办法是在猛缩小视图时发现的。遇到这问题的童鞋可以看一下是不是这个问题。
这个取到的经纬度,前面是经度,后面是纬度。而在调用api时,demo里的默认则是 double cLat = 22.547489;&&//纬度& && & double cLon=xxx //经度。这个问题害得我花了三个小时~~~~
如果是偶尔加载出网格的话,赶紧升级v2.4,v2.3.5有bug
如果是偶尔加载出网格的话,赶紧升级v2.4,v2.3.5有bug
t330chenx 发表于
& &顺便提一下,使用demo自带的locsdk 3.0,在定位上可能会出现一直不生效的问题,直接替换到官方最新 4.1就可以定位了。
我把程序打包允许就没事。。。。但是我调试允许就不显示。。。提示网络是否正常,,还报102错误。。。为啥打包允许就可以呢。。。102 要啥key ??
这个key非常不稳定,而且改了之后,还不能生效,有一次我key写错了,然后重新申请一个,就好,然后,还有时好时坏的时候,官方能给个解释么?
我自己的应用加载地图时错误代码102 。auth info的信息只有mcode,没有ak是怎么回事,另外输出一个好像是百度糯米的,两个都有
05-13 08:52:35.171&&/? I/auth info﹕ ak:&&Cc2ea8fd422e1bf3b5e1e
05-13 08:52:35.171&&/? I/auth info﹕ mcode:&&D5:FD:15:60:E5:BE:4A:FE:75:FE:82:A2:14:49:99:B6:79:A1:FE:DD;com.nuomi
05-13 08:55:31.946&&/? D/auth info﹕ mcode: 33:77:AF:97:61:35:9A:61:7D:FE:6F:3F:35:B1:51:D1:55:1D:5D:20;com.mypakage
05-13 08:56:17.326&&/? D/auth info﹕ mcode: 33:77:AF:97:61:35:9A:61:7D:FE:6F:3F:35:B1:51:D1:55:1D:5D:20;com.mypakage
如果SD卡空间不足会不会出这个问题,我的在其它手机正常,就是一个华为的手机只显示网格,SD卡剩余不足20M。
我在模拟器上显示网格,但是装到手机上运行没有问题。
请问是什么原因?
追查第三步,貌似GeneralListener 的 onGetPermissionResult(int state) 没有被调到,断点不能执行到。
楼主没有把问题讲解清楚,最关键的就是不同签名对应不同KEY,他一笔带过了。看下我的这个帖子。
已经按照官网的步骤,由于是发布版的app,用的非debug模式的keystroe生成的SHA1+包名,依旧打开app地图一片空白只有白色方格,也检查过后台日志的输入安全码和官网上申请的完全一致
我的应用在Android4.4.2上显示网格,网络环境正常,其他的版本上也正常,请问是怎么回事?
06-18 11:42:31.960: ERROR/baidumapsdk(21397): Authentication Error errorcode: 40002 uid: -1 appid -1 msg: key not exist
key是存在的,现存项目中用到了定位sdk都能定位到,说明key也是可以用的,求解,为什么不出地图只出网格
xiangzhiwu55
& & 请问你的问题解决了吗?如何解决的?或者有什么替代方案?
我在家申请了一个Key,在模拟器中运行BaiduMapsApiDemo,没有问题。 我用同样的方法,在公司我也申请了一个key,但在模拟器中运行,地图却无法显示(我可以确定我申请的key没有问题,而且模拟器也能联网)。
按照上面的说法,还是不行,不过 我自己发现了一个问题
本来是用;来进行连接,我写成了:
如果你的写错了,不要在原来的上修改,因为这样ak值是不变的,
一定要重新添加一个应用
Authentication Error errorcode: 102 uid: null appid -1 msg: IP/SN/SCODE/REFERER Illegal:
并没有出现Authentication Error,配置已经读取到&&30:A4:06:D4:F3:D7:CA:3F:75:2D:17:7E:11:68:5B:71:24:52:A2:83;baidumapsdk.demo&&采用百度demo运行 依然显示方框,地图没有,请问????
Powered by

我要回帖

更多关于 安卓开发百度地图定位 的文章

 

随机推荐