gps工具箱适配安卓7.0

99ANYc3cd6
预计阅读时长 36 分钟
位置: 首页 安卓 正文

下面我将从核心问题、解决方案、代码示例和完整适配步骤四个方面,为您详细说明如何进行GPS工具箱的安卓7.0适配。

gps工具箱适配安卓7.0
(图片来源网络,侵删)

核心问题与挑战

安卓7.0主要有三个变更会直接影响GPS工具箱的开发:

  1. HTTP明文流量的限制:这是最常见也最容易出问题的地方,如果你的应用在安卓7.0及以上的设备上,尝试通过非加密的HTTP协议(http://)去访问网络(下载离线地图、获取NMEA数据流、连接RTK服务器等),系统会直接抛出NetworkOnMainThreadException或类似的安全异常,导致功能失效。
  2. 后台执行限制:安卓7.0进一步收紧了后台应用的权限,如果你的GPS工具箱在后台持续进行高精度的定位或数据记录,系统可能会为了省电而限制其后台活动,导致定位精度下降或数据记录中断。
  3. 运行时权限:虽然安卓6.0已经引入了运行时权限,但很多开发者在适配时仍会忽略或处理不当,GPS工具箱通常需要ACCESS_FINE_LOCATION(精确位置)权限,在安卓7.0上,这个权限的申请和处理逻辑必须正确,否则应用根本无法获取任何位置信息。

核心解决方案

解决HTTP明文流量问题(最重要)

这是安卓7.0适配的首要任务,解决方案有两种,推荐第一种。

全面启用HTTPS(推荐)

这是最安全、最彻底的解决方案,你需要将所有网络请求从http://改为https://

gps工具箱适配安卓7.0
(图片来源网络,侵删)
  • 服务器端:确保你的服务器支持HTTPS,并拥有有效的SSL/TLS证书(可以是免费的Let's Encrypt证书)。
  • 客户端:修改所有网络请求的URL。
    • 下载离线地图:http://yourserver.com/map.zip -> https://yourserver.com/map.zip
    • 连接NMEA/RTK服务:tcp://yourserver.com:5000 (TCP不受影响) 或 http://yourserver.com/nmea -> https://yourserver.com/nmea

network_security_config.xml中允许特定HTTP(不推荐,仅作临时方案)

如果因为某些原因(连接的是老旧的、不支持HTTPS的硬件设备),你无法立即启用HTTPS,可以在应用中临时允许特定的HTTP域名。

操作步骤:

  1. res/xml/ 目录下创建一个文件 network_security_config.xml

    gps工具箱适配安卓7.0
    (图片来源网络,侵删)
    <!-- res/xml/network_security_config.xml -->
    <network-security-config>
        <domain-config cleartextTrafficPermitted="true">
            <domain includeSubdomains="true">yourserver.com</domain>
            <!-- 可以添加多个需要允许HTTP的域名 -->
            <domain includeSubdomains="true">192.168.1.100</domain> <!-- 允许内网IP -->
        </domain-config>
    </network-security-config>
  2. AndroidManifest.xml 中为 <application> 标签添加 android:networkSecurityConfig 属性。

    <!-- AndroidManifest.xml -->
    <application
        ...
        android:networkSecurityConfig="@xml/network_security_config"
        android:usesCleartextTraffic="true"
        ...>
        ...
    </application>
    • android:usesCleartextTraffic="true":这个属性告诉系统,你的应用允许使用明文流量(HTTP),在安卓9(Pie, API 28)及更高版本中,这是必需的。
    • 注意:此方法会降低应用的安全性,应仅作为过渡方案,并尽快迁移到HTTPS。

解决后台执行限制

GPS工具箱通常需要在后台持续工作,适配策略如下:

使用前台服务

这是最标准、最可靠的方式,前台服务会在状态栏显示一个持续的通知,告诉用户应用正在后台执行任务,从而有效避免系统被杀死。

操作步骤:

  1. AndroidManifest.xml 中声明前台服务权限

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
  2. 启动服务时,将服务设置为前台服务: 在你的 ServiceonStartCommandonStart 方法中,调用 startForeground()

    // 在你的Service类中
    private static final int NOTIFICATION_ID = 1;
    private static final String CHANNEL_ID = "GPS_Toolbox_Channel";
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 1. 创建通知渠道 (Android 8.0+ 需要)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "GPS Service",
                    NotificationManager.IMPORTANCE_DEFAULT);
            NotificationManager manager = getSystemService(NotificationManager.class);
            if (manager != null) {
                manager.createNotificationChannel(channel);
            }
        }
        // 2. 创建通知
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("GPS工具箱")
                .setContentText("正在记录轨迹...")
                .setSmallIcon(R.drawable.ic_gps_notification) // 必须设置一个图标
                .build();
        // 3. 启动为前台服务
        startForeground(NOTIFICATION_ID, notification);
        // ... 执行你的GPS记录任务
        return START_STICKY;
    }

优化定位策略

  • 降低更新频率:在后台时,可以适当降低位置更新的频率,例如从每秒一次改为每5秒一次,以节省电量。
  • 使用 setPriority():在 LocationRequest 中设置较高的优先级,虽然不能完全保证后台定位,但能提高被系统选中的概率。
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    locationRequest.setInterval(1000); // 1秒
    locationRequest.setFastestInterval(500); // 最快0.5秒

处理运行时权限

确保你的权限请求逻辑是健壮的。

标准流程:

  1. 检查权限:在需要定位功能时,首先检查是否已授权。

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
            != PackageManager.PERMISSION_GRANTED) {
        // 权限未授予,请求权限
    } else {
        // 权限已授予,开始定位
    }
  2. 请求权限:使用 ActivityCompat.requestPermissions()

    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
            MY_PERMISSIONS_REQUEST_LOCATION);
  3. 处理回调:重写 onRequestPermissionsResult 方法处理用户的授权结果。

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == MY_PERMISSIONS_REQUEST_LOCATION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 用户授权成功,可以开始定位
                startLocationUpdates();
            } else {
                // 用户拒绝授权,可以显示一个解释对话框,并引导用户去设置中手动开启
                showPermissionRationale();
            }
        }
    }
  4. 处理“不再询问”:当用户拒绝权限时,最好检查一下是否勾选了“不再询问”,如果是,则应引导用户到应用设置页面手动开启权限。

    // 在用户拒绝权限后调用
    private void showPermissionRationale() {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
            // 用户拒绝了权限,但没有勾选“不再询问”,可以再次解释并请求
            new AlertDialog.Builder(this)
                    .setTitle("权限需要")
                    .setMessage("此应用需要位置权限才能提供GPS功能。")
                    .setPositiveButton("确定", (dialog, which) -> {
                        ActivityCompat.requestPermissions(this,
                                new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                MY_PERMISSIONS_REQUEST_LOCATION);
                    })
                    .setNegativeButton("取消", null)
                    .show();
        } else {
            // 用户勾选了“不再询问”,直接跳转到设置页面
            Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
            Uri uri = Uri.fromParts("package", getPackageName(), null);
            intent.setData(uri);
            startActivity(intent);
        }
    }

代码示例:一个简化的后台GPS服务

这是一个结合了前台服务定位权限的简化版服务,可以作为你GPS工具箱的核心。

// GpsService.java
public class GpsService extends Service implements LocationListener {
    private LocationManager locationManager;
    private Location currentLocation;
    private NotificationManager notificationManager;
    @Override
    public void onCreate() {
        super.onCreate();
        locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startForegroundService();
        startLocationUpdates();
        return START_STICKY;
    }
    private void startForegroundService() {
        // 创建通知渠道 (Android 8.0+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("gps_channel",
                    "GPS Service",
                    NotificationManager.IMPORTANCE_LOW);
            notificationManager.createNotificationChannel(channel);
        }
        Notification notification = new NotificationCompat.Builder(this, "gps_channel")
                .setContentTitle("GPS工具箱")
                .setContentText("正在后台定位...")
                .setSmallIcon(R.drawable.ic_gps) // 请确保有这个图标
                .build();
        startForeground(1, notification);
    }
    private void startLocationUpdates() {
        // 检查权限
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // 权限不足,停止服务
            stopSelf();
            return;
        }
        LocationRequest locationRequest = LocationRequest.create();
        locationRequest.setInterval(1000);
        locationRequest.setFastestInterval(500);
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        try {
            locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
                    locationRequest.getInterval(),
                    locationRequest.getSmallestDisplacement(),
                    this,
                    Looper.getMainLooper()); // 使用主线程的Looper处理回调
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void onLocationChanged(Location location) {
        this.currentLocation = location;
        // 在这里处理位置数据,例如保存到文件或发送到服务器
        Log.d("GpsService", "Location: " + location.getLatitude() + ", " + location.getLongitude());
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        if (locationManager != null) {
            locationManager.removeUpdates(this);
        }
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

完整的安卓7.0适配清单

  1. 检查 targetSdkVersion:确保你的 build.gradle 文件中的 targetSdkVersion 已经设置为 24 或更高。

    android {
        compileSdkVersion 34
        defaultConfig {
            targetSdkVersion 24 // 至少为24
            ...
        }
    }
  2. 权限处理

    • AndroidManifest.xml 中声明所有需要的权限,特别是 ACCESS_FINE_LOCATIONFOREGROUND_SERVICE
    • 在运行时动态请求 ACCESS_FINE_LOCATION,并妥善处理用户拒绝和“不再询问”的情况。
  3. 网络配置

    • 强烈建议:将所有HTTP链接改为HTTPS。
    • 临时方案:如果必须使用HTTP,创建 res/xml/network_security_config.xml 并在 AndroidManifest.xml 中引用它,同时设置 android:usesCleartextTraffic="true"
  4. 后台服务

    • 如果应用需要在后台持续定位,请务必使用前台服务
    • AndroidManifest.xml 中声明 FOREGEND_SERVICE 权限。
    • 在服务启动时调用 startForeground() 并提供一个合适的通知。
  5. 测试

    • 在一台运行安卓7.0的真机上进行全面测试,模拟器有时无法完全模拟真实环境。
    • 重点测试以下场景:
      • 首次启动,权限申请流程是否顺畅?
      • 切换到桌面,GPS记录是否继续?通知是否正常显示?
      • 尝试通过HTTP下载文件,是否成功?(如果使用了HTTPS,则测试HTTPS链接)
      • 在设置中手动关闭你的应用的定位权限,应用行为是否正确?

通过以上步骤,你的GPS工具箱就能很好地适配安卓7.0系统了。

-- 展开阅读全文 --
头像
苹果短信删除后,还能找回吗?
« 上一篇 2025-12-07
华为手机开机键坏了怎么开机
下一篇 » 2025-12-07

相关文章

取消
微信二维码
支付宝二维码

最近发表

标签列表

目录[+]