5

我們一直在爲android平臺開發服務。即使在鎖定設備後,是否可以在android平臺上持續運行服務?

在我們的服務中,我們需要每隔一分鐘後將設備的GPS數據(緯度和長度)發送到某個外部REST服務。

設備鎖定後,它運行良好近15分鐘。但之後它不會發送任何數據。

解鎖設備後,它再次開始通過REST服務發送數據。

到目前爲止我的代碼

public class MainActivity extends AppCompatActivity { 

private PendingIntent pendingIntent; 
private PowerManager.WakeLock wakeLock; 

public static final String USER_NAME = "USERNAME"; 

String username; 
String password; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    Intent alarm = new Intent(this, AlarmReceiver.class); 
    boolean alarmRunning = (PendingIntent.getBroadcast(this, 0, alarm, PendingIntent.FLAG_NO_CREATE) != null); 
    if(alarmRunning == false) { 
     PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarm, 0); 
     AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 
     alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), 30000, pendingIntent); 
    } 

    PowerManager mgr = (PowerManager)this.getSystemService(Context.POWER_SERVICE); 
    wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakeLock"); 
    wakeLock.acquire(); 
} 

public class BackgroundService extends Service { 

private boolean isRunning; 
private Context context; 
private Thread backgroundThread; 

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

@Override 
public void onCreate() { 
    this.context = this; 
    this.isRunning = false; 
    this.backgroundThread = new Thread(myTask); 
} 

private Runnable myTask = new Runnable() { 
    public void run() { 
     // Do something here 
     login("admin","admin"); 
     stopSelf(); 
    } 
}; 

@Override 
public void onDestroy() { 
    this.isRunning = false; 
} 

@Override 
public int onStartCommand(Intent intent, int flags, int startId) { 
    if(!this.isRunning) { 
     this.isRunning = true; 
     this.backgroundThread.start(); 
    } 
    return START_STICKY; 
} 

private void login(final String strLatitude, final String strLongitude) { 


    class LoginAsync extends AsyncTask<String, Void, String> { 

     String charset = "UTF-8"; 
     HttpURLConnection conn; 
     DataOutputStream wr; 
     StringBuilder result = new StringBuilder(); 
     URL urlObj; 
     JSONObject jObj = null; 
     StringBuilder sbParams; 
     String paramsString; 

     @Override 
     protected void onPreExecute() { 
      super.onPreExecute(); 
      // loadingDialog = ProgressDialog.show(MainActivity.this, "Please wait", "Loading..."); 
     } 

     @Override 
     protected String doInBackground(String... params) { 
      String uname = params[0]; 
      String pass = params[1]; 

      sbParams = new StringBuilder(); 

      try { 
       sbParams.append("name").append("=") 
         .append(URLEncoder.encode(uname, charset)); 
       sbParams.append("&"); 
       sbParams.append("password").append("=") 
         .append(URLEncoder.encode(pass, charset)); 
      } catch (UnsupportedEncodingException e) { 
       e.printStackTrace(); 
      } 

      try { 

       String url="http://192.168.0.122:1234/YegoService.svc/AddVehicleMovement"; 
       URL object=new URL(url); 

       HttpURLConnection con = (HttpURLConnection) object.openConnection(); 
       con.setDoOutput(true); 
       con.setDoInput(true); 
       con.setRequestProperty("Content-Type", "application/json"); 
       con.setRequestProperty("Accept", "application/json"); 
       con.setRequestMethod("POST"); 

       JSONObject parent = new JSONObject(); 

       parent.put("strValidatorID","111"); 
       parent.put("TXT_LAT", "28.25252525"); 

       parent.put("TXT_LONG", "77.7777777"); 
       parent.put("DAT_DATE", ""); 
       con.connect(); 

       OutputStreamWriter wr = new OutputStreamWriter(con.getOutputStream()); 
       wr.write(parent.toString()); 
       wr.flush(); 
       wr.close(); 

       InputStream input = con.getInputStream(); 
       BufferedReader reader = new BufferedReader(new InputStreamReader(input)); 
       String line; 

       while ((line = reader.readLine()) != null) { 
        result.append(line); 
       } 

       con.disconnect(); 

      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
      catch (Exception ex) { 
       ex.printStackTrace(); 
      } 

      return result.toString(); 
     } 

     @Override 
     protected void onPostExecute(String result){ 
      String s = result.trim(); 
     } 
    } 

    LoginAsync la = new LoginAsync(); 
    la.execute("admin", "admin"); 

} 

} 

public class AlarmReceiver extends BroadcastReceiver { 
String strLatitude; 
String strLongitude; 

@Override 
public void onReceive(Context context, Intent intent) { 
    Intent background = new Intent(context, BackgroundService.class); 
    context.startService(background); 
} 
} 

怎麼辦?

+0

你有不同的設備上進行測試? o/p是否一樣? –

+0

在另一臺設備上測試,結果相同!!! –

+0

你使用部分喚醒鎖嗎? – ozo

回答

4

您正在購買Activity中的喚醒鎖。這裏的問題是,當設備被鎖定時,您的Activity被推到後臺。經過15分鐘的不活動之後,Android只是簡單地殺死這個進程。這釋放了喚醒鎖。設備進入睡眠狀態。

現在,下一次你的鬧鐘響起時,設備醒來,你的BroadcastReceiver被觸發,onReceive()被調用時,它開始你的Service,但隨後的設備回到睡眠狀態,因爲沒有喚醒鎖,所以`服務沒有做任何事情。


另一種方法,如果你想阻止手機進入您的應用程序運行時睡覺,將收購在Service喚醒鎖。在這種情況下,每次運行Runnable時,都不想撥打stopSelf()。您需要保持Service的運行狀態,直到您想停止它,此時您會撥打stopService()。這樣,Service將始終處於活動狀態(即使它沒有做任何事情),它會阻止設備通過喚醒鎖休眠。但是,這可能會給電池造成不可接受的損耗(您必須對其進行測試)。

您需要獲取BroadcastReceiver中的喚醒鎖,並確保Service開始並在設備恢復休眠前獲取喚醒鎖。看看WakefulBroadcastReceiver,你可以用它來實現這種行爲。

+0

我高中嘗試使用你的方法。但仍然不適合我。我仍然是Android應用程序的初學者。你能否給我提供一個示範代碼來展示手機永遠不會入睡。這將是非常讚賞。 –

4

一種方法可能是依靠AlarmManager:一旦您訂閱了AlarmManager,即使您的應用程序未處於活動狀態,系統本身也會按照您設置的時間間隔運行您的代碼。每次運行時,您都可以決定處理一些代碼......因此,您完全可以避免保持服務的活力。

你需要的是一個Alarm類,它將處理AlarmManager的意圖。


創造你的警報:

public class Alarm extends BroadcastReceiver 
{ 

    private static final String TAG = "Alarm"; 

    @Override 
    public void onReceive(Context context, Intent intent) 
    { 
     PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 
     PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, ""); 
     wl.acquire(); 

     /*************** 
     Here you can do your stuff... 
     This will be triggered every second. 
     Send data from here, or better: call an IntentService 
     that will take care of it. 
     ****************/ 

     wl.release(); 
    } 

    public void SetAlarm(Context context) 
    { 
     Intent i = new Intent(context, Alarm.class); 

     boolean alarmUp = (PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_NO_CREATE) != null); 

     if (alarmUp) 
     { 
      // The alarm is already running, do not set it twice 
     } 
     else 
     { 
      AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 
      PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0); 
      am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000, pi); // 1000 Millisec means it will trigger it every second... and RTC_WAKEUP means that it will wake up your device if needed. 
     } 
    } 

    // later on, use this method if you want to manually cancel the AlarmManager : 

    public void CancelAlarm(Context context) 
    { 
     Intent intent = new Intent(context, Alarm.class); 
     PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0); 
     AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 
     alarmManager.cancel(sender); 
    } 
} 

在您的清單聲明此報警廣播接收器

<receiver 
    android:name=".utils.Alarm" 
    android:process=":remote" > 
</receiver> 

而且從你想去的地方在你的活動調用此AlarmManager!

Alarm alarm = new Alarm(); 

@Override 
protected void onCreate(Bundle savedInstanceState) 
{ 

    alarm.SetAlarm(this); 

} 

// or if elsewhere you need to stop the Alarm : 
alarm.CancelAlarm(this); 

這是主要想法。 現在您需要處理屏幕打開或關閉。 對於這2種解決方案:您可以註冊設備屏幕狀態意圖並管理AlarmManager開/關...或者您可以讓AlarmManager始終運行,但在發送數據前檢查設備是否開啓/關閉...

希望這會有所幫助!

1

是的,即使設備被鎖定,您也可以運行任何服務。甚至,您可以在重新啓動設備後恢復服務。

您可以實施GCM網絡管理器。需要

示例代碼: -

<service 
    android:name=".MyTaskService" 
    android:exported="true" 
    android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE"> 
    <intent-filter> 
     <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" /> 
    </intent-filter> 
</service> 

Java代碼: -

mGcmNetworkManager = GcmNetworkManager.getInstance(this); 
OneoffTask task = new OneoffTask.Builder() 
     .setService(MyTaskService.class) 
     .setTag(TASK_TAG_WIFI) 
     .setExecutionWindow(0L, 3600L) 
     .setRequiredNetwork(Task.NETWORK_STATE_UNMETERED) 
     .build(); 

mGcmNetworkManager.schedule(task); 

富勒更多信息,您可以訪問https://developers.google.com/cloud-messaging/network-manager#run_tasks和閱讀文檔。

您只需在您的項目中包含gcm服務即可使用GCM網絡管理器。支持4.0 +

如果這是您想要的解決方案,請接受此答案。這也可以幫助其他開發者。

1

是的,你可以實現一個後臺服務,它幾乎不會被殺死。但是你必須聲明它在前臺運行。你可以通過參考這個網址(http://developer.android.com/guide/components/services.html)看到這個網址(http://developer.android.com/guide/components/services.html),他們說,

重要性層次結構和不同類型的進程按重要性排序有五個級別(第一個過程是最重要的,最後被殺害):

  1. 前臺進程

所需什麼用戶目前正在做的過程。的方法被認爲是在前景如果任一下列條件爲真:

  • 它寄宿在用戶與交互的活性(該活性 的的onResume()方法被調用)。
  • 它託管與用戶正在進行交互的活動綁定的服務。
  • 它託管着一個「在前臺」運行的服務 - 服務調用了startForeground()。
  • 它託管執行其生命週期回調 (onCreate(),onStart()或onDestroy())的服務。
  • 它託管正在執行其onReceive() 方法的BroadcastReceiver。

通常,在任何給定時間只有少數前臺進程存在。他們只是作爲最後的手段被殺 - 如果記憶太低,他們不能全部繼續奔跑。一般來說,此時,設備已達到內存分頁狀態,因此需要殺死一些前臺進程以保持用戶界面的響應。

所以你必須在前臺開始你的服務。爲了做到這一點,你已經實現瞭如下的服務。

public class MyForegroundService extends Service { 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     //your code goes here 
    } 

    @Override 
    public IBinder onBind(Intent intent) { 
     throw new UnsupportedOperationException("Not yet implemented"); 
    } 

    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
     keepServiceAlive(); 
     //your code goes here 
     return(START_NOT_STICKY); 
    } 

    private void keepServiceAlive() { 
     Intent notificationIntent = new Intent(this, MainActivity.class); 
     PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); 

     Notification notification = new NotificationCompat.Builder(this).setContentTitle(getString(R.string.app_name)) 
       .setContentText("Hello") 
       .setSmallIcon(R.mipmap.ic_launcher) 
       .setContentIntent(pendingIntent) 
       .build(); 
     startForeground(Notification.FLAG_ONGOING_EVENT, notification); 
    } 

    @Override 
    public void onDestroy() { 
     super.onDestroy(); 
     Log.w(getClass().getName(), "Got to stop()!"); 
     stopForeground(true); 
    } 
} 

感謝和GUD運氣..

+0

你想在應用程序的主線程上運行服務,在前臺,你稱它幾乎從來沒有殺過?如果服務不會停留一整天,則不應在主線程上運行服務。如果前臺服務綁定到Activity(線程)的生命週期,它將繼續運行,只要活動被銷燬,它就會被終止。 –

+0

@JemshitIskenderov 將服務置於前臺狀態,系統認爲該服務是用戶主動注意的事情(正如我在答案中也提到的那樣,「他們只是作爲最後的手段被殺死」)。請閱讀http://developer.android.com/guide/components/services.html#Basics中的基礎知識部分(您會看到,他們提到「如果服務被聲明爲在前臺運行,那麼它幾乎永遠不會被殺死「),並閱讀http://developer.android.com/guide/components/processes-and-threads.html中的流程生命週期部分)以獲取更多詳細信息。 – TdSoft

+0

即使在這種情況下,15分鐘左右服務也會死亡。 –

1

我曾在我的應用程序相同的問題,但我解決我的問題,首先要創建服務,使用週期服務。您可以指定更新數據的時間限制。在我的情況下,這是代碼。

UpdateService.java

public class UpdateServices extends Service implements LocationListener { 

    String id, latee, longee; 
    // j 
    private ProgressDialog pDialog; 
    ProgressDialog progressDialog; 
    JSONParser jsonParser = new JSONParser(); 
    DBManager db; 
    private static String url_create_locationupdate = "http://192.168.0.175/simple_demo3/classes/create_locationupdate.php"; 
    private static final String TAG_SUCCESS = "success"; 
    public static String LOG = "Log"; 
    private final Context mContext; 
    boolean isGPSEnabled = false; 
    boolean isNetworkEnabled = false; 
    boolean canGetLocation = false; 
    Location location; // location 
    double latitude; // latitude 
    double longitude; // longitude 
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 3; // 0 meters 
    private long MIN_TIME_BW_UPDATES; // 10 second 
    private long MIN_LENGTH_BW_UPDATES; 
    SharedPreferences mPref; 
    protected LocationManager locationManager; 

    public UpdateServices(Context context) { 
     this.mContext = context; 
    } 

    public UpdateServices() { 
     super(); 
     mContext = UpdateServices.this; 
    } 

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

    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
     Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); 
     Log.i(LOG, "Service started"); 

     mPref = getSharedPreferences("mFile", 0); 
     MIN_TIME_BW_UPDATES = mPref.getLong("mint", 1) * 1000 * 60; 
     MIN_LENGTH_BW_UPDATES = mPref.getLong("kmeter", 1) * 1000; 
     Log.i("asd", "This is sparta"); 
     latitude = getLocation().getLatitude(); 
     longitude = getLocation().getLongitude(); 

     return START_STICKY; 
    } 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     Log.i(LOG, "Service created"); 
    } 

    @Override 
    public void onDestroy() { 
     super.onDestroy(); 
     Log.i(LOG, "Service destroyed"); 

    } 

    public Location getLocation() { 
     try { 
      locationManager = (LocationManager) mContext 
        .getSystemService(LOCATION_SERVICE); 

      isGPSEnabled = locationManager 
        .isProviderEnabled(LocationManager.GPS_PROVIDER); 
      isNetworkEnabled = locationManager 
        .isProviderEnabled(LocationManager.NETWORK_PROVIDER); 

      if (!isGPSEnabled && !isNetworkEnabled) { 
      } else { 
       this.canGetLocation = true; 
       if (isNetworkEnabled) { 
        locationManager.requestLocationUpdates(
          LocationManager.NETWORK_PROVIDER, 5000, 
          MIN_DISTANCE_CHANGE_FOR_UPDATES, this); 
        Log.d("Network", "Network"); 
        if (locationManager != null) { 
         location = locationManager 
           .getLastKnownLocation(LocationManager.NETWORK_PROVIDER); 
         if (location != null) { 
          latitude = location.getLatitude(); 
          longitude = location.getLongitude(); 
         } 
        } 
       } 
       // if GPS Enabled get lat/long using GPS Services 
       if (isGPSEnabled) { 
        if (location == null) { 
         locationManager.requestLocationUpdates(
           LocationManager.GPS_PROVIDER, 
           MIN_TIME_BW_UPDATES, 
           MIN_DISTANCE_CHANGE_FOR_UPDATES, this); 
         Log.d("GPS Enabled", "GPS Enabled"); 
         if (locationManager != null) { 
          location = locationManager 
            .getLastKnownLocation(LocationManager.GPS_PROVIDER); 
          if (location != null) { 
           latitude = location.getLatitude(); 
           longitude = location.getLongitude(); 
          } 
         } 
        } 
       } 
      } 

     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return location; 
    } 

    @Override 
    public void onLocationChanged(Location location) { 
     // this will be called every second 
     String laty = Double.toString(getLocation().getLatitude()); 
     String lagy = Double.toString(getLocation().getLongitude()); 
     db = new DBManager(mContext); 
     db.open(); 
     db.mInsertGPSCor(laty, lagy); 
     Toast.makeText(
       getApplicationContext(), 
       "Your Location is - \nLat: " + location.getLatitude() 
         + "\nLong: " + location.getLongitude(), 
       Toast.LENGTH_LONG).show(); 

     Toast.makeText(UpdateServices.this, "record entered", 
       Toast.LENGTH_SHORT).show(); 
     db.close(); 
// store in server 
     new CreateNewProduct(this).execute(); 

    } 

    @Override 
    public void onStatusChanged(String provider, int status, Bundle extras) { 

    } 

    @Override 
    public void onProviderEnabled(String provider) { 

    } 

    @Override 
    public void onProviderDisabled(String provider) { 

    } 

    class CreateNewProduct extends AsyncTask<String, String, String> { 
     private Context mContext; 

     public CreateNewProduct(Context context) { 
      super(); 
      mContext = context; 
     } 

     @Override 
     protected void onPreExecute() { 
      try { 
       super.onPreExecute(); 
       progressDialog = ProgressDialog.show(mContext, 
         "Press Back to Cancel", "Sending Data to Server..", 
         true, false); 
      } catch (Exception e) { 
       // TODO: handle exception 
      } 

     } 

     /** 
     * Creating product 
     * */ 
     protected String doInBackground(String... args) { 
      List<NameValuePair> params = new ArrayList<NameValuePair>(); 
      params.add(new BasicNameValuePair("ID", id)); 
      params.add(new BasicNameValuePair("LATITUDE", latee)); 
      params.add(new BasicNameValuePair("LONGITUDE", longee)); 

      JSONObject json = jsonParser.makeHttpRequest(
        url_create_locationupdate, "POST", params); 

      try { 
       int success = json.getInt(TAG_SUCCESS); 

       if (success == 1) { 

        return "done"; 
       } else { 
        // failed to create product 
        return "fail"; 
       } 
      } catch (JSONException e) { 
       e.printStackTrace(); 
       return "exec"; 
      } 

     } 

     /** 
     * After completing background task Dismiss the progress dialog 
     * **/ 
     protected void onPostExecute(String file_url) { 
      if (progressDialog.isShowing()) 
       progressDialog.dismiss(); 

      if (file_url.equalsIgnoreCase("done")) { 

       show.message(mContext, "uploading successed"); 
      } 
      if (file_url.equalsIgnoreCase("fail") 
        || file_url.equalsIgnoreCase("exec")) { 
       try { 
        show.message(mContext, "uploading failed"); 
       } catch (Exception e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

    public void onConnectionSuspended(int arg0) { 
     // TODO Auto-generated method stub 

    } 

} 

和Main.java

public class Main extends Activity { 

    Button btn_startGps, btn_stopGps; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     // TODO Auto-generated method stub 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.auto_gps_update); 
     btn_startGps = (Button) findViewById(R.id.button_service); 
     btn_stopGps = (Button) findViewById(R.id.button_stopservice); 


     btn_startGps.setOnClickListener(new View.OnClickListener() { 

      @Override 
      public void onClick(View v) {   
       startService(new Intent(About.this, UpdateServices.class)); 
       Toast.makeText(About.this, "Service Started", 
         Toast.LENGTH_SHORT).show(); 
      } 
     }); 

     btn_stopGps.setOnClickListener(new View.OnClickListener() { 

      @Override 
      public void onClick(View v) { 
       stopService(new Intent(About.this, UpdateServices.class)); 
      Log.e("sss", "ddddd"); 
       Toast.makeText(About.this, "Service Stopped", 
         Toast.LENGTH_SHORT).show(); 

      } 
     }); 
} 

但這裏的問題服務沒有停在這裏停止服務

,因爲我有返回

return START_STICKY; 

onStartCommand(...)

閱讀更多START_STICKY and START_NOT_STICKY

Official docs

1

如果您運行的API 21+的應用程序,使用被谷歌文檔中描述的也是最好的辦法JobScheduler。另外,如果不想更改代碼結構,即使屏幕關閉,也可以使用服務保持CPU處於開啓狀態。請閱讀Google文檔中的to keep CPU On。就在您的清單<uses-permission android:name="android.permission.WAKE_LOCK" />並在Service.onCreate添加權限,放在:

PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); 
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 
     "MyWakelockTag"); 
wakeLock.acquire(); 

而且在Service.onDestroywakelock.release()釋放。 但請注意,它會排出電池電量。但是,如果你說這個設備總是插上電源,我認爲這不會成爲問題。以防萬一,應用程序中的管理用戶界面將更好地手動停止服務。

1
In Manifest file, 
<service android:name=".MyService"></service> 

MyService。java的

public class MyService extends Service { 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     // your code here 
    } 
    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
     return START_STICKY; 
    } 
    @Override 
    public void onDestroy() { 
     super.onDestroy(); 
     Intent it = new Intent(MyService.this, MyService.class); 
     getApplication().startService(it); // If service will destroy, Start the service again 
    } 

    @Override 
    public IBinder onBind(Intent intent) { 
     // TODO Auto-generated method stub 
     return null; 
    } 


} 

運行的服務,它添加到您的活動,

Intent it = new Intent(getApplication(), MyService.class); 
getApplicationContext().startService(it); 
相關問題