1

所以我一直在编写一个应用程序来确定你的跑步速度,并将其整合到锻炼程序中。由于我是 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 的原因。

任何帮助都会很棒,我似乎在我的搜索中找不到任何关于我的问题的内容。据我所知,我已经正确绑定了服务。谢谢你的时间!

4

1 回答 1

0

我没有看到您启动服务,您应该执行以下操作:

    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 来调用。

于 2015-05-20T20:46:41.273 回答