从有界服务更新活动UI

时间:2017-02-26 07:08:09

标签: android service android-service handler

我正在尝试制作计算出租车/汽车票价的应用程序。该应用程序将作为后台服务运行,即使在用户退出应用程序后,仍会在后台更新乘坐数据,如位置,票价,距离等。当用户按下停止按钮时,该服务将停止。现在我的问题是,每次激活恢复/重新启动时,它都会重置为初始状态 - 就像服务停止一样。如何持续更新UI以使活动保持更新,以便每当用户访问页面时都会显示更新的数据。我正在跑一个STICKY服务。

这是我的服务

public class LocationManager extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {

    private static final String TAG = LocationManager.class.getSimpleName();
    double last_lat = -1000.0f;
    double last_lon = -1000.0f;
    double dist_total = 0.0f;
    double fare_total = 0.0f;
    long start_time = -1;
    private GoogleApiClient mGoogleApiClient;
    private Context mContext;
    private LocationRequest mLocationRequest;
    private boolean mToStartUpdates = false;
    private boolean isInited = false;
    private long mLastLocationMillis = 0;
    private SharedPreferences settings;
    String rideTime = "00h:00m:00s";
    private IBinder mBinder = new TukTukMeterBinder();
    private Timer timer = new Timer();

    public LocationManager(){}
    public void init(boolean startUpdates) {

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

        if (mGoogleApiClient != null) {
            mGoogleApiClient.connect();
        }
    }
    public class TukTukMeterBinder extends Binder{
         LocationManager getBinder(){
            return LocationManager.this;
        }
    }

    public double getDistanceTraveled(){
        return dist_total;
    }
    public double getFare_total(){
        return fare_total;
    }
    public double getLast_lat(){
        return last_lat;
    }
    public double getLast_lon(){
        return last_lon;
    }
    public String getRideTime(){
        return rideTime;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;
        init(true);
    }

    @Override
    public void onConnected(Bundle bundle) {
        LogUtil.i("GoogleApiClient connection has Connected");
        isInited = true;
        if (mToStartUpdates && RequirementHelper.isLocationEnabled(mContext)) {
            createLocationRequest();
        } else {
            createLocationRequestDialog();
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        LogUtil.i("Could not connect to googleApiClient" + i);
        if (mGoogleApiClient != null) {
            mGoogleApiClient.reconnect();
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent.getAction().equals(AppConstants.ACTION.STARTFOREGROUND_ACTION)) {
            LogUtil.i("Received Start Foreground Intent ");
            buildNotification();
            start_time = System.currentTimeMillis();
            mHandler.postDelayed(mUpdateTimeTask,1000);
        } else if (intent.getAction().equals(AppConstants.ACTION.STOPFOREGROUND_ACTION)) {
            stopForeground(true);
            stopSelf();
            mHandler.removeCallbacks(mUpdateTimeTask);
        }
        return START_STICKY;
    }

    private void buildNotification() {

        Intent notificationIntent = new Intent(this, TukTukHomeActivity.class);
        notificationIntent.setAction(AppConstants.ACTION.MAIN_ACTION);

        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
                notificationIntent, 0);

        Bitmap icon = BitmapFactory.decodeResource(getResources(),
                R.mipmap.ic_launcher);

        Notification notification = new NotificationCompat.Builder(this)
                .setContentTitle("TukTuk Meter")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(
                        Bitmap.createScaledBitmap(icon, 128, 128, false))
                .setContentIntent(pendingIntent)
                .setOngoing(true)
                .build();
        startForeground(AppConstants.NOTIFICATION_ID.FOREGROUND_SERVICE,
                notification);

    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        isInited = false;
    }

    /**
     * This method will automatically creates a dialog for automatically turning on GPS without navigating to settings activity.
     */
    public void createLocationRequestDialog() {
        mLocationRequest = LocationRequest.create();
        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                .addLocationRequest(mLocationRequest);
        builder.setAlwaysShow(true);

        PendingResult<LocationSettingsResult> result =
                LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
        result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
            @Override
            public void onResult(LocationSettingsResult result) {
                final Status status = result.getStatus();
                final LocationSettingsStates state = result.getLocationSettingsStates();
                LogUtil.d("onResult state:[" + state + "]");
                LogUtil.d("onResult status:[" + status + "]");

                switch (status.getStatusCode()) {
                    case LocationSettingsStatusCodes.SUCCESS://Already have a location.

                        if (RequirementHelper.isLocationEnabled(mContext)) {
                            createLocationRequest();
                            break;
                        } else {

                        }//$fallthrough without break
                    case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                        try {
                            LogUtil.d("showing request loccation dialog");
                            // Show the dialog by calling startResolutionForResult(),
                            // and check the result in onActivityResult().
                            status.startResolutionForResult(((Activity) mContext), TukTukHomeActivity.REQUEST_ENABLE_GPS);
                        } catch (IntentSender.SendIntentException e) {
                            // Ignore the error.
                        }
                    case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                        // Location settings are not satisfied. However, we have no way to fix the
                        // settings so we won't show the diaLogUtil.

                        break;
                }
            }
        });
    }

    /**
     * The dialog to be shown to turn on location is currently disabled.
     */
    public void createLocationRequest() {
        mLocationRequest = LocationRequest.create();
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mLocationRequest.setInterval(10 * 1000);

        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                .addLocationRequest(mLocationRequest);
        builder.setAlwaysShow(true);
        requestLocationUpdates();
    }

    public void requestLocationUpdates() {
        if (mGoogleApiClient.isConnected() && RequirementHelper.hasAnyLocationPermission(mContext)) {
            LogUtil.d("requestLocationUpdates");
            LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, LocationManager.this);
        }
    }

    @Override
    public void onLocationChanged(Location location) {
        LogUtil.d("Fetched AdsLocation" + location);
        if (location != null) {
           // DataStorePrefManager.getInstance(mContext).saveLastKnownLocation(location);

            mLastLocationMillis = SystemClock.elapsedRealtime();
            settings = PreferenceManager.getDefaultSharedPreferences(mContext);

            double min_fare = settings.getFloat(DataStorePrefManager.KEY_BASE_FARE, 0.0f);
            double min_dist = settings.getFloat(DataStorePrefManager.KEY_MIN_DISTANCE, 0.00f);
            double rate_per_km = settings.getFloat(DataStorePrefManager.KEY_KM_FARE, 0.00f);
            Log.i(TAG, location.getLatitude() + " , " + location.getLongitude());
            if (last_lat < -90 || last_lon < -180) {
                last_lat = location.getLatitude();
                last_lon = location.getLongitude();
            } else {
                double lat1 = Math.toRadians(last_lat);
                double lon1 = Math.toRadians(last_lon);

                double lat2 = Math.toRadians(location.getLatitude());
                double lon2 = Math.toRadians(location.getLongitude());

                double R = 6371.0f;
                double dLat = (lat2 - lat1);
                double dLon = (lon2 - lon1);
                double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                        Math.cos(lat1) * Math.cos(lat2) *
                                Math.sin(dLon / 2) * Math.sin(dLon / 2);
                double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
                double d = R * c;


                if (d > 0.05 && location.getAccuracy() < 50)    // Add only if delta > 50 m and uncertainty < 50m
                {
                    dist_total += d;

                    if (dist_total > min_dist) {
                        fare_total = min_fare + (dist_total - min_dist) * rate_per_km;
                    } else {
                        fare_total = min_fare;
                    }

                    Log.i("Distance", Double.toString(dist_total));

                    last_lat = location.getLatitude();
                    last_lon = location.getLongitude();

                }
            }
            DecimalFormat df = new DecimalFormat("#.0");
            df.format(fare_total);
            df.format(dist_total);
        } else return;

    }

    public void onDestroy() {
        isInited = false;
        mHandler.removeCallbacks(mUpdateTimeTask);
        if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
    }

    public boolean isInited() {
        return isInited;
    }

    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
    private Runnable mUpdateTimeTask = new TimerTask() {
        @Override
        public void run() {
            int hrs = 0,min = 0,sec= 0;
            if(start_time != -1)
            {
                int interval = (int) (System.currentTimeMillis() - start_time)/1000;
                sec = interval%60;
                min = interval/60;
                hrs = interval/3600;

                rideTime = String.format("%02dh:%02dm:%02ds", hrs,min,sec);

            }
            if(isInited){
                mHandler.postDelayed(this,1000);
            }

        }};

}

这是我的活动

public class TukTukHomeActivity extends AppCompatActivity implements View.OnClickListener, NavigationView.OnNavigationItemSelectedListener {

LocationManager mLocationManager;
public static final int REQUEST_ENABLE_GPS = 100;
boolean bound = false;
TextView rideDistance, totalFare, rideTotalTime, mapsTv;
private DrawerLayout mDrawerLayout;
double distance = 0;
double fareTotal = 0;
String rideTime = "00h:00m:00s";
boolean isRunning = false;

private ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        LocationManager.TukTukMeterBinder mBinder = (LocationManager.TukTukMeterBinder) service;
        mLocationManager = mBinder.getBinder();
        bound = true;
        initUI();
        displayDistance();
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        bound = false;
        mLocationManager = null;
    }
};


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_home);
    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    initUI();
    findViewById(R.id.startRide).setOnClickListener(this);
    toolbar.setNavigationIcon(R.drawable.menu);
    toolbar.setNavigationOnClickListener(this);
    setSupportActionBar(toolbar);
    getSupportActionBar().setDisplayShowTitleEnabled(false);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, mDrawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);
    mDrawerLayout.addDrawerListener(toggle);
    toggle.syncState();
    NavigationView navigationView = (NavigationView) findViewById(R.id.navigation);
    navigationView.setNavigationItemSelectedListener(this);
}
private void initUI(){
    rideDistance = (TextView)findViewById(R.id.rideDistance) ;
    totalFare = (TextView)findViewById(R.id.fareTotal);
    rideTotalTime = (TextView)findViewById(R.id.rideTime) ;
    mapsTv = (TextView)findViewById(R.id.openMaps);
    mapsTv.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.startRide:
            startMeterService();
            break;
        case R.id.drawerLayout:
            mDrawerLayout.openDrawer(GravityCompat.START);
            break;
        case R.id.openMaps:
            startActivity(new Intent(this,TukTukMaps.class));
            break;
    }
}

@Override
public void onBackPressed() {
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawerLayout);
    if (drawer.isDrawerOpen(GravityCompat.START)) {
        drawer.closeDrawer(GravityCompat.START);
    } else {
        super.onBackPressed();
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.items, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        startActivity(new Intent(TukTukHomeActivity.this, TukTukSettings.class));
        return true;
    }
    if(id == R.id.navigation){
        mDrawerLayout.openDrawer(GravityCompat.START);
        return true;
    }
    return super.onOptionsItemSelected(item);
}

private void startMeterService() {
    Intent startIntent = new Intent(this, LocationManager.class);
    startIntent.setAction(AppConstants.ACTION.STARTFOREGROUND_ACTION);
    startService(startIntent);
    bindService(startIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
    displayDistance();
}

@Override
protected void onResume() {
    super.onResume();
    initUI();
    displayDistance();
}

private void displayDistance() {
    final Handler handler = new Handler();
    handler.post(new Runnable() {
        @Override
        public void run() {

            if (mLocationManager != null) {
                distance = mLocationManager.getDistanceTraveled();
                fareTotal = mLocationManager.getFare_total();
                rideTime = mLocationManager.getRideTime();
            }
            rideDistance.setText(String.valueOf(distance)+"km");
            totalFare.setText(getResources().getString(R.string.min_fare_symbol)+String.valueOf(fareTotal));
            rideTotalTime.setText(String.valueOf(rideTime));
            handler.postDelayed(this, 1000);
        }
    });
}

@Override
protected void onStop() {
    super.onStop();
    if (bound) {
        unbindService(mServiceConnection);
        bound = false;
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case REQUEST_ENABLE_GPS:
            switch (resultCode) {
                case Activity.RESULT_OK:
                    startMeterService();
                    break;
                case Activity.RESULT_CANCELED:
                    break;
            }
            break;
    }
}

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    switch (item.getItemId()) {
        case R.id.about_us:
            startActivity(new Intent(TukTukHomeActivity.this,AboutUs.class));
            mDrawerLayout.closeDrawers();
            return true;
    }
    return true;
}

}

2 个答案:

答案 0 :(得分:0)

将数据从服务发送到活动

Intent i = new Intent();
i.setAction(SOME_ACTION_NAME);
i.setExtra(KEY,VALUE);
context.sendBroadcast(i);

在活动中使用广播接收器

BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(SOME_ACTION_NAME) {
           //read your extra from intent
            }
        }
 }

将您的操作名称添加到意图过滤器并注册广播接收器。

答案 1 :(得分:0)

  1. 尝试使用首选项设置并将值保存在服务类中。
  2. 添加日志以查看您是否正在接收位置更新,并且您的值是否已打印。
  3. handler.postDelayed(this,1000);如果您的服务正在更新值,则可能不需要。