我刚刚开始学习如何使用服务,我正在尝试构建一个应用程序,当设备与用户定义的位置相距一定距离时会触发警报。 我读了几个教程并写了这个:
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();
}
}
当我在模拟器中运行应用程序时,应用程序崩溃,并在日志中写有“谷歌客户端尚未连接”。 我该如何解决?此外,这项服务甚至可以正确构建吗?
编辑:--------------------------------------------- -------
我更改了setoffalarm():
private void setOffAlarm(int index) {
Log.d("setoff", "got here");
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,
index, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am =
(AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),
pendingIntent);
}
我还补充说:
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);
}
}
但它仍然不会激活闹钟,有什么建议吗? 编辑2 -------------------------------------------
alarmreceiver类:
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;
}
}
“闹钟,到达这里”并没有出现在日志中
答案 0 :(得分:1)
问题是,当API尚未连接时,您正在调用setUpLocationClientIfNeeded()
。
要解决此错误,请在调用onConnected()
之前等待setUpLocationClientIfNeeded()
回调。
因此,请将其从onStartCommand()
:
@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;
}
然后将其添加到onConnected()
:
@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();
}
编辑:关于未触发的警报,看起来您只创建了一个警报,列表中的每个警报都会覆盖前一个警报。
您需要为每个PendingIntent提供不同的requestCode
以便解决此问题。
请参阅documentation for PendingIntent.getActivity() here。
因此,您应该向setAlarm()
添加一个参数,您只需使用alarms
列表中的索引:
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
}
}
然后,在setOffAlarm()
中,使用传入参数的索引作为requestCode
,而不是将其硬编码为12345
:
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);
}