2017-02-26 22 views
0

我正在嘗試製作一個計算出租車/自動票價的應用程序。該應用程序將作爲後臺服務運行,即使在用戶退出應用程序後,它仍會繼續更新後臺數據,例如位置,票價,距離等。當用戶按下停止按鈕時,服務將停止。現在我的問題是,每次活動恢復/重新啓動時,它都會重置爲初始狀態 - 就像服務停止一樣。如何持續更新用戶界面,以便活動保持更新,以便每當用戶進入頁面時都會顯示更新的數據。我正在運行一個STICKY服務。更新來自有界服務的活動UI

這是我的服務

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; 
} 

}

+0

閱讀關於處理程序和使用處理程序來從服務更新UI .. – shadygoneinsane

回答

0

要從服務將數據發送到活動

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

使用broadcase收到r內部活動

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 
      } 
     } 
} 

將您的動作名稱添加到意圖過濾器並註冊廣播接收器。

+0

有沒有比廣播更好的方法。我必須每隔一秒更新一次,每10-50米左右更新一次位置/距離。 –

+0

廣播接收器是一個很好的解決方案。我用它每100毫秒更新一次數據。 –

0
  1. 嘗試使用首選項設置並將您的值保存在服務類中。
  2. 添加日誌以查看您是否正在接收位置更新,並且您的值是否已打印。
  3. handler.postDelayed(this,1000);如果您的服務正在更新值,則可能不需要。
+0

我的位置正在更新。因此距離計算也。我想以某種方式將服務中的數據發佈到活動中。另外,當我打開應用程序時,我需要自啓動以來更新的距離和時間。 –

+0

然後這將是您的問題最簡單的解決方案。將這些值保存在首選項中。在onResume()中讀取這些值。 – albeee