我们一直致力于为Android平台开发服务。
在我们的服务中,我们需要每隔一分钟将设备的GPS数据(Lat和Long)发送到某些外部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 :(得分:4)
您正在Activity
获取唤醒锁定。这里的问题是当设备被锁定时,您的Activity
会被推到后台。在15分钟不活动之后,Android正在简单地终止进程。这会释放唤醒锁定。设备进入睡眠状态。
现在,下次闹钟响起时,设备会唤醒,BroadcastReceiver
被触发,onReceive()
被调用,它会启动Service
,但设备会返回因为没有唤醒锁定而睡觉,所以“服务不做任何事情。”
另一种方法是,如果您想在应用程序运行时阻止手机进入睡眠状态,则可以获取Service
中的唤醒锁定。在这种情况下,您不希望每次stopSelf()
运行时都致电Runnable
。您希望让Service
保持运行直至您想要停止它,此时您可以拨打stopService()
。这样,Service
将始终处于活动状态(即使它没有做任何事情),它会阻止设备通过唤醒锁休眠。但是,这可能会对电池造成不可接受的消耗(您必须对其进行测试)。
您需要在BroadcastReceiver
中获取唤醒锁,并确保Service
启动并在设备恢复睡眠之前获取唤醒锁定。查看WakefulBroadcastReceiver
,您可以使用它来实现此行为。
答案 1 :(得分:4)
一种方法可能是您依赖AlarmManager:一旦您订阅了AlarmManager,系统本身就会按照您设置的时间间隔运行您的代码,即使您的应用程序未处于活动状态。每次运行时,您都可以决定处理一些代码......这样您就可以完全避免使服务保持活跃状态。
您需要的是一个将处理AlarmManager意图的Alarm类。
创建闹钟:
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);
}
}
在你的Manifest中声明这个Alarm BroadcastReceiver
<receiver
android:name=".utils.Alarm"
android:process=":remote" >
</receiver>
在你的Activity中你想要的地方调用这个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);
这是主要的想法。 现在你需要打开或关闭屏幕。 对于这两个解决方案:您可以注册设备屏幕状态意图并管理AlarmManager打开/关闭...或者您可以让AlarmManager始终运行但在发送数据之前检查设备是否打开/关闭...
希望这会有所帮助!
答案 2 :(得分: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 +
如果这是您想要的解决方案,请接受此答案。这也可以帮助其他开发人员。
答案 3 :(得分:1)
是的,你可以实现几乎永远不会被杀死的后台服务。但是你必须声明它在前台运行。您可以在本文(http://developer.android.com/guide/components/services.html)中引用此网址(http://developer.android.com/guide/components/processes-and-threads.html)来了解Android开发者网站所说的内容,
重要性层次结构中有五个级别,按重要性顺序排列不同类型的流程(第一个流程最重要,最后被杀死):
用户当前正在执行的操作所需的过程。如果满足以下任一条件,则认为进程处于前台:
通常,在任何给定时间只存在少数前台进程。它们只是作为最后的手段而被杀死 - 如果内存太低而无法继续运行。通常,此时设备已达到内存分页状态,因此需要终止某些前台进程以保持用户界面的响应。
所以你必须在前台开始服务。为此,您必须实施以下服务。
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);
}
}
谢谢你,好运......
答案 4 :(得分:1)
当服务完成运行时,您必须反复触发警报。
此外,您还可以实现在设备启动时启动服务的BroadCastReceiver。
查看本教程: http://ncona.com/2014/04/schedule-your-android-app-to-do-something-periodically/
答案 5 :(得分: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(...)
中的了解详情
答案 6 :(得分:1)
如果您在API 21+上运行应用,则使用Google文档中描述的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.onDestroy
在wakelock.release()
中发布。
但要注意它会耗尽你的电池。但如果你说该设备将始终插入源电源,我认为这不会是一个问题。为了以防万一,最好在应用程序中设置管理UI以手动停止服务。
答案 7 :(得分: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);