Android- setPeriodic for JobScheduler won't work - android

I've wrote an app for creating and reminding task which uses local SqliteDatabase.
I wrote a jobScheduler service to check the device time and date with tasks on the database and if matches shows a push notification.
What I want also is service to run in background and check the data every 5 seconds .
but when I write
builder.setPeriodic(5000);
builder.setPersisted(true);
the service stops checking data.
Here's my code
MainActivity
public class MainActivity extends AppCompatActivity {
ImageButton plusImageBtn;
DatabaseHelper databaseHelper;
BottomNavigationView navigation;
Toolbar toolbar;
private ComponentName mServiceComponent;
private int jobId=0;
private static final String TAG = MainActivity.class.getSimpleName();
public static final String WORK_DURATION_KEY =
BuildConfig.APPLICATION_ID + ".WORK_DURATION_KEY";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
navigation = (BottomNavigationView) findViewById(R.id.navigation);
plusImageBtn = (ImageButton) findViewById(R.id.plusImagBtn);
toolbar= (Toolbar) findViewById(R.id.toolbar_main);
setSupportActionBar(toolbar);
mServiceComponent = new ComponentName(this, JobSchedulerService.class);
databaseHelper= new DatabaseHelper(this);
navigation.setOnNavigationItemSelectedListener
(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.navigation_calendar:
selectedFragment = new CalendarFragment();
break;
case R.id.navigation_home:
selectedFragment = new ViewListContents();
break;
}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_layout, selectedFragment);
transaction.commit();
return true;
}
});
FragmentTransaction transaction =
getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_layout,new ViewListContents());
transaction.commit();
scheduleJob();
}
#Override
protected void onStart() {
super.onStart();
// Start service and provide it a way to communicate with this class.
Intent startServiceIntent = new Intent(this, JobSchedulerService.class);
startService(startServiceIntent);
}
#Override
protected void onStop() {
stopService(new Intent(this,JobSchedulerService.class));
super.onStop();
}
public void scheduleJob() {
JobInfo.Builder builder = new JobInfo.Builder(jobId++, mServiceComponent);
// builder.setPeriodic(5000);
// builder.setPersisted(true);
builder.setMinimumLatency(1000);
builder.setOverrideDeadline(1000);
// Extras, work duration.
PersistableBundle extras = new PersistableBundle();
extras.putLong("",5000);
builder.setExtras(extras);
// Schedule job
Log.d(TAG, "Scheduling job");
JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.schedule(builder.build());
Toast.makeText(this, "Scheduling job ", Toast.LENGTH_SHORT).show();
}}
JobSchedulerService
public class JobSchedulerService extends JobService {
int id;
String stringId;
String date;
String taskDate,taskTitle,taskTime,sepYear,sepMonth,
sepDay,convert,DeviceDate;
Cursor taskDateCursor;
DatabaseHelper databaseHelper;
Roozh roozh;
String[] seperatedString;
int iYear,iMonth, iDay;
SimpleDateFormat dateFormat, timeFormat;
private static final String TAG = JobSchedulerService.class.getSimpleName();
#Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service created");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Service destroyed"); }
#Override
public boolean onStartJob(final JobParameters params) {
stringId = String.valueOf(params.getJobId());
id = params.getJobId();
final long duration = params.getExtras().getLong(WORK_DURATION_KEY);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
databaseHelper= new DatabaseHelper(getApplicationContext());
taskDateCursor=databaseHelper.getDateForNotification();
if (taskDateCursor.moveToFirst()){
do {
taskTitle=taskDateCursor.getString(taskDateCursor.getColumnIndex(DatabaseHelper.COL2));
taskDate=taskDateCursor.getString(taskDateCursor.getColumnIndex(DatabaseHelper.COL3));
taskTime=taskDateCursor.getString(taskDateCursor.getColumnIndex(DatabaseHelper.COL4));
roozh= new Roozh();
seperatedString=taskDate.split("/");
sepYear= seperatedString[0];
sepMonth= seperatedString[1];
sepDay= seperatedString[2].trim();
iYear= Integer.parseInt(sepYear);
iMonth= Integer.parseInt(sepMonth);
iDay= Integer.parseInt(sepDay);
roozh.PersianToGregorian(iYear,iMonth,iDay);
convert= roozh.toString();
dateFormat= new SimpleDateFormat(
"yyyy-MM-dd", Locale.getDefault());
DeviceDate= dateFormat.format(new Date());
timeFormat=new SimpleDateFormat("HH:m",Locale.getDefault());
String deviceTime=timeFormat.format(new Date());
RemoteViews remoteViews= new RemoteViews(getPackageName(),R.layout.notification);
remoteViews.setImageViewResource(R.id.notif_image, R.mipmap.ic_launcher);
remoteViews.setTextViewText(R.id.title,taskTitle);
remoteViews.setTextViewText(R.id.text,taskTime);
if (DeviceDate.equals(convert) && deviceTime.equals(taskTime) ){
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext())
.setSmallIcon(R.drawable.drawable_circle)
.setContent(remoteViews)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setColor(ContextCompat.getColor(getApplication(), R.color.primaryDarkColor))
.setShowWhen(true)
.setAutoCancel(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext());
notificationManager.notify(0, mBuilder.build());
}
Log.i(TAG, "data " + DeviceDate+ "task" + convert+ " " + "dd" + " "+ taskTime + "dt" + "" + deviceTime);
}while (taskDateCursor.moveToNext());
} }
}, duration);
Log.i(TAG, "on start job: " + params.getJobId());
return true; }
#Override
public boolean onStopJob(JobParameters params) {
Log.i(TAG, "on stop job: " + params.getJobId());
return true; }

I think your problem is the frequency you would like to execute your job. The minimum period for a job is 15 minutes in the standard AOSP. So it probably isn't the right API for you. Alarm Manager would probably be what you want, but setting an alarm for every 5 seconds is expensive. Google has also been restricting background services more and more with each release. Just something to keep in mind.
See:
JobScheduler not repeating job

setPeriodic(long intervalMillis) only works on devices lower than Nougat change it with this setPeriodic (long intervalMillis, long flexMillis) for devices having android version Nougat and above.

ScheduledThreadPoolExecutor is a better option.
Tasks can be scheduled by scheduleAtFixedRate or scheduleWithFixedDelay.
Reference:
https://developer.android.com/reference/kotlin/java/util/concurrent/ScheduledThreadPoolExecutor#schedule

Refer this answer of mine:
https://stackoverflow.com/a/60295377/9166855
The minimum period is 15minutes!

Related

How to stop IntentService Android

I have build an IntentService in Android.
So if I received a pushNotification message, I muse stopped this service.
public class DosimeterDataSensingService extends IntentService {
private static final String TAG = "DosimeterDataSensingService";
public static boolean isStarted = false;
private Context mContext;
private int mStatus;
private BluetoothDevice mDevice;
private BluetoothGatt mConnGatt;
private boolean notificationsEnabled;
private long dosimeterScanningTime;
private boolean isThreadStarted = false;
private List<DosimeterDataRequest.DataBean> dosimeterDataList;
private List<DosimeterDataRequest.DataBean> dosimeterDataListMqtt;
private DosimeterMqttParcel dosimeterMqttParcel = new DosimeterMqttParcel();
private DosimeterDataRequest dosimeterDataRequest;
private String macAddress;
private boolean isRecordingEnabled = false;
private String dose = "";
private int battery = -1;
private Boolean syncRadiactionWS = false;
private Boolean syncRadiactionMQTT = false;
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* #param name Used to name the worker thread, important only for debugging.
*/
public DosimeterDataSensingService(String name) {
super(name);
}
public DosimeterDataSensingService() {
super(null);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
Log.d("dosimetro", "ON HANDLE INTENT");
if(intent!=null){
String action = intent.getStringExtra(PreferenceHandler.ACTION_STOP);
Log.d("dosimetro", action!=null ? action : "no action");
if(action!=null && action.equals(PreferenceHandler.ACTION_STOP)){
Log.d("dosimetro", "fermo il servizio");
String syncScanTime = PreferenceHandler.readString(getApplicationContext(), PreferenceHandler.DOSIMETER_SCANNING_TIME, null);
Log.d("dosimetro", syncScanTime!=null ? syncScanTime : "nullo");
String syncRotTime = PreferenceHandler.readString(getApplicationContext(), PreferenceHandler.DOSIMETER_ROTATION_TIME, null);
Log.d("dosimetro", syncRotTime!=null ? syncRotTime : "nullo");
super.stopSelf();
Log.d("dosimetro", "ho stoppato");
onDestroy();
return;
}
}
}
#Override
public void onCreate() {
super.onCreate();
Paper.init(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = createNotificationChannel();
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, getString(R.string.default_notification_channel_id))
.setSmallIcon(R.drawable.logoxhdpi)
.setCategory(Notification.CATEGORY_SERVICE)
.setContentTitle("Cardio App")
.setContentText("Getting data from Dosimeter")
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
Notification notification = mBuilder.build();
startForeground((int) (System.currentTimeMillis() + 1), notification);
isStarted = true;
if (PreferenceHandler.readString(this, PreferenceHandler.TYPE_USER, null).equals("2")) {
checkDosage();
}
List<GetAssignedDevicesListResponse.Parameters> preferenzeParametri= Paper.book().read(PreferenceHandler.PARAMETRI_VITALI, new ArrayList<>());
if(preferenzeParametri!=null && preferenzeParametri.size()>0){
for (GetAssignedDevicesListResponse.Parameters p: preferenzeParametri) {
if(p.getIdParameter() == PreferenceHandler.ID_RADIACTION){
//VERIFICO COME L'RR DEVE ESSERE SINCRONIZZATO
syncRadiactionWS = p.getSyncWs()!=null ? p.getSyncWs() : false;
syncRadiactionMQTT = p.getSyncMqtt()!=null ? p.getSyncMqtt() : false;
Log.e("DOSIMETER", "syncRadiactionWS true");
}
}
}else{
Log.e("DOSIMETER", "paperi init false");
}
checkBattery();
Log.i("SCANNINGTIME", "SYNC WS STARTED");
syncRadiactionLevel();
syncMqtt();
}
#Override
public void onDestroy() {
Log.d("DOSIMETRO", "ondestroy");
isStarted = false;
disconnectDosimeter();
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#SuppressLint("LongLogTag")
public void disconnectDosimeter() {
if (mConnGatt != null) {
isThreadStarted = false;
if ((mStatus != BluetoothProfile.STATE_DISCONNECTING)
&& (mStatus != BluetoothProfile.STATE_DISCONNECTED)) {
mConnGatt.disconnect();
mConnGatt.close();
mConnGatt = null;
mStatus = BluetoothProfile.STATE_DISCONNECTED;
}
}
try {
Method m = mDevice.getClass().getMethod("removeBond", (Class[]) null);
m.invoke(mDevice, (Object[]) null);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
Log.v("Dosimeter", "isRecordingEnabled" + isRecordingEnabled);
Log.v("Dosimeter", "Disconnecteddd");
}
/**
* Enable recording of values
*/
private void startRecordingData() {
String dosimeterRotation = PreferenceHandler.readString(getApplicationContext(), PreferenceHandler.DOSIMETER_ROTATION_TIME, null);
Log.i("ROTATIONTIME", dosimeterRotation);
long dosimeterRotationTime = 0L;
if (dosimeterRotation != null) {
dosimeterRotationTime = Long.parseLong(dosimeterRotation);
new Handler().postDelayed(() -> {
isRecordingEnabled = true;
isThreadStarted = false;
}, dosimeterRotationTime);
}
}
}
To stop the service I m using this code:
Intent i = new Intent(this, DosimeterDataSensingService.class);
i.putExtra(PreferenceHandler.ACTION_STOP,PreferenceHandler.ACTION_STOP);
stopService(new Intent(this, DosimeterDataSensingService.class));
From my log I can see that the system call
super.stopSelf();
onDestroy();
method but the IntentService works always.
You need not call stopSelf() or stopService() for IntentService.
As per the description mentioned in Docs:
Because most of the started services don't need to handle multiple requests simultaneously (which can actually be a dangerous multi-threading scenario), it's best that you implement your service using the IntentService class.
The IntentService class does the following:
It creates a default worker thread that executes all of the intents that are delivered to onStartCommand(), separate from your application's main thread.
Creates a work queue that passes one intent at a time to your onHandleIntent() implementation, so you never have to worry about multi-threading.
Stops the service after all of the start requests are handled, **so you never have to call stopSelf().**
Provides a default implementation of onBind() that returns null.
Provides a default implementation of onStartCommand() that sends the intent to the work queue and then to your onHandleIntent() implementation.
If the service is still running may be some intents are running.
Hope this helps.

Handle consecutive events with job scheduler

My App must be aware of taking picture by any camera apps on user device and do some progress on that picture.
On API < 24 i used a broadcast receiver which catches com.android.camera.NEW_PICTURE events and it works fine.
But according to this document to achieve that goal in API >= 24 we must use Job-Scheduler instead.
I used this code and it works fine for one picture in one job execution time. but when i take consecutive pictures job scheduler only handles first one.
#TargetApi(Build.VERSION_CODES.N)
public class CameraJobService extends JobService
{
private static final String TAG = "CameraJobService";
#Override
public boolean onStartJob(final JobParameters params)
{
Log.i(TAG, "onStartJob: " + this);
// do in background thread
new Thread(() -> {
doJob(params);
// mark the job as 'finished'
jobFinished(params, false);
// create a new job
prepareJob(getApplicationContext());
}).start();
// mark the job as 'on processing'
return true;
}
#Override
public boolean onStopJob(JobParameters params)
{
Log.i(TAG, "onStopJob: " + this);
// reschedule job if it was terminated by os
return true;
}
private void doJob(JobParameters params)
{
if (params.getTriggeredContentAuthorities() != null && params.getTriggeredContentUris() != null)
{
//some job here
Log.d(TAG, "doJob: ");
}
}
#TargetApi(Build.VERSION_CODES.N)
public static void prepareJob(Context context)
{
Log.i(TAG, "prepareJob");
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(1001,
new ComponentName(context, CameraJobService.class));
builder.addTriggerContentUri(
new JobInfo.TriggerContentUri(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
if (scheduler != null)
{
scheduler.schedule(builder.build());
}
}
}
I initialized my JobService and BroadcastReceiver Here in MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// use job scheduler
CameraJobService.prepareJob(this);
} else {
// use broadcast receiver
CameraBroadcastReceiver.register(this);
}
}
I think this problem happens because one job is in progress now and we can't start new one before current job be complete. but is there any way to handle this problem?

Update view when a push notification is received

I would like to update a view from within an open activity when the device receives a push notification.
When a push notification is received the updateBalance function is executed,
a mysql database is queried and an amount is returned.
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private void updateBalance(String messageBody) {
h1 = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
bb = msg.getData();
String str = bb.getString("result");
Log.d(TAG,str);
Message msg=handler.obtainMessage()
}
};
t = new Thread(new MyRunnable(h1));
t.start();
try {
t.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
I have another class
public class MyRunnable implements Runnable {
private Handler h2;
public MyRunnable(Handler h) {
this.h2 = h;
}
#Override
public void run() {
String name = "w12";
BalanceActivity NB = new BalanceActivity(name);
Message m = Message.obtain();
Bundle b = new Bundle();
b.putString("result", "10");
m.setData(b);
h2.sendMessage(m);
}
}
I have a MainActivity that I would like to update after the amount is returned. How would I do this possibly with another Handler and Runnable.
public class MainActivity extends Activity {
TextView TV = (TextView) findViewById(package.name.R.id.Balance);
}
Try to check your activity is currently in foreground. if yes then create method where you can update your view.
public static boolean isServiceRunning(Context context) {
Log.i(TAG, "Checking if service is running");
ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
boolean isServiceFound = false;
for (int i = 0; i < services.size(); i++) {
if (Constants.PACKAGE.equals(services.get(i).service.getPackageName())){
if (Constants.BACKGROUND_SERVICE_CLASS.equals(services.get(i).service.getClassName())){
isServiceFound = true;
}
}
}
Log.i(TAG, "Service was" + (isServiceFound ? "" : " not") + " running");
return isServiceFound;
}
Make use of Broadcast Receivers. Register local broadcast receiver in activity. Broadcast data when notification received.

How to stop a thread in a service

Today I have a problem in my Android project. I use a Service with a thread in it to log location information in a period of 10s. However, when I change the screen orientation (Portrait -> Landscape), the period just messed up.
I think I may run another thread so that I got one more thread running behind once I rotate the screen. I have print log messages and it seems my guessing is right.
Here is my code:
public class LocationService extends Service
{
public Location loc;
public LocationService()
{
}
#Override
public int onStartCommand(Intent intent, int flags, int id)
{
Thread thread = new Thread(new Runnable()
{
#Override
public void run()
{
LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
if ( ContextCompat.checkSelfPermission(LocationService.this, android.Manifest.permission.ACCESS_COARSE_LOCATION ) == PackageManager.PERMISSION_GRANTED )
{
loc = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if(loc == null) // fall back to network if GPS is not available
{
loc = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
}
if(loc != null)
{
Timer timer = new Timer();
final String time = (new Date()).toString();
timer.scheduleAtFixedRate(new TimerTask()
{
#Override
public void run()
{
Log.d(time, "hehe");
double currentLat = loc.getLatitude();
double currentLng = loc.getLongitude();
Intent done = new Intent();
done.setAction("location");
done.putExtra("currentLat", currentLat);
done.putExtra("currentLng", currentLng);
sendBroadcast(done);
//Toast.makeText(LocationService.this, String.valueOf(currentLat) + String.valueOf(currentlng), Toast.LENGTH_LONG).show();
}
}, 10000, 10000);
}
}
else
{
Toast.makeText(LocationService.this, "Please allow app to access your location", Toast.LENGTH_LONG).show();
}
}
});
thread.start();
return START_STICKY; // stay running
}
#Override
public IBinder onBind(Intent intent)
{
// // TODO: Return the communication channel to the service.
// throw new UnsupportedOperationException("Not yet implemented");
return null;
}
// #Override
// public void onDestroy()
// {
// Log.d("hehe","onDestroy");
// super.onDestroy();
// }
}
Here is the code for activity:
public class MainActivity extends AppCompatActivity
{
private Toolbar toolbar;
private FragmentManager fragmentManager;
private LocalFragment localFragment;
private ServerFragment serverFragment;
private QueryFragment queryFragment;
private FragmentTransaction transaction;
public SQLiteHelper dbHelper;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new SQLiteHelper(this);
//garb handlers
fragmentManager = getFragmentManager();
localFragment = (LocalFragment) fragmentManager.findFragmentById(R.id.fragment_local);
serverFragment = (ServerFragment) fragmentManager.findFragmentById(R.id.fragment_server);
queryFragment = (QueryFragment) fragmentManager.findFragmentById(R.id.fragment_query);
// initial visibility
transaction = fragmentManager.beginTransaction();
if(this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
{
// toolbar (must be put in checking orientation because landscape layout does not have toolbar here)
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(R.string.toolbar_title);
setSupportActionBar(toolbar);
transaction.show(localFragment);
transaction.hide(serverFragment);
transaction.hide(queryFragment);
transaction.commit();
}
else // ORIENTATION_LANDSCAPE
{
transaction.hide(queryFragment); // landscape orientation does not need query function (?)
transaction.show(localFragment);
transaction.show(serverFragment);
transaction.commit();
}
// register network status receiver
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
NetworkStatusReceiver myNetworkReceiver = new NetworkStatusReceiver();
registerReceiver(myNetworkReceiver, intentFilter);
// start location service
Intent intent = new Intent(this, LocationService.class);
intent.setAction("location");
startService(intent);
// register location receiver
IntentFilter intentFilterLocation = new IntentFilter();
intentFilterLocation.addAction("location");
LocationReceiver myLocationReceiver = new LocationReceiver();
registerReceiver(myLocationReceiver, intentFilterLocation);
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_main_activity, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
transaction = fragmentManager.beginTransaction();
// handle click event
if(item.getItemId() == R.id.action_online)
{
transaction.hide(localFragment);
transaction.hide(queryFragment);
transaction.show(serverFragment);
transaction.commit();
}
else if(item.getItemId() == R.id.action_offline)
{
transaction.hide(serverFragment);
transaction.hide(queryFragment);
transaction.show(localFragment);
transaction.commit();
}
else // Query
{
transaction.hide(localFragment);
transaction.hide(serverFragment);
transaction.show(queryFragment);
transaction.commit();
}
return super.onOptionsItemSelected(item);
}
// receiver for network change action
private class NetworkStatusReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) // if receive network change event broadcast
{
Toast.makeText(context, "Network status changed!", Toast.LENGTH_LONG).show();
// why I cannot use another thread to do so? CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
// Thread thread = new Thread(new Runnable()
// {
// #Override
// public void run()
// {
int type = 0;
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); // get current network type
if (networkInfo != null && networkInfo.isAvailable())
{
type = networkInfo.getType();
String typeName = networkInfo.getTypeName(); // (?)
//serverFragment = (ServerFragment)fragmentManager.findFragmentById(R.id.fragment_server);
if (type == ConnectivityManager.TYPE_WIFI) // wifi
{
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
Log.d("wifiInfo", wifiInfo.toString());
Log.d("SSID",wifiInfo.getSSID());
serverFragment.setNetworkStatusText("WIFI: " + wifiInfo.getSSID()); // thread issues here. WTF
} else if (type == ConnectivityManager.TYPE_MOBILE) // Cellar
{
serverFragment.setNetworkStatusText("Mobile Data");
} else // no network
{
serverFragment.setNetworkStatusText("No Network");
}
} else // no network
{
serverFragment.setNetworkStatusText("No Network");
}
// }
// });
// thread.start();
}
}
}
// receiver for location per 10s
public class LocationReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if(action.equals("location"))
{
double currentLat = intent.getDoubleExtra("currentLat", 0.0);
double currentLng = intent.getDoubleExtra("currentLng", 0.0);
Date now = new Date();
localFragment.addNewLocation(now, currentLat, currentLng); // add to list for listview
// TODO: 11/5/16 implement SQLite insert
dbHelper.addEntry(now, currentLat, currentLng);
Toast.makeText(MainActivity.this, "Current Location: " + String.valueOf(currentLat) + ", " + String.valueOf(currentLng), Toast.LENGTH_LONG).show();
}
}
}
public void setNetworkStatus(String networkStatus)
{
serverFragment.setNetworkStatusText(networkStatus);
}
}
My Question are:
It seems that the onDestroy () method would not be called when rotate the screen?
A new Service will not be created when rotate the screen?
How can I stop the thread created previously? Or what is the best way to handle this problem?
It seems that the onDestroy() method would not be called when rotate the screen?
No, it isn't. Changing the screen orientation kills the Activity and creates a new one, but your Service is still running. Screen orientation has no effect on this.
A new Service will not be created when rotate the screen?
No. A Service is essentially a singleton. Android will not create a new instance of your Service if there is already one running. However, onStartCommand() will be called again because your Activity calls startService() when it is created.
How can I stop the thread created previously? Or what is the best way to handle this problem?
The easiest way to deal with this is to check in onStartCommand() if your thread is already running. If so, you don't need to start it again. Save a reference to your Thread in a member variable (a field) in your Service and call isAlive() on it to see if it is running.
Also, in onDestroy() you should make sure that your Thread shuts down, otherwise it will continue to run even after your Service is dead. To do that you should create a boolean member variable (field) in the Thread, which you check in each loop. In onDestroy() of your Service, set that boolean so that the Thread exits.

Google Play Service Activity recognition start and stops itself constantly

I need to call the Google activity recognition service through a service (not activity) and run it in the background, of course when the user starts the app, which has an activity (But the service does not called directly from activity).
Therefore I have created a service class (ActivitySensor) and another class (ActivityRecognitionScan).
When I install the app on my Galaxy Nexus S device, the service starts calling onCreate and onDestroy automatically. Even without doing anything in the GUI
It is very strange behaviour. Does anybody has the same experience or solution for it?
I mean I get something as follows in the debug console:
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
...
Here are my two classes:
public class ActivitySensor extends IntentService {
private ActivityRecognitionScan myascan;
private Intent inIntent;
private static long ACTIVITY_LOG_INTERVAL = 30000L;
private static JsonEncodeDecode jsonencoder = new JsonEncodeDecode();
public ActivitySensor() {
super("ActivitySensor");
}
#Override
public void onCreate(){
super.onCreate();
Log.d("Activity-Logging", "--- onCreate");
try {
myascan = new ActivityRecognitionScan(getApplicationContext());
myascan.startActivityRecognitionScan();
} catch (Exception e) {
Log.e("[Activity-Logging]","----------Error:"+e.getLocalizedMessage());
e.printStackTrace();
}
}
#Override
public void readSensor() {
// Log.e("Activity-Logging", "ActivityRecognitionResult.hasResult: "+String.valueOf(ActivityRecognitionResult.hasResult(inIntent)));
if (ActivityRecognitionResult.hasResult(inIntent)) {
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(inIntent);
DetectedActivity activity = result.getMostProbableActivity();
final int type = activity.getType();
String strType = new String();
switch(type){
case DetectedActivity.IN_VEHICLE:
strType = "invehicle";
break;
case DetectedActivity.ON_BICYCLE:
strType ="onbicycle";
break;
case DetectedActivity.ON_FOOT:
strType = "onfoot";
break;
case DetectedActivity.STILL:
strType = "still";
break;
case DetectedActivity.TILTING:
strType ="tilting";
break;
case DetectedActivity.UNKNOWN:
strType ="unknown";
break;
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
Editor edt = prefs.edit();
String previousActv = prefs.getString("PREVIOUS_ACTIVIY","");
long previousDate = prefs.getLong("PREVIOUS_DATE", 0);
if (previousActv.length()==0){ // nothing was in the string and it is the first time just initialize
previousActv = strType;
previousDate = new Date().getTime();
// Log.e("-----FIRST TIME: type:", previousActv+" date:"+String.valueOf(previousDate));
edt.putString("PREVIOUS_ACTIVIY", strType);
edt.putLong("PREVIOUS_DATE", previousDate);
edt.commit();
}else {
if (!strType.equalsIgnoreCase(previousActv)){
Date readablePrevDate = new Date(previousDate);
Date nowDate = new Date();
String jsonstr = jsonencoder.EncodeActivity("Activity", readablePrevDate, nowDate, strType, activity.getConfidence());
// Log.e("[Activity-Logging] ----->",jsonstr);
edt.putString("PREVIOUS_ACTIVIY", strType);
edt.putLong("PREVIOUS_DATE", nowDate.getTime());
edt.commit();
DataAcquisitor.dataBuff.add(jsonstr);
}
}
}
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d("Activity-Logging", "--- onHandleIntent"+ "---"+intent.getAction());
intent.putExtra("LOG_INTERVAL",ACTIVITY_LOG_INTERVAL );
intent.putExtra("STOP",false);
inIntent = intent;
readSensor();
}
#Override
public void onDestroy(){
Log.d("Activity-Logging", "--- onDestroy");
myascan.stopActivityRecognitionScan();
myascan=null;
//super.onDestroy();
}
}
This is the class that calls the Google Activity Recognition Service:
ActivityRecognitionScan implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener {
private Context ctx;
private static final String TAG = "ActivityRecognition";
private static ActivityRecognitionClient actrecClient;
private static PendingIntent callbackIntent;
private long ACTIVITY_LOG_INTERVAL=30000;
public ActivityRecognitionScan(Context context) {
ctx=context;
}
public void startActivityRecognitionScan(){
int resp = GooglePlayServicesUtil.isGooglePlayServicesAvailable(ctx);
if(resp == ConnectionResult.SUCCESS){
actrecClient = new ActivityRecognitionClient(ctx, this, this);
if (!actrecClient.isConnected()){
actrecClient.connect();
} else{
Log.e("ActivityRecognitionScan"," ---Activity recognition client is already connected");
}
}else{
Log.e("[Activity-Logging]", "Google Play Service hasn't installed");
}
}
public void stopActivityRecognitionScan(){
try{
if (actrecClient.isConnected() || actrecClient.isConnecting() ){
actrecClient.removeActivityUpdates(callbackIntent);
actrecClient.disconnect();
}
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.e("[ActivityRecognitionScan]", "Connection Failed");
}
#Override
public void onConnected(Bundle connectionHint) {
try{
Intent intent = new Intent(ctx, ActivitySensor.class);
Bundle bundle = intent.getExtras();
callbackIntent = PendingIntent.getService(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
long interval = 5000;
if ( null!= bundle && bundle.containsKey("LOG_INTERVAL") ){
interval = bundle.getLong("LOG_INTERVAL");
}
actrecClient.requestActivityUpdates(interval, callbackIntent);
actrecClient.disconnect();
}catch(Exception ex){
Log.e("[Activity-Logging]","Error in requesting Activity update "+ex.getMessage());
ex.printStackTrace();
}
}
#Override
public void onDisconnected() {
callbackIntent.cancel();
actrecClient = null;
Log.e("[ActivityRecognitionScan]","---onDisconnected");
}
}
IntentService automatically stops itself on completion of onHandleIntent as per the source code (see ServiceHandler.handleMessage()) as per the description of an IntentService:
Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
Use a Service if you want it to run continuously in the background.
You have 2 issues with your code that is causing the problem you are experiencing.
When activity is detected, the pending intent that is called calls (and creates, since it is an IntentService) ActivitySensor. The onCreate will connect another ActivityRecognitionClient, which is unnecessary. This causes another activity to be detected which causes your logging loop.
You should separate the creation of the ActivityRecognitionClient from the handling of the detected activity. You don't need to keep recreating it as subsequent detections will use the same PendingIntent. This will prevent the logging loop.

Categories

Resources