【问题标题】:Android App add geofences and receive intents in same serviceAndroid App 在同一服务中添加地理围栏和接收意图
【发布时间】:2016-12-23 03:50:27
【问题描述】:

我正在尝试构建一个地理围栏应用程序,但它似乎只在主要活动开始时注册地理围栏,并且当应用程序关闭时意图服务停止接收它们。因此,我将添加地理围栏逻辑(连同意图处理代码)移到了意图服务中,并确保该服务已启动,但现在该服务根本没有收到任何意图!

服务定义

public class GeofenceTransitionsIntentService extends IntentService implements ConnectionCallbacks, OnConnectionFailedListener, ResultCallback<Status>

服务中的所有内容(构建和连接的谷歌 api 客户端)都在 onCreate 中完成,意图处理程序和地理围栏注册东西 onConnected 注册地理围栏等。基本上,我试图实现大量借用用于处理这些意图的同一服务中的地理围栏示例代码(来自文档)。

所有主要活动都是启动服务并绘制与服务中收到的地理围栏通知相关的内容。

如果您需要更多信息,请告诉我。

编辑

好吧,看来我们需要更多信息——服务大纲:

public class GeofenceTransitionsIntentService extends IntentService implements ConnectionCallbacks, OnConnectionFailedListener, ResultCallback<Status> {

    protected static final String TAG = "GeofenceTransitionsIS";

    protected GoogleApiClient mGoogleApiClient;
    protected ArrayList<Geofence> mGeofenceList;
    private boolean mGeofencesAdded;
    private PendingIntent mGeofencePendingIntent;
    private SharedPreferences mSharedPreferences;

    public GeofenceTransitionsIntentService() {
        super(TAG);
    }

    @Override
    public void onCreate() {
        super.onCreate();

        buildGoogleApiClient();
        populateGeofenceList();
        mGoogleApiClient.connect();

    }

    ...

    @Override
    protected void onHandleIntent(Intent intent) {
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
        // handle the intent, send a notification
    }


    private void sendNotification(String notificationDetails) {
        // sends a notification
    }

    @Override
    public void onConnected(Bundle connectionHint)
    {
        LocationServices.GeofencingApi.addGeofences(
                mGoogleApiClient,
                getGeofencingRequest(),
                getGeofencePendingIntent()
        ).setResultCallback(this);
    }

    // straight out of the example
    private GeofencingRequest getGeofencingRequest()
    {
        ...
    }


    // from a branch of the example that reuses the pending intent
    private PendingIntent getGeofencePendingIntent()
    {
        if (mGeofencePendingIntent != null)
        {
            return mGeofencePendingIntent;
        }

        Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
        mGeofencePendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        return mGeofencePendingIntent;
    }

    public void populateGeofenceList() {
        for (thing place : listofplaces) {
            mGeofenceList.add(...)
        }
    }

    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    public void onResult(Status status)
    {
        // were fences added? usually yes
    }
}

我的研究令人沮丧——我看到有人声称能够通过广播接收器(请参阅第一条评论)而不是通过服务执行此类操作?

从我一直在处理的所有修订中,我有一个非常混乱的 manifest.xml:

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

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

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

    <service android:name=".GeofenceTransitionsIntentService"
             android:exported="true"
             android:enabled="true">
        <intent-filter >
            <action android:name="com.aol.android.geofence.ACTION_RECEIVE_GEOFENCE"/>
        </intent-filter>
    </service>

    ...

</application>

intent-filterandroid:exported="true" 添加到服务定义都没有任何帮助。

【问题讨论】:

  • 我不是安卓开发者*
  • 也许这个问题可以帮助你。 stackoverflow.com/questions/21090674/…
  • @TychoTheTaco 感谢您的回复,但它似乎没有任何效果 - 添加“导出”,重建并重新启动应用程序 + 服务,仍然没有。更令人气愤的是,没有调试输出表明它可能无法正常工作!
  • 应用关闭后如何确保服务重启?
  • 我关闭了应用程序,然后在设备设置菜单中关闭了服务,然后重新启动应用程序并从服务启动查看所有调试日志

标签: android geofencing android-geofence


【解决方案1】:

首先,不要为此使用IntentService。它的唯一目的是获得一个意图,在后台线程中运行它,然后自行停止。您正在寻找的是 Service,因为它会持续一段时间(直到操作系统开始耗尽资源)。

其次,将代码移至服务后,请执行以下操作:

public class GeofenceTransitionsService extends Service implements ConnectionCallbacks, OnConnectionFailedListener, ResultCallback<Status> {
    //Whatever you need to declare
    ....
    GeofencingRequest mRequest;

    //This is only called once per instance of a Service, so use this to instantiate class variables
    @Override
    public void onCreate() {
        super.onCreate();
        buildGoogleApiClient();
        mGoogleApiClient.connect();
    }

    //Every time you call context.startService(Intent intent) after the service is created, 
    //this function gets called with the intent you have given it. You can use this to modify or change the geofence api,
    //passing GeofencingRequests in intents by calling intent.putExtra(...) before sending the intent, and retrieving it here.
    //I just assume you are passing GeofencingRequest objects, since they are pacelable.
    @Override
    public int onStartCommand(Intent intent, int flags, final int startId) {
        mRequest = intent.getParcelableExtra("request"); //Or whatever the key is for your request.
        if(mGoogleApiClient.isConnected()){
            LocationServices.GeofencingApi.addGeofences(
                mGoogleApiClient,
                mRequest,
                getGeofencePendingIntent()
            ).setResultCallback(this);
        }
    }

    @Override
    public void onConnected(Bundle connectionHint)
    {
        LocationServices.GeofencingApi.addGeofences(
            mGoogleApiClient,
            mRequest,
            getGeofencePendingIntent()
        ).setResultCallback(this);
    }

    // from a branch of the example that reuses the pending intent
    private PendingIntent getGeofencePendingIntent()
    {
        if (mGeofencePendingIntent != null)
        {
            return mGeofencePendingIntent;
        }

        mGeofencePendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(this, GoogleGeofenceReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
        return mGeofencePendingIntent;
    }

    //The rest of your code
    ....
}

请记住,Android 会在资源不足时终止您的服务,而不会发出太多警告。如果您需要此服务以更高的优先级运行,我强烈建议您查看starting in the foreground

第三,现在我们已经设置了服务,您可能已经注意到 getGeofencePendingIntent() 函数现在使用 BroadcastReceiver 而不是它正在运行的服务。这是您如何设置的上:

public class GoogleGeofenceReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(final Context context, final Intent intent) {
        GeofencingEvent event = GeofencingEvent.fromIntent(intent);
        ...
        //Do whatever you did in your Service handleIntent function here.
    }
}

第四,您需要修改您的 Manifest 以让应用知道应该使用此 BroadcastReceiver:

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

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

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

    <service android:name=".GeofenceTransitionsService"
             android:exported="true"
             android:enabled="true">
        <intent-filter >
            <action android:name="com.aol.android.geofence.ACTION_RECEIVE_GEOFENCE"/>
        </intent-filter>
    </service>
    <receiver android:name=".GoogleGeofenceReceiver"/>

    ...

</application>

我不确定你为什么要使用 export 和 enabled 标志,但它们不需要声明,因为默认情况下设置了 enabled,如果你有一个意图过滤器,则 export 的默认值为“true”。

我建议您阅读有关 Activity、Service 和 BroadcastReceiver 生命周期的内容,因为了解这将使您对这个项目大有裨益,并让您更好地了解 Android 的一般痛苦。

【讨论】:

  • 这是完美的。绝对解决所有问题的一点是将接收器用于未决意图。 1)为什么需要广播接收器? 2) 广播接收器似乎在位置更改后很长时间调用了onReceive - 为接收器注册未决意图 (=lag) 和活动 (=no lag) 之间是否存在根本区别?
  • 另外一件事 - 在几个 onReceive 之后,广播接收器停止工作(不再接收任何意图)。我正在调查它;这可能只是这个滞后问题的一个极端案例。
  • 1) 它不一定是BroadcastReceiver,但它是更适合使用的类,因为onReceive方法在主线程上调用,而onHandleIntent方法在后台运行线。如果您尝试操作 UI,这非常重要。 2) 延迟时间长的原因是Google geofence API 似乎使用网络位置来验证设备是否进入/退出了地理围栏,并且网络位置存在高度的不准确性。从我上次测试开始,在我退出地理围栏后,它需要额外的 50m 才能注册。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多