【问题标题】:How to? Listen for Location Setting being turned ON (Android App)如何?监听位置设置是否开启(Android 应用)
【发布时间】:2016-03-31 05:45:55
【问题描述】:

因此,过去几周我一直在研究我的 Android 应用程序并寻找实现我需要做的事情的最佳方式,但仍然无法完全正确。非常感谢任何/所有帮助,因为我仍然掌握了窍门..

任务:

(假设“位置”/GPS 设置当前处于关闭状态),我需要让我的应用不断监听“位置”设置以打开。此时,只需启动一个 Activity。

想法:

这些都是我认为它可能起作用的不同方式:

  • LocationListener 使用“onProviderEnabled”

  • GpsStatusListener 使用“onGpsStatusChanged”和“GPS_EVENT_STARTED”

  • GpsProvider 需要Satellite(以确定它是否启动),或以某种方式使用 GpsProvider 的“可用”常量/int

  • SettingInjectorService 使用“ACTION_SERVICE_INTENT”(和/或)“ACTION_INJECTED_SETTING_CHANGED”和“onGetEnabled”或“isEnabled”

  • Settings.Secure 使用 "LOCATION_MODE" != "LOCATION_MODE_OFF"

  • ContentObserver/ContentResolver

  • Intent getAction (...)

  • 某种“if/else”

问题:

非常感谢您对以下任何问题的任何建议或答案..

  • 以上哪个想法是完成任务的最佳方式?越简单越好,但最重要的是它需要时刻倾听,并在位置设置打开时立即响应。

  • 对于以上任一想法最有效,我将如何实施? (例如,我需要一个 BroadcastListener?还是一个 Service?它们如何组合在一起?

我非常感谢您可以为我提供的任何建议或帮助。我仍然掌握这一切,但有足够的信心去做,并渴望发布我的第一个应用程序。所以谢谢你,它意义重大,将极大地帮助我。


编辑:

好的,这就是我到目前为止所得到的......
这是我的接收器:

MyReceiver.Java

public class MyReceiver extends BroadcastReceiver {

    private final static String TAG = "LocationProviderChanged";

    boolean isGpsEnabled;
    boolean isNetworkEnabled;



    public MyReceiver() {
    // EMPTY

    // MyReceiver Close Bracket
    }



    // START OF onReceive
    @Override
    public void onReceive(Context context, Intent intent) {


        // PRIMARY RECEIVER
        if (intent.getAction().matches("android.location.PROVIDERS_CHANGED")) {

            Log.i(TAG, "Location Providers Changed");

            LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
            isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            Toast.makeText(context, "GPS Enabled: " + isGpsEnabled + " Network Location Enabled: " + isNetworkEnabled, Toast.LENGTH_LONG).show();

            // START DIALOG ACTIVITY
            if (isGpsEnabled || isNetworkEnabled) {
                Intent runDialogActivity = new Intent(context, DialogActivity.class);
                context.startActivity(runDialogActivity);

            }

        }



        // BOOT COMPLETED (REPLICA OF PRIMARY RECEIVER CODE FOR WHEN BOOT_COMPLETED)
        if (intent.getAction().matches("android.intent.action.BOOT_COMPLETED")) {

            Log.i(TAG, "Location Providers Changed Boot");

            LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
            isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            Toast.makeText(context, "GPS Enabled Boot: " + isGpsEnabled + " Network Location Enabled Boot: " + isNetworkEnabled, Toast.LENGTH_LONG).show();

            // START DIALOG ACTIVITY
            if (isGpsEnabled || isNetworkEnabled) {
                Intent runDialogActivity = new Intent(context, DialogActivity.class);
                context.startActivity(runDialogActivity);

            }

        }



    // onReceive CLOSE BRACKET
    }



// CLOSE OF FULL CLASS
}

这是清单的样子:

清单.XML

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ender.projects.receivertest">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:allowBackup="true"
        android:fullBackupContent="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".DialogActivity">
        </activity>

        <receiver
            android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.location.PROVIDERS_CHANGED" />

                <action android:name="android.intent.action.BOOT_COMPLETED" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>

    </application>

</manifest>

除了这些文件,我还有以下文件:
MainActivity.JavaDialogActivity.Java,都带有布局文件,
activity_main.xmlactivity_dialog.xml 与它们匹配。

所以如果我理解正确,当用户下载应用程序并打开它时,我的MainActivity.Java 和相应的布局就会启动。但我只是将其用作首选项屏幕。因此,一旦他们第一次打开应用程序,广播接收器应该会自动开始侦听要打开的 LOCATION 设置,对吗?我还希望广播侦听器即使在接收到初始广播之后仍继续侦听(这样我的onReceive 仍然会在他们关闭 LOCATION 设置时触发,然后他们再次将其打开..

那么 (A) 到目前为止我的代码看起来如何?
(B) 要完成我刚才描述的内容,需要添加什么内容?
(C) 当我打开 LOCATION 设置时,运行它会引发此错误。
..我该如何解决?:
java.lang.RuntimeException: Unable to start receiver com.bryce.projects.servicesthreadsetc.MyReceiver: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?


再次感谢所有帮助!

【问题讨论】:

  • 那么我将如何实现呢?听起来 GpsStatusListener 是一个更好的选择,因为它只需要 1 个方法,而 LocationListener 有 4 个。但无论如何,将所有内容放在哪里的步骤是什么?既然它本身已经是一个监听器(我认为),我应该简单地在服务中运行它(而不是在广播监听器中)还是什么?
  • @DanielNugent 所以有几个问题。获取示例中的纬度和经度等?问题 2 是,我是否也需要使用示例中的处理程序?如果我没记错的话,它已经硬编码到 LocationListener 中了,对吧?与 BroadcastListener 的功能一样,这意味着我也不需要它,并且可以简单地在服务(或没有布局的活动)中运行代码对吗?或者没有?谢谢
  • @DanielNugent 同样,我什至不需要任何实际位置。我实际上只需要我的应用程序在“位置”打开时进行监听,然后开始新的活动。而已。但除此之外,我的应用程序与使用位置或任何其他东西没有任何关系(除了在后台运行并在打开位置设置后立即启动我的下一个活动).. 这会改变什么吗,就最佳选择而言?
  • @DanielNugent 当您说“使用 BroadcastReceiver for android.location.PROVIDERS_CHANGED”时,您是指使用 LocationListener(在服务内),还是说要实现某种“如果/Else" 以某种方式包含 PROVIDERS_CHANGED 的语句,来自实际的 BroadcastListener?
  • @DanielNugent 我希望有一种方法可以让我们更轻松地交流。我还有一些非常基本的问题,但我也不想发表这篇文章/线程长..你介意我给你发电子邮件吗?如果我能收到你的电子邮件,那将对实现我的目标非常有帮助。非常感谢您的帮助。

标签: java android gps broadcastreceiver locationmanager


【解决方案1】:

由于您不需要实际获取位置,因此满足您需求的最佳实现是 BroadcastReceiver。

这是最好的选择,因为您不需要一直运行 Service(导致额外的电池消耗),并且您可以从 BroadcastReceiver 启动 Activity。

使用 Intent 过滤器和 BroadcastReceiver,只要位置设置发生更改(启用或禁用),操作系统就会启动您的应用,如果启用,您可以从 BroadcastReceiver 启动您的 Activity。

首先添加intent过滤器,当OS发出设置发生变化的隐式Intent时会捕获该过滤器。

<receiver
    android:name=".LocationProviderChangedReceiver"
    android:exported="false" >
    <intent-filter>
        <action android:name="android.location.PROVIDERS_CHANGED" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</receiver>

请注意,要在 Android Oreo 及更高版本上运行此功能,您需要在运行时注册广播接收器,请参阅此处:https://developer.android.com/guide/components/broadcasts#context-registered-receivers

然后,在 LocationProviderChangedReceiver.java 中,您的实现将是这样的:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.location.LocationManager;
import android.util.Log;
import android.widget.Toast;

public class LocationProviderChangedReceiver extends BroadcastReceiver {
    private final static String TAG = "LocationProviderChanged";

    boolean isGpsEnabled;
    boolean isNetworkEnabled;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().matches("android.location.PROVIDERS_CHANGED"))
        {
            Log.i(TAG, "Location Providers changed");

            LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
            isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            //Start your Activity if location was enabled:
            if (isGpsEnabled || isNetworkEnabled) {
                  Intent i = new Intent(context, YourActivity.class);
                  context.startActivity(i);
            }
        }
    }
}

编辑

使用 Kotlin 更新解决方案并在运行时为 Android 9 注册接收器。

Kotlin 中的 BroadcastReceiver 类:

class LocationProviderChangedReceiver : BroadcastReceiver() {

    internal var isGpsEnabled: Boolean = false
    internal var isNetworkEnabled: Boolean = false

    override fun onReceive(context: Context, intent: Intent) {
        intent.action?.let { act ->
            if (act.matches("android.location.PROVIDERS_CHANGED".toRegex())) {
                val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
                isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
                isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)

                Log.i(TAG, "Location Providers changed, is GPS Enabled: " + isGpsEnabled)

                //Start your Activity if location was enabled:
                if (isGpsEnabled || isNetworkEnabled) {
                    val i = Intent(context, YourActivity::class.java)
                    context.startActivity(i)
                }
            }
        }
    }

    companion object {
        private val TAG = "LocationProviderChanged"
    }
}

在运行时注册,例如在您应用的 MainActivity 的 onCreate() 中:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val br: BroadcastReceiver = LocationProviderChangedReceiver()
    val filter = IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION)
    registerReceiver(br, filter)
}

【讨论】:

  • 嘿,Android Oreo 怎么样?我们不能再使用清单,因为它违反了一些新的限制。当应用程序在后台时,它基本上不会被调用。我知道我们应该用 JobScheduler 做点什么。有什么想法吗?
  • 您必须使用基于上下文的注册或动态注册。文档 [链接] (developer.android.com/guide/components/…)
【解决方案2】:

这是一个使用动态注册的解决方案:

在你的fragment或activity的onResume()方法中,监听LocationManager.PROVIDERS_CHANGED_ACTION的变化

IntentFilter filter = new IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION);
filter.addAction(Intent.ACTION_PROVIDER_CHANGED);
mActivity.registerReceiver(gpsSwitchStateReceiver, filter);

这是gpsSwitchStateReceiver 对象的代码示例:

private BroadcastReceiver gpsSwitchStateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {


            if (LocationManager.PROVIDERS_CHANGED_ACTION.equals(intent.getAction())) {
                // Make an action or refresh an already managed state.

                LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
                boolean isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
                boolean isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

                if (isGpsEnabled || isNetworkEnabled) {
                    Log.i(this.getClass().getName(), "gpsSwitchStateReceiver.onReceive() location is enabled : isGpsEnabled = " + isGpsEnabled + " isNetworkEnabled = " + isNetworkEnabled);
                } else {
                    Log.w(this.getClass().getName(), "gpsSwitchStateReceiver.onReceive() location disabled ");
                }
            }
        }
    };

在你的 onPause() 方法中,取消注册接收器

mActivity.unregisterReceiver(gpsSwitchStateReceiver);

【讨论】:

  • 如果您不想在单独的类中使用它,这很有用
【解决方案3】:

LocationListeners 的onProvideEnabled() 方法将立即生效。当您调用此函数时,请记住如果您在 onProviderEnabled() 之间执行 LocationManager.removeUpdates() 将不起作用。

当您从 Android 的 Location API 读取位置时,这非常简单。您不需要广播接收器进一步收听和通信

但是当您使用来自 google API 的 FusedLocationProviderClient 并且如果您使用 LocationListener 的 onProviderEnabledonProviderDisabled 方法时,您将有两个服务并行运行,检查提供程序的更改。所以使用BroadcastReceiver更好

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-26
    • 1970-01-01
    • 2019-08-17
    • 1970-01-01
    相关资源
    最近更新 更多