Android - 调用 bindservice 但无法访问该服务中的任何方法
Posted
技术标签:
【中文标题】Android - 调用 bindservice 但无法访问该服务中的任何方法【英文标题】:Android - calling bindservice but not being able to access any methods in that service 【发布时间】:2015-05-20 20:39:34 【问题描述】:所以我一直在编写一个应用程序来确定您的跑步速度,并将其整合到锻炼程序中。由于我是 android 新手,我在 Activity 中完成了所有操作,但我已经达到了我想将速度计算部分以及涉及 GPS 的所有内容转移到服务的地步,这将绑定到任何需要的锻炼它。
这是锻炼活动:
package com.example.stropheum.speedcalculatortest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.IBinder;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.os.Vibrator;
import com.example.stropheum.speedcalculatortest.SpeedCalculationService.SpeedCalculationBinder;
public class SpeedAlarmActivity extends ActionBarActivity
public SpeedCalculationService speedCalculator;
boolean isBound = false;
final int MILLI_TO_SEC = 1000;
final int SEC_TO_HOUR = 3600;
double currentPace, goalPace;
String paceText;
Vibrator vibrator;
// Allow 15 seconds of error for time calculations
final double MILE_TIME_ERROR = 0.25;
LocationManager locationManager;
LocationListener locationListener;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
Intent i = new Intent(this, SpeedCalculationService.class);
startService(i);
bindService(i, speedConnection, Context.BIND_AUTO_CREATE);
// Starts the service for calulating user's speed
//startService(new Intent(getBaseContext(), SpeedCalculationService.class)); // was below bind before
vibrator = (Vibrator) this.getSystemService(Context.VIBRATOR_SERVICE);
setContentView(R.layout.activity_speed_alarm);
double startTime, elapsedTime;
double alertIntervalStart, alertIntervalElapsed;
double speed, goalPace, currentPace;
String paceText = "Waiting for GPS signal";
updatePaceText(paceText);
if (isBound);
// Delays workout until the service finds a signal
while (!speedCalculator.gpsSignalFound());
// Once GPS connection is established, being the workout
paceText = "Begin!";
updatePaceText(paceText);
///////////////////
// Part One Begins
/////////////////
startTime = System.currentTimeMillis();
elapsedTime = 0;
alertIntervalStart = startTime; // Initialize 30 second timer on workout start
goalPace = 10.0;
updateGoalPace(goalPace);
do
// Update time since last alert
alertIntervalElapsed = System.currentTimeMillis() - alertIntervalStart;
speed = speedCalculator.getCurrentSpeed();
currentPace = 60 / speed;
// Update speed and pace every second
if (elapsedTime >= 1.0 * MILLI_TO_SEC)
updateSpeed(speed);
updateCurrentPace(currentPace);
// Alerts user if 30 seconds have gone by with no change
if (alertIntervalStart >= 30 * MILLI_TO_SEC)
paceAlert();
alertIntervalStart = System.currentTimeMillis();
// If 5 seconds have elapsed and perfect pace, alert user
if (alertIntervalElapsed >= 5 * MILLI_TO_SEC && checkPace(currentPace, goalPace))
paceAlert();
alertIntervalStart = System.currentTimeMillis();
elapsedTime = System.currentTimeMillis() - startTime;
while (elapsedTime < 120 * MILLI_TO_SEC);
paceText = "Workout Complete!";
updatePaceText(paceText);
// ///////////////////
// // Part Two Begins
// /////////////////
// startTime = System.currentTimeMillis();
// elapsedTime = 0;
// alertIntervalStart = startTime; // Initialize 30 second timer on workout start
//
// goalPace = 6.0;
//
// do
//
// elapsedTime = System.currentTimeMillis() - startTime;
// while (elapsedTime < 60 * MILLI_TO_SEC);
//
//
/**
* Checks if the user is running in an acceptable range of the goal pace
* @param currentPace Current speed of the user
* @param goalPace Goal speed of the user
* @return True if the pace is acceptable, false otherwise
*/
private boolean checkPace(double currentPace, double goalPace)
boolean result = true;
if (currentPace > goalPace + MILE_TIME_ERROR || currentPace < goalPace - MILE_TIME_ERROR)
result = false;
return result;
/**
* Updates the display to show the current speed
* @param speed The current speed of the user
*/
private void updateSpeed(double speed)
final TextView speedVal = (TextView) findViewById(R.id.SpeedVal);
speedVal.setText(String.format("%.2f", speed));
/**
* Updates the current estimated mile time
* @param currentPace User's current mile time
*/
private void updateCurrentPace(double currentPace)
int minutes = (int)currentPace;
int seconds = (int)(((currentPace * 100) % 100) * 0.6);
final TextView emtVal = (TextView) findViewById(R.id.emtVal);
emtVal.setText(String.format("%d:%02d", minutes, seconds));
/**
* Updates the current goal mile time
* @param goalPace New goal mile time
*/
private void updateGoalPace(double goalPace)
int minutes = (int)goalPace;
int seconds = (int)(((goalPace * 100) % 100) * 0.6);
final TextView gmtVal = (TextView) findViewById(R.id.gmtVal);
gmtVal.setText(String.format("%d:%02d", minutes, seconds));
/**
* Updates the current pace text
* @param paceText indicator for user;s current speed in relation to goal time
*/
private void updatePaceText(String paceText)
final TextView pace = (TextView) findViewById(R.id.paceView);
pace.setText(paceText);
/**
* Checks current pace and assigns appropriate text
*/
private void paceAlert()
if (currentPace > goalPace + MILE_TIME_ERROR)
paceText = "Speed up";
vibrator.vibrate(300);
try
Thread.sleep(300);
catch (Exception e)
vibrator.vibrate(300);
try
Thread.sleep(300);
catch (Exception e)
vibrator.vibrate(300);
else if (currentPace < goalPace - MILE_TIME_ERROR)
paceText = "Slow Down";
vibrator.vibrate(1000);
else
paceText = "Perfect Pace!";
@Override
public boolean onCreateOptionsMenu(Menu menu)
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_speed_alarm, menu);
return true;
@Override
public boolean onOptionsItemSelected(MenuItem item)
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings)
return true;
return super.onOptionsItemSelected(item);
@Override
public void onBackPressed()
locationManager.removeUpdates(locationListener);
// Terminate the speed calculation service
stopService(new Intent(getBaseContext(), SpeedCalculationService.class));
finish();
return;
ServiceConnection speedConnection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName name, IBinder service)
SpeedCalculationBinder binder = (SpeedCalculationBinder) service;
speedCalculator = binder.getService();
isBound = true;
@Override
public void onServiceDisconnected(ComponentName name)
isBound = false;
;
这是我所做的服务:
package com.example.stropheum.speedcalculatortest;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.widget.Toast;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
public class SpeedCalculationService extends Service
final int MILLI_TO_SEC = 1000; // Number of milliseconds in a second
final int SEC_TO_HOUR = 3600; // Number of seconds in an hour
private final IBinder binder = new SpeedCalculationBinder();
LocationManager locationManager;
LocationListener locationListener;
boolean firstLocationCheck = true;
// Tracks distance traveled between location calls
double distanceTraveled = 0;
double speed = 0;
public SpeedCalculationService()
@Override
public IBinder onBind(Intent intent)
return binder;
@Override
public int onStartCommand(Intent intent, int flags, int startId)
// This service runs until it is stopped
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
locationListener = new LocationListener()
// Tracks the longitude and latitude of the previous and current location calls
double lonNew, lonOld;
double latNew, latOld;
double startTime = System.currentTimeMillis();
double currentTime, timeElapsed;
public void onLocationChanged(Location location)
if (firstLocationCheck)
// Prime old locations for first distance calculation
latOld = Math.toRadians(location.getLatitude());
lonOld = Math.toRadians(location.getLongitude());
firstLocationCheck = false;
latNew = Math.toRadians(location.getLatitude());
lonNew = Math.toRadians(location.getLongitude());
currentTime = System.currentTimeMillis();
timeElapsed = currentTime - startTime;
distanceTraveled += haversine(latOld, lonOld, latNew, lonNew);
if (distanceTraveled > 1000) distanceTraveled = 0; // Handles start errors
speed = distanceTraveled / timeElapsed * MILLI_TO_SEC * SEC_TO_HOUR;
latOld = latNew;
lonOld = lonNew;
public void onStatusChanged(String Provider, int status, Bundle extras)
public void onProviderEnabled(String provider)
public void onProviderDisabled(String provider)
;
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
return START_STICKY;
/**
* Computes distance (in miles) between two coordinates using the haversine formula
* @param lat1 latitude of previous location
* @param lon1 longitude of previous location
* @param lat2 latitude of current location
* @param lon2 longitude of current location
* @return distance in miles
*/
private double haversine(double lat1, double lon1, double lat2, double lon2)
final double EARTH_RADIUS_M = 3959;
double dLon, dLat, a, c, distance;
// Calculate distance traveled using Haversine formula
dLon = lon2 - lon1;
dLat = lat2 - lat1;
a = Math.sin(dLat/2.0) * Math.sin(dLat/2.0) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon/2.0) * Math.sin(dLon/2.0);
System.out.println("a = " + a);
c = 2.0 * Math.atan(Math.sqrt(a));
System.out.println("c = " + c);
distance = EARTH_RADIUS_M * c;
return distance;
/**
* Returns the current calculated speed of the user
* @return the current speed in mph format
*/
public double getCurrentSpeed()
return this.speed;
/**
* Method to check if GPS connection is established
* @return true if first location check has been completed
*/
public boolean gpsSignalFound()
return !this.firstLocationCheck;
@Override
public void onDestroy()
super.onDestroy();
locationManager.removeUpdates(locationListener);
Toast.makeText(this, "Service Stopped", Toast.LENGTH_LONG).show();
// Binder class that will bind to the workout activities
public class SpeedCalculationBinder extends Binder
SpeedCalculationService getService()
return SpeedCalculationService.this;
我已经设法缩小错误来自服务中的方法在调用时返回空值,并且我添加了一些调试行以确定 isBound 变量永远不会设置为 true,因此服务是实际上并没有绑定到活动,我很确定这就是我得到 NullPointerException 的原因。
任何帮助都会很棒,我似乎无法在我的搜索中找到任何关于我的问题的内容。据我所知,我已经正确绑定了服务。感谢您的宝贵时间!
【问题讨论】:
你需要调用 startService(i);在绑定服务之前 另外,这段代码很糟糕。你会因为其他十几个原因而崩溃。您不能只在 onCreate 中等待,您将绊倒看门狗并被杀死。即使您不这样做,这也是一种糟糕的用户体验。 我真的不明白你想告诉我什么。你说的等待是什么意思?你说的看门狗绊倒是什么意思?为什么这是一种糟糕的用户体验? 【参考方案1】:我没有看到您启动服务,您应该执行以下操作:
Intent intent = new Intent(this, SpeedCalculationService.class);
startService(intent);
bindService(intent, speedConnection, Context.BIND_AUTO_CREATE);
您需要确保在启动和绑定服务时使用相同的意图。
以下是“Context.BIND_AUTO_CREATE”的一些文档:
* Flag for @link #bindService: automatically create the service as long
* as the binding exists. Note that while this will create the service,
* its @link android.app.Service#onStartCommand
* method will still only be called due to an
* explicit call to @link #startService. Even without that, though,
* this still provides you with access to the service object while the
* service is created.
请注意,onCommandStart 仅在显式调用 startService 时才被调用。
【讨论】:
我最初有这个,我读到绑定服务无论如何都会启动它,所以我认为这会导致某种冲突。我刚刚添加了那行,错误是完全一样的 您不能添加您拥有的同一行。您需要使用相同的意图 您应该确保您的代码到达您的 ServiceConnection 中的 onServiceConnected。然后在确定已连接后从那里启动与服务相关的任何内容以上是关于Android - 调用 bindservice 但无法访问该服务中的任何方法的主要内容,如果未能解决你的问题,请参考以下文章
Android里Service的bindService()和startService()混合使用深入分析
Android10Q/11R/12S Service bindService系统源码分析1-千里马framework
Android10Q/11R/12S Service bindService系统源码分析1-千里马framework
Android-bindService远程服务启动其他应用的Activity