简体   繁体   English

怎么办? 监听位置设置被打开(Android 应用程序)

[英]How to? Listen for Location Setting being turned ON (Android App)

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

Task:任务:

(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. (假设“位置”/GPS 设置当前已关闭),我需要让我的应用程序不断监听“位置”设置是否打开。此时,只需启动一个 Activity。

Ideas:想法:

These are all the different ways I think it could possibly work:这些是我认为它可能起作用的所有不同方式:

  • LocationListener using "onProviderEnabled" LocationListener 使用“onProviderEnabled”

  • GpsStatusListener using "onGpsStatusChanged" with "GPS_EVENT_STARTED" GpsStatusListener 使用“onGpsStatusChanged”和“GPS_EVENT_STARTED”

  • GpsProvider requiresSatellite (to determine if it starts), or somehow use the GpsProvider's "AVAILABLE" Constant/int GpsProvider requiresSatellite(以确定它是否启动),或以某种方式使用 GpsProvider 的“可用”常量/int

  • SettingInjectorService using "ACTION_SERVICE_INTENT" (and/or) "ACTION_INJECTED_SETTING_CHANGED" with "onGetEnabled" or "isEnabled" SettingInjectorService 使用“ACTION_SERVICE_INTENT”(和/或)“ACTION_INJECTED_SETTING_CHANGED”和“onGetEnabled”或“isEnabled”

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

  • a ContentObserver/ContentResolver一个 ContentObserver/ContentResolver

  • Intent getAction (...)意图 getAction (...)

  • An "if/else" of some kind某种“if/else”

Questions:问题:

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.越简单越好,但最重要的是它需要一直在听,并在位置设置打开时立即响应。

  • 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? (例如,我需要一个 BroadcastListener 吗?还是一个 Service?以及它们如何拼凑在一起?

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.我真的很感谢您可以提供给我的任何建议或帮助。并将极大地帮助我。


EDIT:编辑:

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
}

And heres what the Manifest looks like:下面是 Manifest 的样子:

Manifest.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>

Aside from these files, I have the following files:除了这些文件,我还有以下文件:
MainActivity.Java and DialogActivity.Java , both with Layout files, MainActivity.JavaDialogActivity.Java ,都带有布局文件,
activity_main.xml and activity_dialog.xml to match with them. activity_main.xmlactivity_dialog.xml与它们匹配。

So if I understand correctly, when the user downloads the App and opens it, my MainActivity.Java and corresponding layout will launch.所以如果我理解正确的话,当用户下载应用程序并打开它时,我的MainActivity.Java和相应的布局就会启动。 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..我还希望广播监听器保持监听,即使在它收到初始广播之后(这样如果他们关闭 LOCATION 设置,我的onReceive仍然会触发,然后他们再次打开它..

So (A) How does my code look thus far?那么(A)到目前为止我的代码看起来如何?
and (B) To accomplish what I just described, what would need to be added? (B) 为了完成我刚刚描述的内容,需要添加什么?
and (C) Running it throws this error when I turn the LOCATION setting ON. (C) 当我打开 LOCATION 设置时,运行它会引发此错误。
..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!再次感谢所有的帮助!

Since you don't need to actually get a Location, the best implementation for your needs would be a BroadcastReceiver.由于您不需要实际获取位置,因此满足您需求的最佳实现是 BroadcastReceiver。

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.这是最好的选择,因为您不需要一直运行服务(导致额外的电池消耗),并且您将能够从 BroadcastReceiver 启动您的活动。

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 过滤器和 BroadcastReceiver,只要位置设置发生更改(启用或禁用),操作系统就会启动您的应用程序,并且在启用的情况下,您可以从 BroadcastReceiver 启动您的 Activity。

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>

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请注意,要使其在 Android Oreo 及更高版本上运行,您需要在运行时注册广播接收器,请参见此处: https : //developer.android.com/guide/components/broadcasts#context-registered-receivers

Then, in LocationProviderChangedReceiver.java, your implementation would be something like this:然后,在 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);
            }
        }
    }
}

Edit编辑

Updated solution with Kotlin and registering receiver at runtime for Android 9.使用 Kotlin 更新解决方案并在运行时为 Android 9 注册接收器。

The BroadcastReceiver class in Kotlin: 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"
    }
}

Register at runtime, for example in onCreate() of your app's MainActivity:在运行时注册,例如在您应用的 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)
}

Here is a solution using dynamic registration:这是使用动态注册的解决方案:

In your fragment or activity's onResume() method, listen to changes in LocationManager.PROVIDERS_CHANGED_ACTION在您的片段或活动的 onResume() 方法中,监听LocationManager.PROVIDERS_CHANGED_ACTION更改

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

Here is a code sample for the gpsSwitchStateReceiver object:以下是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 ");
                }
            }
        }
    };

In your onPause() method, unregister the receiver在您的 onPause() 方法中,取消注册接收器

mActivity.unregisterReceiver(gpsSwitchStateReceiver);

LocationListeners's onProvideEnabled() method will work instantly. LocationListeners 的onProvideEnabled()方法将立即起作用。 When you call this remember if you are doing LocationManager.removeUpdates() in between onProviderEnabled() won't work.当您调用它时,请记住如果您在onProviderEnabled()之间执行LocationManager.removeUpdates() onProviderEnabled()将不起作用。

And this is pretty much simple when you are reading locations from Location API's of Android.当您从 Android 的 Location API 读取位置时,这非常简单。 You don't need the broadcast receivers listening and communication further您不需要广播接收器进一步监听和通信

But when you are using FusedLocationProviderClient from google API and if you use LocationListener's onProviderEnabled and onProviderDisabled methods you will be having two services running in parallel, checking for the provider changes.但是,当您使用FusedLocationProviderClient从谷歌API,如果你使用LocationListener的的onProviderEnabledonProviderDisabled方法,你将有两个服务并行运行,检查供应商的变化。 So using BroadcastReceiver is better所以使用BroadcastReceiver更好

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM