如何?监听位置设置是否开启(Android 应用)

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

So I've spent the past few weeks working on my Android App and looking into the best way of implementing what I need to do, but still can't quite get it right.. Any/all help is greatly appreciated, as I am still getting the hang of things..

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

(Assuming the "Location"/GPS Setting is currently off), I need to have my App constantly be listening for the "Location" Setting to be turned ON.. At this point, simply start an Activity.

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

These are all the different ways I think it could possibly work:

  • LocationListener 使用onProviderEnabled"

  • LocationListener using "onProviderEnabled"

GpsStatusListener 使用onGpsStatusChanged"和GPS_EVENT_STARTED"

GpsStatusListener using "onGpsStatusChanged" with "GPS_EVENT_STARTED"

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

GpsProvider requiresSatellite (to determine if it starts), or somehow use the GpsProvider's "AVAILABLE" Constant/int

SettingInjectorService 使用ACTION_SERVICE_INTENT"(和/或)ACTION_INJECTED_SETTING_CHANGED"和onGetEnabled"或isEnabled"

SettingInjectorService using "ACTION_SERVICE_INTENT" (and/or) "ACTION_INJECTED_SETTING_CHANGED" with "onGetEnabled" or "isEnabled"

Settings.Secure using "LOCATION_MODE" != "LOCATION_MODE_OFF"

Settings.Secure using "LOCATION_MODE" != "LOCATION_MODE_OFF"

ContentObserver/ContentResolver

a ContentObserver/ContentResolver

Intent getAction (...)

Intent getAction (...)

某种if/else"

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

Any kind of advice or answers for any of the following questions are very much appreciated..

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

  • Which of the Ideas above would be the best way of accomplishing the Task? The simpler the better, but most importantly it needs to be listening at all times, and respond instantly when the Location Setting is turned On.

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

For whichever one of the Ideas above works best, how would I implement it? (For example, would I need a BroadcastListener? or a Service? and how would it all piece together?

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


I truly appreciate any advice or help that you can provide me with.. I'm still getting the hang of all this, but confident enough to do it, and eager to publish my first App.. So thank you, it means a lot and will greatly help me along.




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

MyReceiver.Java



OK So here's what I've got so far...
Heres my Receiver:

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 设置,对吗?我还希望广播侦听器继续侦听,即使在它接收到初始广播之后(这样如果他们关闭 LOCATION 设置,我的 onReceive 仍然会触发,然后他们再次将其打开......

那么 (A) 到目前为止我的代码看起来如何?
(B) 要完成我刚才描述的内容,需要添加什么内容?
(C) 当我打开 LOCATION 设置时,运行它会引发此错误.
..我该如何解决它?:
java.lang.RuntimeException:无法启动接收器 com.bryce.projects.servicesthreadsetc.MyReceiver:android.util.AndroidRuntimeException:从 Activity 上下文外部调用 startActivity() 需要 FLAG_ACTIVITY_NEW_TASK 标志.这真的是你想要的吗?


再次感谢所有帮助!

Aside from these files, I have the following files:
MainActivity.Java and DialogActivity.Java, both with Layout files,
activity_main.xml and activity_dialog.xml to match with them.

So if I understand correctly, when the user downloads the App and opens it, my MainActivity.Java and corresponding layout will launch. But I am only going to use this simply as a preferences screen. So once they open the App for the first time, the Broadcast Receiver should automatically start listening for the LOCATION setting to be turned on, correct? I also want the Broadcast Listener to remain listening, even after it receives the initial Broadcast (so that my onReceive still fires if they turn the LOCATION setting off, and then later they turn it ON again..

So (A) How does my code look thus far?
and (B) To accomplish what I just described, what would need to be added?
and (C) Running it throws this error when I turn the LOCATION setting ON.
..How can I fix it?:
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?


Thanks again for all the help!

推荐答案

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

Since you don't need to actually get a Location, the best implementation for your needs would be a BroadcastReceiver.

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

This is the best option because you wouldn't need to have a Service running at all times (resulting in extra batter drain), and you would be able to start your Activity from the BroadcastReceiver.

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

With the intent filter and BroadcastReceiver, your app will be started by the OS whenever the Location setting has changed (enabled or disabled), and in the case that it is enabled, you can start your Activity from the BroadcastReceiver.

首先添加intent过滤器,当操作系统发出设置发生变化的隐式Intent时会捕获该过滤器.

First add the intent filter, which will be captured when the OS sends out the implicit Intent that the setting has changed.

<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

Note that for this to work on Android Oreo and above, you'll need to register the broadcast receiver at runtime, see here: https://developer.android.com/guide/components/broadcasts#context-registered-receivers

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

Then, in LocationProviderChangedReceiver.java, your implementation would be something like this:

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 注册接收器.

Updated solution with Kotlin and registering receiver at runtime for Android 9.

Kotlin 中的 BroadcastReceiver 类:

The BroadcastReceiver class in Kotlin:

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() 中:

Register at runtime, for example in onCreate() of your app's MainActivity:

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)
}

相关文章