Android 使用后台服务获取位置更新

Android 使用后台服务获取位置更新,android,android-service,android-location,android-alarms,android-googleapiclient,Android,Android Service,Android Location,Android Alarms,Android Googleapiclient,我刚刚开始学习如何使用服务,我正在尝试构建一个应用程序,当设备与用户定义的位置相距一定距离时,它会发出警报。 我读了几篇教程,并写了以下内容: package com.example.fgps; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import android.app.Activity; import android.app.AlarmManager; import an

我刚刚开始学习如何使用服务,我正在尝试构建一个应用程序,当设备与用户定义的位置相距一定距离时,它会发出警报。 我读了几篇教程,并写了以下内容:

 package com.example.fgps;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.IntentSender;
import android.database.Cursor;
import android.location.Location;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.model.LatLng;

public class BackgroundLocationService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
    public static final String TAG = BackgroundLocationService.class.getSimpleName();

    private List<LocationAlarm> alarms;
    private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;

    private GoogleApiClient mGoogleApiClient;
    private boolean mInProgress;

    private LocationRequest mLocationRequest;



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

        mGoogleApiClient = new GoogleApiClient.Builder(this)
        .addConnectionCallbacks(this)
        .addOnConnectionFailedListener(this)
        .addApi(LocationServices.API)
        .build();

        // Create the LocationRequest object
        mLocationRequest = LocationRequest.create()
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setInterval(3 * 1000)        // 3 seconds, in milliseconds
                .setFastestInterval(1 * 500); // 1/2 second, in milliseconds
        openDB();
        Cursor c =Mdb.getAllRows(); 
        LatLng latLng;
        for(alarms = new ArrayList<LocationAlarm>(); !c.isAfterLast(); c.moveToNext()) {
            latLng=new LatLng(c.getDouble(c.getColumnIndex(c.getColumnName(2))),c.getDouble(c.getColumnIndex(c.getColumnName(3))));
            alarms.add(new LocationAlarm(c.getInt(c.getColumnIndex(c.getColumnName(4))) != 0, latLng, c.getDouble(c.getColumnIndex(c.getColumnName(5))), c.getString(c.getColumnIndex(c.getColumnName(1)))));
        }

    }
    @Override
    public int onStartCommand (Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);

        if(mGoogleApiClient.isConnected() || mInProgress)
            return START_STICKY;

        setUpLocationClientIfNeeded();

        if(!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
            mInProgress = true;
            mGoogleApiClient.connect();
        }

        return START_STICKY;
    }
    private void setUpLocationClientIfNeeded() {
        Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        if (location == null) {
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void onConnected(Bundle bundle) {
        Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        if (location == null) {
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        }
        else {
            handleNewLocation(location);
        }
    }

    @Override
    public void onConnectionSuspended(int i) {

    }


    public void onConnectionFailed(ConnectionResult connectionResult) {
        mInProgress = false;

        // * Google Play services can resolve some errors it detects.
        // * If the error has a resolution, try sending an Intent to
        // * start a Google Play services activity that can resolve
         //* error.

        if (connectionResult.hasResolution()) {
            try {
                // Start an Activity that tries to resolve the error
                connectionResult.startResolutionForResult(null, CONNECTION_FAILURE_RESOLUTION_REQUEST);

                // * Thrown if Google Play services canceled the original
                // * PendingIntent

            } catch (IntentSender.SendIntentException e) {
                // Log the error
                e.printStackTrace();
            }
        } else {

             //* If no resolution is available, display a dialog to the
            // * user with the error.

            Log.i(TAG, "Location services connection failed with code " + connectionResult.getErrorCode());
        }
    }
    @Override
    public void onDestroy(){
        mInProgress = false;
        if (mGoogleApiClient.isConnected()) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
            mGoogleApiClient.disconnect();
        }
        super.onDestroy();
    }

    @Override
    public void onLocationChanged(Location location) {
        handleNewLocation(location);
    }

    private DBAdapter Mdb;
    private void handleNewLocation(Location location) {
        Log.d(TAG, location.toString());
        int c=0;
        for(int i=0;i<alarms.size();i++)
        {
            if(alarms.get(i).getActive())
            {
                break;
            }
            else{
                c++;
            }
        }
        if(c==alarms.size())
            stopSelf();

        double currentLatitude = location.getLatitude();
        double currentLongitude = location.getLongitude();
        LatLng latLng = new LatLng(currentLatitude, currentLongitude);
        for(int i=0;i<alarms.size();i++)
        {
            if(alarms.get(i).getActive() && (distanceCal(latLng, alarms.get(i).getLatlng())<=alarms.get(i).getDistance()))
            {
                setOffAlarm();
            }
        }

    }
    private void setOffAlarm() {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.SECOND, 1);

        //Create a new PendingIntent and add it to the AlarmManager
        Intent intent = new Intent(this, AlarmReceiverActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,
            12345, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        AlarmManager am = 
            (AlarmManager)getSystemService(Activity.ALARM_SERVICE);
        am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),
                pendingIntent);
    }
    private double distanceCal(LatLng Mloc,LatLng destination)
    {
        final int R = 6371; // Radius of the earth
        Double lat1 = Mloc.latitude;
        Double lon1 = Mloc.longitude;
        Double lat2 = destination.latitude; 
        Double lon2 = destination.longitude;
        Double latDistance = toRad(lat2-lat1);
        Double lonDistance = toRad(lon2-lon1);
        Double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) + 
                   Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * 
                   Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
        Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        Double distance = R * c;
        return distance;
    }
    private Double toRad(Double value) {
        return value * Math.PI / 180;
    }

    private void openDB(){
        Mdb = new DBAdapter(this);
        Mdb.open();
    }
}
我还要补充:

for(int i=0;i<alarms.size();i++)
    {
        if(alarms.get(i).getActive() && (distanceCal(latLng, alarms.get(i).getLatlng())<=alarms.get(i).getDistance()))
        {
            setOffAlarm(i);
        }
    }

“alarm,got here”未出现在日志cat中。问题是,当API尚未连接时,您正在调用
setupLocationClientIfNeed()

要修复错误,只需等待
onConnected()
回调,然后调用
setupLocationClientIfNeed()

因此,请将其从
onStartCommand()
中删除:

然后将其添加到
onConnected()

编辑:关于未触发的报警,看起来您只创建了一个报警,列表中的每个报警都会覆盖上一个报警。 您需要为每个PendingEvent指定不同的
requestCode
,以修复该问题

因此,您应该在
setAlarm()
中添加一个参数,您可以使用
报警列表中的索引:

    for(int i=0;i<alarms.size();i++)
    {
        if(alarms.get(i).getActive() && (distanceCal(latLng, alarms.get(i).getLatlng())<=alarms.get(i).getDistance()))
        {
            setOffAlarm(i); //give the index here
        }
    }

展示你的日志。。请问您是否已安装Google API系统映像以访问模拟器上的Google play服务?正如sameer所说,请安装Google API映像。转到sdk管理器并下载google api映像我安装的google api系统映像already@user3542927请将您的警报问题添加到新问题!它会得到更多的关注,我也会看一看。一次一个问题!:)我根据您的指示更改了代码,错误停止了,但是我仍然无法让闹钟工作,因此您可以重新查看代码吗?我编辑了您提到的部分,但不幸的是它仍然无法工作。检查上面问题的代码,我也在那里添加了它。无论如何,谢谢您的帮助。@user3542927尝试在for循环中调用
setOffAlarm(i)
的地方添加一个日志项或一个Toast,以确保它在您期望的时候进入该代码。我添加了'log.d(“循环”,“到达这里”+i);'在环的一侧,它确实到达了那里。我将在问题中添加intent发送到上面的类的代码,也许问题就在那里。@user3542927好的,听起来不错。需要注意的是,您可能应该提出一个新问题,并在其中包含所有相关信息,因为这个问题已经解决。你不应该在已经解决的现有问题上添加新问题。
    package com.example.fgps;

import java.io.IOException;

import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;

/**
 * This activity will be called when the alarm is triggered.
 * 
 */
public class AlarmReceiverActivity extends Activity {
    private MediaPlayer mMediaPlayer; 
    private PowerManager.WakeLock wl;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
        wl.acquire();
        Log.d("alarm", "got here");
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | 
                WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | 
                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | 
                WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
                WindowManager.LayoutParams.FLAG_FULLSCREEN | 
                WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | 
                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | 
                WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
        setContentView(R.layout.alarm);

        Button stopAlarm = (Button) findViewById(R.id.stopAlarm);
        stopAlarm.setOnTouchListener(new OnTouchListener() {
            public boolean onTouch(View arg0, MotionEvent arg1) {
                mMediaPlayer.stop();
                finish();
                return false;
            }
        });

        playSound(this, getAlarmUri());
    }

    @Override
    protected void onStop() {
        super.onStop();
        wl.release();
    }

    private void playSound(Context context, Uri alert) {
        mMediaPlayer = new MediaPlayer();
        try {
            mMediaPlayer.setDataSource(context, alert);
            final AudioManager audioManager = (AudioManager) context
                    .getSystemService(Context.AUDIO_SERVICE);
            if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
                mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
                mMediaPlayer.prepare();
                mMediaPlayer.start();
            }
        } catch (IOException e) {
            System.out.println("OOPS");
        }
    }

    //Get an alarm sound. Try for an alarm. If none set, try notification, 
    //Otherwise, ringtone.
    private Uri getAlarmUri() {
        Uri alert = RingtoneManager
                .getDefaultUri(RingtoneManager.TYPE_ALARM);
        if (alert == null) {
            alert = RingtoneManager
                    .getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
            if (alert == null) {
                alert = RingtoneManager
                        .getDefaultUri(RingtoneManager.TYPE_RINGTONE);
            }
        }
        return alert;
    }
}
@Override
public int onStartCommand (Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);

    if(mGoogleApiClient.isConnected() || mInProgress)
        return START_STICKY;

    //remove this:
    //setUpLocationClientIfNeeded();

    if(!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
        mInProgress = true;
        mGoogleApiClient.connect();
    }

    return START_STICKY;
}
@Override
public void onConnected(Bundle bundle) {
    Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
    if (location == null) {
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
    }
    else {
        handleNewLocation(location);
    }

    //add this here!
    setUpLocationClientIfNeeded();
}
    for(int i=0;i<alarms.size();i++)
    {
        if(alarms.get(i).getActive() && (distanceCal(latLng, alarms.get(i).getLatlng())<=alarms.get(i).getDistance()))
        {
            setOffAlarm(i); //give the index here
        }
    }
private void setOffAlarm(int index) {
    Calendar cal = Calendar.getInstance();
    cal.add(Calendar.SECOND, 1);

    //Create a new PendingIntent and add it to the AlarmManager
    Intent intent = new Intent(this, AlarmReceiverActivity.class);

    //don't use 12345 for requestCode, this will overwrite previous alarm
    //PendingIntent pendingIntent = PendingIntent.getActivity(this,
    //    12345, intent, PendingIntent.FLAG_CANCEL_CURRENT);

    //use index instead:
    PendingIntent pendingIntent = PendingIntent.getActivity(this,
        index, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    AlarmManager am = 
        (AlarmManager)getSystemService(Activity.ALARM_SERVICE);
    am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),
            pendingIntent);
}