Android 应用添加地理围栏并在同一服务中接收意图

Android App add geofences and receive intents in same service

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

服务定义

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

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

activity 所做的主要工作是启动服务并绘制与服务中收到的地理围栏通知相关的内容。

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

编辑

好的,看来我们需要更多信息 -- 服务概要:

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" 都没有任何帮助。

首先,不要使用IntentService for this. Its sole purpose is to get a single intent, run it in a background thread, then stops itself. What you are looking for is a Service,因为它会持续一段时间(直到OS开始运行资源不足)。

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

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

Third,现在我们已经设置了服务,您可能已经注意到 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.
    }
}

第四,您需要修改您的清单,让应用知道应该使用此 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>

我不确定您为什么要使用导出和启用标志,但不需要声明它们,因为默认设置为启用,如果您有意图过滤器。

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