I am working with an application where i am creating a service which do some work in background, this service gets stop when i clear my application from recent open applications. I want my service to be run in background even if I clear the application. I am testing my application with Xiaomi Mi4i device.
This is my service class
public class LocalNotificationService extends Service {
private int i = 1;
public static final String TAG = LocalNotificationService.class.getSimpleName();
private static long UPDATE_INTERVAL = 1 * 5 * 1000; //default
private static Timer timer = new Timer();
private static final String TIME_FORMAT_LOCAL_NOTIFICATION = "HH:mm";
private boolean isNotificationFired = false;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
_startService();
startForeground(1,new Notification());
Log.v(TAG, "service created....");
}
private void _startService() {
timer.scheduleAtFixedRate(
new TimerTask() {
public void run() {
doServiceWork();
}
}, 1000, UPDATE_INTERVAL);
}
private void doServiceWork() {
Log.v(TAG, "service working....");
try {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
String dateString = null;
try {
String format = "yyyy-MM-dd";
final SimpleDateFormat sdf = new SimpleDateFormat(format);
dateString = sdf.format(new Date()) + "T00:00:00";
Log.v(TAG, dateString);
} catch (Exception e) {
e.printStackTrace();
}
List<AppointmentModel> list = new RushSearch().whereEqual("Date", dateString).find(AppointmentModel.class);
if (list != null & list.size() > 0) {
for (AppointmentModel model : list) {
try {
if (model.Status.equalsIgnoreCase("Confirmed")) {
SimpleDateFormat sdf = new SimpleDateFormat(LocalNotificationService.TIME_FORMAT_LOCAL_NOTIFICATION);
Date currentTime = sdf.parse(sdf.format(Calendar.getInstance().getTime()));
String From = DateAndTimeUtil.getTimeLocale_HHmm(model.FromTime);
Date FromTime = sdf.parse(From);
long difference = currentTime.getTime() - FromTime.getTime();
int days = (int) (difference / (1000 * 60 * 60 * 24));
int hours = (int) ((difference - (1000 * 60 * 60 * 24 * days)) / (1000 * 60 * 60));
int min = (int) (difference - (1000 * 60 * 60 * 24 * days) - (1000 * 60 * 60 * hours)) / (1000 * 60);
hours = (hours < 0 ? -hours : hours);
Log.v("======= days", " :: " + days);
Log.v("======= hours", " :: " + hours);
Log.v("======= min", " :: " + min);
switch (min){
case -15: {
if(!isNotificationFired){
Bundle bundle = new Bundle();
bundle.putString(ArkaaNotificationHandler.NOTIFICATION_KEY_FROM, "Appointment Reminder");
bundle.putString(ArkaaNotificationHandler.NOTIFICATION_KEY_TITLE, "Appointment Reminder");
bundle.putString(ArkaaNotificationHandler.NOTIFICATION_KEY_MESSAGE, "You Have Appointment With " + model.Doctor.Name + "At " + DateAndTimeUtil.getTimeLocale_HHmmaa(From));
bundle.putString(ArkaaNotificationHandler.NOTIFICATION_KEY_COLLAPSE_KEY, "");
ArkaaNotificationHandler.getInstance(LocalNotificationService.this).createSimpleNotification(LocalNotificationService.this, bundle);
isNotificationFired = true;
}
break;
}
case -5: {
if(!isNotificationFired){
Bundle bundle = new Bundle();
bundle.putString(ArkaaNotificationHandler.NOTIFICATION_KEY_FROM, "");
bundle.putString(ArkaaNotificationHandler.NOTIFICATION_KEY_COLLAPSE_KEY, "");
bundle.putString(ArkaaNotificationHandler.NOTIFICATION_KEY_MESSAGE, "You Have Appointment With" + model.Doctor.Name + "At" + DateAndTimeUtil.getTimeLocale_HHmmaa(From));
if(NetworkUtils.getNetworkClass(ArkaaApplicationClass.getInstance().getBaseContext()).equalsIgnoreCase("2G")){
bundle.putString(ArkaaNotificationHandler.NOTIFICATION_KEY_TITLE, "For Better Call Experience Please Switch To High BandWidth Network ");
}else{
bundle.putString(ArkaaNotificationHandler.NOTIFICATION_KEY_TITLE, "Appointment Reminder");
}
ArkaaNotificationHandler.getInstance(LocalNotificationService.this).createSimpleNotification(LocalNotificationService.this, bundle);
isNotificationFired = true;
}
}
case -4:
case -14:
isNotificationFired = false;
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
return null;
}
}.execute();
} catch (Exception e) {
Log.v(TAG, e.toString());
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
stopSelf();
}
private void _shutdownService() {
if (timer != null) timer.cancel();
Log.i(getClass().getSimpleName(), "Timer stopped...");
}
#Override
public void onDestroy() {
super.onDestroy();
_shutdownService();
Log.v(TAG, "service destroyed.....");
// if (MAIN_ACTIVITY != null) Log.d(getClass().getSimpleName(), "FileScannerService stopped");
}
}
In your Activity, do the following to start your service. For example in onCreate():
Intent intent = new Intent(this, MyService.class);
startService(intent);
In your Service, do the following in onCreate():
int NOTIFICATION_ID = 2707;
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
Uri sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
builder.setSmallIcon(android.R.drawable.ic_menu_info_details)
.setContentTitle("MyAppName")
.setContentText("the service is running!")
.setTicker("the service is running!")
.setOnlyAlertOnce(true)
.setOngoing(true)
.setSound(alarmSound);
Notification notification = builder.build();
startForeground(NOTIFICATION_ID, notification);
Just hide stopSelf(); and _shutdownService(); or try WakefulBroadcastReceiver method
https://developer.android.com/reference/android/support/v4/content/WakefulBroadcastReceiver.html
Related
I have a service which looks for beacons when the user enters any building based on the location it will return back valid beacon regions. The BeaconRanger service is then triggered and should start scanning for the beacons in that region. This should work for both background and foreground state of the application. It also updates time intervals based on the app foreground state and background state.
Update scan periods in background: can find code in updateScanPeriods()
private static Long BACKGROUND_SCAN_PERIOD_MEDIUM = 10 * 1000l; // 10 sec
private static Long FOREGROUND_SCAN_PERIOD_MEDIUM = 1100l; // 1.1 sec
private static Long BACKGROUND_BETWEEN_SCAN_PERIOD_MEDIUM = 10 * 60 * 1000l; // 10 min
private static Long FOREGROUND_BETWEEN_SCAN_PERIOD_MEDIUM = 5 * 1000l; // 5 sec
Update scan periods in foreground: can find code in updateScanPeriods()
private static Long BACKGROUND_SCAN_PERIOD_LOW = 1100l; // 1.1 sec
private static Long FOREGROUND_SCAN_PERIOD_LOW = 1100l; // 1.1 sec
private static Long BACKGROUND_BETWEEN_SCAN_PERIOD_LOW = 4 * 1000l; // 5 sec
private static Long FOREGROUND_BETWEEN_SCAN_PERIOD_LOW = 3 * 1000l; // 3 sec
Set background mode: can find code in public void onEventMainThread(BeaconRangerBackgroundSetting beaconRangerBackgroundSetting)
if (beaconRangerBackgroundSetting.status) {
if (beaconManager != null && beaconManager.isBound(this))
beaconManager.setBackgroundMode(true);
} else {
if (beaconManager != null && beaconManager.isBound(this))
beaconManager.setBackgroundMode(false);
}
I am able to get the valid regions in background state and pass it to BeaconRanger class but the ranged beacons list is always empty from the Altbeacon manager unless I open the app again. I implemented the RegionBootstrap/ BootstrapNotifier for background startup but the result is the same.
I wanted to know if something is wrong with my implementation?
public class BeaconRanger extends Service implements BeaconConsumer, BootstrapNotifier {
private static final String TAG = "ArubaBeacons";
private static final int MAX_INTERVAL = 60 * 1000; // 1 min
private static final int MIN_INTERVAL = 25 * 1000; // 25 sec
private static final int MAX_RETRY_TIME = 2 * 60 * 1000;
private static final int PERMISSION_REQUEST_STORAGE = 1;
private static Long BACKGROUND_SCAN_PERIOD_MEDIUM = 10 * 1000l; // 10 sec
private static Long FOREGROUND_SCAN_PERIOD_MEDIUM = 1100l; // 1.1 sec
private static Long BACKGROUND_BETWEEN_SCAN_PERIOD_MEDIUM = 10 * 60 * 1000l; // 10 min
private static Long FOREGROUND_BETWEEN_SCAN_PERIOD_MEDIUM = 5 * 1000l; // 5 sec
private static Long BACKGROUND_SCAN_PERIOD_LOW = 1100l; // 1.1 sec
private static Long FOREGROUND_SCAN_PERIOD_LOW = 1100l; // 1.1 sec
private static Long BACKGROUND_BETWEEN_SCAN_PERIOD_LOW = 4 * 1000l; // 5 sec
private static Long FOREGROUND_BETWEEN_SCAN_PERIOD_LOW = 3 * 1000l; // 3 sec
private ArrayList<Model.beacon> beaconList = new ArrayList<Model.beacon>();
private ArrayList<Region> regions = new ArrayList<>();
private ArrayList<Region> lastRangedRegions = new ArrayList<>();
private ArrayList<Region> rangedRegions = new ArrayList<>();
private BeaconManager beaconManager;
private BeaconApplication mApp;
private long mLastRangeTime = 0;
private long mLastPostedTime = 0;
private long mLastRetryTime = 0;
private int retryCount = 0;
private EventBus mBus = EventBus.getDefault();
private ArrayList<String> regionIds = new ArrayList<>();
private ArrayList<Model.beacon> mLastRangedBeacons = new ArrayList<>();
private ArrayList<Model.beacon> mLastUpdatedBeacons = new ArrayList<>();
private RegionBootstrap regionBootstrap;
private int mLastBeaconSetting = -1;
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
try {
mApp = BeaconApplication.mInstance;
mApp.mBeaconRanger = this;
mBus.registerSticky(this);
new BackgroundPowerSaver(this);
beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.getBeaconParsers().add(new BeaconParser()
.setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));
// set the duration of the scan
updateScanPeriods();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setForeground();
}
beaconManager.bind(this);
BeaconManager.setDebug(false);
} catch (Exception e) {
Timber.e("Exception in onCreate: " + e.getMessage());
}
}
#RequiresApi(api = Build.VERSION_CODES.O)
private void setForeground() {
try {
String NOTIFICATION_CHANNEL_ID = "location_service_notifications";
String channelName = "Location Service Notifications";
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_LOW);
notificationChannel.setLightColor(Color.BLUE);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(notificationChannel);
Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID);
builder.setSmallIcon(R.drawable.ic_location);
builder.setContentTitle(getString(R.string.arcangel_location_beacon_notification_title));
builder.setContentText(getString(R.string.arcangel_location_beacon_notification_text));
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())
.putExtra(Settings.EXTRA_CHANNEL_ID, notificationChannel.getId());
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
);
builder.setContentIntent(pendingIntent);
int v = beaconManager.getForegroundServiceNotificationId();
startForeground(2, builder.build());
if (beaconManager.getForegroundServiceNotificationId() < 0) {
beaconManager.enableForegroundServiceScanning(builder.build(), 2);
beaconManager.setEnableScheduledScanJobs(false);
updateScanPeriods();
}
Timber.d("started Foreground");
} catch (Exception e) {
Timber.e("Exception in setForeground: " + e.getMessage());
}
}
private void updateScanPeriods() {
int mCurrentBeaconSetting;
Long backgroundScanPeriod;
Long foregroundScanPeriod;
Long backgroundBetweenScanPeriod;
Long foregroundBetweenScanPeriod;
if ((mApp.isAppVisible()) { // app in the foreground
mCurrentBeaconSetting = 0;
backgroundScanPeriod = BACKGROUND_SCAN_PERIOD_LOW;
foregroundScanPeriod = FOREGROUND_SCAN_PERIOD_LOW;
backgroundBetweenScanPeriod = BACKGROUND_BETWEEN_SCAN_PERIOD_LOW;
foregroundBetweenScanPeriod = FOREGROUND_BETWEEN_SCAN_PERIOD_LOW;
} else { // app in the background
mCurrentBeaconSetting = 1;
backgroundScanPeriod = BACKGROUND_SCAN_PERIOD_MEDIUM;
foregroundScanPeriod = FOREGROUND_SCAN_PERIOD_MEDIUM;
backgroundBetweenScanPeriod = BACKGROUND_BETWEEN_SCAN_PERIOD_MEDIUM;
foregroundBetweenScanPeriod = FOREGROUND_BETWEEN_SCAN_PERIOD_MEDIUM;
}
if (mLastBeaconSetting != mCurrentBeaconSetting && beaconManager != null) {
beaconManager.setBackgroundScanPeriod(backgroundScanPeriod);
beaconManager.setForegroundScanPeriod(foregroundScanPeriod);
beaconManager.setBackgroundBetweenScanPeriod(backgroundBetweenScanPeriod); // callback time between scans background
beaconManager.setForegroundBetweenScanPeriod(foregroundBetweenScanPeriod); // callback time between scans foreground
mLastBeaconSetting = mCurrentBeaconSetting;
try {
beaconManager.updateScanPeriods();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void checkRangedRegionsValidity() {
if (regionIds.size() <= 0) {
beaconList.clear();
for (Region duplicateRegion : beaconManager.getRangedRegions()) {
try {
beaconManager.stopRangingBeaconsInRegion(duplicateRegion);
beaconManager.stopMonitoringBeaconsInRegion(duplicateRegion);
regionBootstrap.removeRegion(duplicateRegion);
} catch (Exception e) {
e.printStackTrace();
}
}
mLastRangedBeacons.clear();
}
}
// called by a different service, will give a valid region based on certain parameters like inside an building or location. If no condition is satisfies gives an empty array
public void setBeaconRegionIds(ArrayList<String> regionIds) {
this.regionIds = regionIds;
checkRangedRegionsValidity();
updateScanPeriods();
try {
regions.clear();
for (String regionId : regionIds) {
regions.add(new Region(regionId.toLowerCase(), Identifier.parse(regionId.toLowerCase()), null, null));
}
if (lastRangedRegions.size() == 0 || !lastRangedRegions.equals(regions)) {
lastRangedRegions = new ArrayList<>(regions);
if (beaconManager != null && beaconManager.isBound(this))
onBeaconServiceConnect();
}
} catch (Exception e) {
Timber.e(TAG + e);
}
}
private void startRanging() {
try {
if (canRun()) {
rangedRegions = (ArrayList<Region>) beaconManager.getRangedRegions();
if (rangedRegions.removeAll(regions)) {
for (Region duplicateRegion : rangedRegions) {
beaconManager.stopRangingBeaconsInRegion(duplicateRegion);
beaconManager.stopMonitoringBeaconsInRegion(duplicateRegion);
}
}
rangedRegions = (ArrayList<Region>) beaconManager.getRangedRegions();
if (regions.size() > 0 && !rangedRegions.equals(regions)) {
regions.removeAll(rangedRegions);
regionBootstrap = new RegionBootstrap(this, regions);
for (Region newRegion : regions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// regionBootstrap = new RegionBootstrap(this, newRegion);
}
beaconManager.startRangingBeaconsInRegion(newRegion);
beaconManager.startMonitoringBeaconsInRegion(newRegion);
}
}
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void addRangerNotifier() {
if (beaconManager == null)
return;
beaconManager.addRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
for (Beacon oneBeacon : beacons) {
for (int i = 0; i < beaconList.size(); i++) {
String arubaDuplicate = beaconList.get(i).id.trim();
String aruba = (oneBeacon.getId1().toString().trim() + ":" + oneBeacon.getId2().toInt() + ":" + oneBeacon.getId3().toInt()).trim();
if (aruba.equals(arubaDuplicate)) {
beaconList.remove(i);
}
}
Timber.d(TAG + "distance: " + oneBeacon.getDistance() + " id:" + oneBeacon.getId1() + "/" + oneBeacon.getId2() + "/" + oneBeacon.getId3());
beaconList.add(new Model.beacon(oneBeacon.getId1().toString().trim(), oneBeacon.getId2().toInt(), oneBeacon.getId3().toInt(), oneBeacon.getDistance()));
retryCount = 0;
}
boolean post = beaconsDifferInOrderByNth(mLastRangedBeacons, beaconList, 4);
mLastRangedBeacons = new ArrayList<>(beaconList);
if (mLastPostedTime < (System.currentTimeMillis() - 4500) && mApp.mState != null && mApp.mState.getLastKnownBeacons() == 0 && beaconList.size() > 0 && regionIds.size() > 0) {
mLastPostedTime = System.currentTimeMillis();
mBus.post(new BeaconRanger.MessageBeaconsRanged());
}
checkRangedRegionsValidity();
}
});
}
public void onEventMainThread(BeaconRangerBackgroundSetting beaconRangerBackgroundSetting) {
try {
if (beaconRangerBackgroundSetting.status) {
if (beaconManager != null && beaconManager.isBound(this))
beaconManager.setBackgroundMode(true);
} else {
if (beaconManager != null && beaconManager.isBound(this))
beaconManager.setBackgroundMode(false);
}
updateScanPeriods();
mBus.removeStickyEvent(beaconRangerBackgroundSetting);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onBeaconServiceConnect() {
if (beaconManager != null && (beaconManager.getRangingNotifiers().size() <= 0))
addRangerNotifier();
startRanging();
}
#Override
public void didEnterRegion(Region region) {
Timber.d("didEnterRegion: " + region);
try {
rangedRegions = (ArrayList<Region>) beaconManager.getRangedRegions();
if (!rangedRegions.contains(region) || rangedRegions.indexOf(region) < 0)
beaconManager.startRangingBeaconsInRegion(region);
} catch (RemoteException e) {
e.printStackTrace();
}
}
#Override
public void didExitRegion(Region region) {
Timber.d("didExitRegion: " + region);
}
#Override
public void didDetermineStateForRegion(int i, Region region) {
Timber.d("didDetermineStateForRegion: " + region);
if (i == 1) {
try {
rangedRegions = (ArrayList<Region>) beaconManager.getRangedRegions();
if ((rangedRegions.contains(region) || rangedRegions.indexOf(region) >= 0) && mLastRangedBeacons.size() == 0) {
beaconManager.stopRangingBeaconsInRegion(region);
beaconManager.startRangingBeaconsInRegion(region);
} else if (!rangedRegions.contains(region) || rangedRegions.indexOf(region) < 0) {
beaconManager.startRangingBeaconsInRegion(region);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
I am trying to run chronometer inside a Service. But I am not able to run it. I press a button in Activity and that event is passed to the Service. If the button in pressed then start the Chronometer but problem is setOnChronometerTickListener is called only once and it stops. Where am I making mistake? Here is my Service and Activity class:
Service class:
public class TimerService extends Service {
NotificationManager notificationManager;
NotificationCompat.Builder mBuilder;
Callbacks activity;
private final IBinder mBinder = new LocalBinder();
private Chronometer chronometer;
SharedPreferences sharedPreferences;
private int state = 0; //0 means stop state,1 means play, 2 means pause
private boolean running = false;
private long pauseOffSet = -1;
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
if (event.message) {
if (!running) {
if (pauseOffSet != -1) {
pauseOffSet = sharedPreferences.getLong("milli", -1);
}
chronometer.setBase(SystemClock.elapsedRealtime() - pauseOffSet);
chronometer.start();
state = 1;
pauseOffSet = 0;
running = true;
}
} else {
if (running) {
chronometer.stop();
pauseOffSet = SystemClock.elapsedRealtime() - chronometer.getBase();
state = 2;
running = false;
}
}
}
#Override
public void onCreate() {
super.onCreate();
EventBus.getDefault().register(this);
}
#Override
public void onDestroy() {
EventBus.getDefault().unregister(this);
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
sharedPreferences = getSharedPreferences("myprefs", MODE_PRIVATE);
chronometer = new Chronometer(this);
state = sharedPreferences.getInt("state", 0);
chronometer.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
#Override
public void onChronometerTick(Chronometer chronometer) {
Log.e("TimerService","timer");
pauseOffSet = SystemClock.elapsedRealtime() - chronometer.getBase();
if (pauseOffSet >= 79200000) {
chronometer.setBase(SystemClock.elapsedRealtime());
chronometer.stop();
running = false;
// progressBar.setProgress(0);
} else {
chronometer.setText(setFormat(pauseOffSet));
// int convertTime = (int) pauseOffSet;
// progressBar.setProgress(convertTime);
}
if (activity != null) {
activity.updateClient(pauseOffSet);
}
}
});
if (state == 1) { // its in play mode
running = true;
chronometer.setBase(SystemClock.elapsedRealtime() - sharedPreferences.getLong("milli", 0));
chronometer.start();
} else if (state == 2) { //its in pause mode
running = false;
pauseOffSet = sharedPreferences.getLong("milli", -1);
long time = SystemClock.elapsedRealtime() - pauseOffSet;
chronometer.setBase(time);
int convertTime = (int) pauseOffSet;
// progressBar.setProgress(convertTime);
} else {
running = false;
}
//Do what you need in onStartCommand when service has been started
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
//returns the instance of the service
public class LocalBinder extends Binder {
public TimerService getServiceInstance() {
return TimerService.this;
}
}
//Here Activity register to the service as Callbacks client
public void registerClient(Activity activity) {
this.activity = (Callbacks) activity;
}
//callbacks interface for communication with service clients!
public interface Callbacks {
public void updateClient(long data);
}
String setFormat(long time) {
int h = (int) (time / 3600000);
int m = (int) (time - h * 3600000) / 60000;
int s = (int) (time - h * 3600000 - m * 60000) / 1000;
String hh = h < 10 ? "0" + h : h + "";
String mm = m < 10 ? "0" + m : m + "";
String ss = s < 10 ? "0" + s : s + "";
return hh + ":" + mm + ":" + ss;
}
}
This is my Activity class:
public class MainActivity extends AppCompatActivity implements View.OnClickListener, TimerService.Callbacks {
private static final String TAG = MainActivity.class.getSimpleName();
Chronometer tvTextView;
Button btnStart, btnStop;
private int state = 0; //0 means stop state,1 means play, 2 means pause
SharedPreferences sharedPreferences;
private boolean running = false;
private long pauseOffSet = -1;
ProgressBar progressBar;
Intent serviceIntent;
TimerService myService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvTextView = findViewById(R.id.textview);
progressBar = findViewById(R.id.puzzleProgressBar);
btnStart = findViewById(R.id.button1);
btnStop = findViewById(R.id.button2);
btnStart.setOnClickListener(this);
btnStop.setOnClickListener(this);
serviceIntent = new Intent(this, TimerService.class);
sharedPreferences = getSharedPreferences("myprefs", MODE_PRIVATE);
state = sharedPreferences.getInt("state", 0);
tvTextView.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
#Override
public void onChronometerTick(Chronometer chronometer) {
long time = SystemClock.elapsedRealtime() - chronometer.getBase();
pauseOffSet = time;
Log.e(TAG, "pauseOffSet " + pauseOffSet);
if (time >= 79200000) {
tvTextView.setBase(SystemClock.elapsedRealtime());
tvTextView.stop();
running = false;
progressBar.setProgress(0);
} else {
chronometer.setText(setFormat(time));
int convertTime = (int) time;
progressBar.setProgress(convertTime);
}
}
});
startService(serviceIntent); //Starting the service
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE); //Binding to the service!
}
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
TimerService.LocalBinder binder = (TimerService.LocalBinder) service;
myService = binder.getServiceInstance();
myService.registerClient(MainActivity.this);
Log.e(TAG, "service connected");
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "service disconnected");
}
};
public void onClick(View v) {
if (btnStart == v) {
EventBus.getDefault().post(new MessageEvent(true));
} else if (btnStop == v) {
EventBus.getDefault().post(new MessageEvent(false));
}
}
#Override
protected void onStop() {
super.onStop();
sharedPreferences.edit().putLong("milli", pauseOffSet).commit();
sharedPreferences.edit().putInt("state", state).commit();
}
String setFormat(long time) {
int h = (int) (time / 3600000);
int m = (int) (time - h * 3600000) / 60000;
int s = (int) (time - h * 3600000 - m * 60000) / 1000;
String hh = h < 10 ? "0" + h : h + "";
String mm = m < 10 ? "0" + m : m + "";
String ss = s < 10 ? "0" + s : s + "";
return hh + ":" + mm + ":" + ss;
}
#Override
public void updateClient(long data) {
Log.d(TAG, "Data from service" + data);
}
}
The Chronometer is a View, that is, a UI element. You never add your Chronometer to any layout, I guess that's why it's never updating.
You could try using a CountDownTimer or a Handler / Runnable combination.
http://developer.android.com/reference/android/os/CountDownTimer.html http://developer.android.com/reference/android/os/Handler.html
Here's an example using Handler / Runnable, I've even thrown in a stopTimer() method for good measure:
private Handler timerHandler;
private Runnable timerRunnable;
// ...
#Override
public void onCreate() {
super.onCreate();
Log.d(LOG_TAG, "TimerService created");
timerHandler = new Handler();
timerRunnable = new Runnable() {
#Override
public void run() {
Log.d(LOG_TAG, "TICK");
timerHandler.postDelayed(timerRunnable, 1000);
}
};
}
public void startTimer() {
Log.d(LOG_TAG, "Timer started");
timerHandler.post(timerRunnable);
}
public void stopTimer() {
Log.d(LOG_TAG, "Timer stopped");
timerHandler.removeCallbacks(timerRunnable);
}
Here is a video which do not use Handler and directly implement the chronometer ,
Do check it out...
https://youtu.be/RLnb4vVkftc
Plus I had this problem I solved by removing
android:format="00:00"
from Chronometer in activity_main.xml
So my code looks like this :
<Chronometer
android:id="#+id/chronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:height="20sp"
android:foregroundGravity="fill_horizontal|top|bottom|center|fill_vertical|fill"
android:maxLines="2"
android:minLines="2"
android:textColor="#FFF"
android:textSize="40sp"
android:verticalScrollbarPosition="defaultPosition"
app:layout_constraintBottom_toBottomOf="#+id/progress_breathing"
app:layout_constraintEnd_toEndOf="#+id/progress_breathing"
app:layout_constraintStart_toStartOf="#+id/progress_breathing"
app:layout_constraintTop_toTopOf="#+id/progress_breathing"
app:layout_constraintVertical_bias="0.43" />
I am downloading multiple files one after the other in service. I want to update download progress in notification. But my notification doesn't update when I kill my app. Download works on different thread. Download works fine in when app is running. When I kill app from recent section, I am unable to update notification.
Here is my DownloadService class
public class DownloadService extends Service {
public static DownloadMap<String, Downloadables> map = new DownloadMap<String, Downloadables>();
private static Thread thread;
private DownloadCancelReceiver receiver;
public DownloadService() {
}
public class BackgroundThread extends Thread {
int serviceId;
private DownloadMap<String, Downloadables> map;
private SpeedRevisionDatabase helper;
private final int notificationId = 1;
private RemoteViews remoteViewsSmall, remoteViewsBig;
private NotificationManagerCompat notificationManager;
private NotificationCompat.Builder mBuilder, mBuilderComplete, downloadFailBuilder;
public BackgroundThread(int serviceId, DownloadMap<String, Downloadables> map) {
this.serviceId = serviceId;
this.map = new DownloadMap<>();
this.map.putAll(map);
}
#Override
public void run() {
remoteViewsSmall = new RemoteViews(getApplicationContext().getPackageName(), R.layout.download_notification_small);
remoteViewsBig = new RemoteViews(getApplicationContext().getPackageName(), R.layout.download_notification);
Intent cancelDownload = new Intent("CANCEL_DOWNLOAD");
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 100, cancelDownload, 0);
remoteViewsBig.setOnClickPendingIntent(R.id.tvCancel, pendingIntent);
notificationManager = NotificationManagerCompat.from(DownloadService.this);
mBuilder = new NotificationCompat.Builder(DownloadService.this, "default");
initChannels(DownloadService.this);
mBuilder.setContentTitle("Download is in progress")
.setContentText("Please wait...")
.setSmallIcon(R.mipmap.download)
.setOngoing(true)
.setCustomContentView(remoteViewsSmall)
.setCustomBigContentView(remoteViewsBig)
.setPriority(NotificationCompat.PRIORITY_HIGH);
notificationManager.notify(notificationId, mBuilder.build());
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
try {
String path = getApplicationContext().getApplicationInfo().dataDir + "/" + "UEP";
File uepFolder = new File(path);
if (!uepFolder.exists()) {
uepFolder.mkdir();
}
int i = 0;
while (map.entrySet().iterator().hasNext()) {
Log.e("WhileCheck", "Now i is " + i);
Map.Entry data = (Map.Entry) map.entrySet().iterator().next();
try {
Downloadables download = (Downloadables) data.getValue();
if (NetworkUtil.getConnectivityStatus(getApplicationContext()) == 0) {
throw new InternetDisconnectedException("Internet Disconnected");
}
HttpURLConnection urlConnection;
InputStream inputStream;
//finding file on internet
try {
URL url = new URL(download.URL);
if (url.toString().endsWith(".xps")) {
url = new URL(url.toString().replace(".xps", ".pdf"));
}
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.connect();
inputStream = urlConnection.getInputStream();
} catch (IOException e) {
Log.e("IOException", "" + e.getMessage());
i++;
map.remove(data.getKey());
notificationManager.notify(notificationId, mBuilder.build());
continue;
}
//Downloading file over internet
File file = new File(uepFolder + "/" + download.FileName + "" + download.Format + ".download");
try {
int fileSize = urlConnection.getContentLength();
FileOutputStream fileOutput = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int bufferLength = 0;
int count = 0;
int previousProgress = 0;
while ((bufferLength = inputStream.read(buffer)) > 0) {
if (this.isInterrupted()) {
throw new InterruptedException("Thread has been kill (Destroyed)");
}
fileOutput.write(buffer, 0, bufferLength);
count += bufferLength;
int progress = (int) (count * 100L / (float) fileSize);
if (previousProgress > 999) {
updateProgress(map.insertedCount, i, progress, download.Name);
notificationManager.notify(notificationId, mBuilder.build());
previousProgress = 0;
}
previousProgress++;
}
fileOutput.close();
} catch (IOException e) {
Log.e("IOException", "" + e.getMessage());
i++;
map.remove(data.getKey());
continue;
}
File actualFile = new File(uepFolder + "/" + download.FileName + "" + download.Format);
file.renameTo(actualFile);
Log.e("Downloaded",""+actualFile.getAbsoluteFile());
} catch (InterruptedException | InternetDisconnectedException e) {
throw e;
}
i++;
map.remove(data.getKey());
Log.e("Downloaded", "Remaining files are "+map.size());
}
Log.e("WhileCheck", "While End");
// Due to thread, sometime the sequence of code flow is not in correct flow, and "Download complete" Notifaction is getting shown
// Hence this check is required.
if (this.isInterrupted()) {
throw new InterruptedException("Thread has been kill (Destroyed)");
}
notificationManager.cancel(notificationId);
mBuilderComplete = new NotificationCompat.Builder(DownloadService.this, "default");
initChannels(DownloadService.this);
mBuilderComplete.setOngoing(false)
.setContentTitle("Download Complete")
.setColor(Color.WHITE)
.setSmallIcon(R.drawable.download);
notificationManager.notify(2, mBuilderComplete.build());
Log.e("DownloadingService", "File downloaded successfully");
stopSelf();
map.insertedCount = 0;
} catch (InterruptedException e) {
closeNotification();
Log.e("InterruptedException", "" + e.getMessage());
} catch (InternetDisconnectedException e) {
stopSelf();
closeNotification();
showNotification("Internet disconnected", "Download failed. Try again later.");
Log.e("InternetDisconnect", "" + e.getMessage());
}
}
private void updateProgress(int totalFiles, int currentFileCount, int currentProgress, String fileName) {
remoteViewsSmall.setTextViewText(R.id.tvOverAll, currentFileCount + "/" + totalFiles);
remoteViewsSmall.setProgressBar(R.id.overallProgress, totalFiles, currentFileCount, false);
remoteViewsBig.setTextViewText(R.id.tvOverAll, currentFileCount + "/" + totalFiles);
remoteViewsBig.setTextViewText(R.id.tvFileName, "Downloading " + fileName);
remoteViewsBig.setTextViewText(R.id.tvCurrentProgress, currentProgress + "%");
remoteViewsBig.setProgressBar(R.id.overallProgress, totalFiles, currentFileCount, false);
remoteViewsBig.setProgressBar(R.id.currentProgress, 100, currentProgress, false);
}
public void closeNotification() {
if (notificationManager != null)
notificationManager.cancel(notificationId);
}
public void showNotification(String title, String message) {
downloadFailBuilder = new NotificationCompat.Builder(DownloadService.this, "default");
initChannels(DownloadService.this);
downloadFailBuilder.setOngoing(false)
.setContentTitle(title)
.setContentText(message)
.setColor(Color.WHITE)
.setSmallIcon(android.R.drawable.stat_sys_warning);
notificationManager.notify(3, downloadFailBuilder.build());
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
ArrayList<Downloadables> list = (ArrayList<Downloadables>) intent.getSerializableExtra("downloadList");
for (Downloadables d : list) {
String key = d.Name + "" + d.FileName;
map.put(key, d);
}
if (thread == null || !thread.isAlive()) {
thread = new BackgroundThread(startId, map);
receiver = new DownloadCancelReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
intentFilter.addAction("CANCEL_DOWNLOAD");
registerReceiver(receiver, intentFilter);
thread.start();
}
}
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
Log.e("Service", " Stop");
try {
thread.interrupt();
} catch (Exception e) {
Log.e("Exception", "" + e.getMessage());
}
map.clear();
unregisterReceiver(receiver);
//closeNotification();
}
public static class DownloadCancelReceiver extends BroadcastReceiver {
public DownloadCancelReceiver() {
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equalsIgnoreCase("CANCEL_DOWNLOAD")) {
Intent intent1 = new Intent(context, DownloadService.class);
context.stopService(intent1);
}
}
}
public void initChannels(Context context) {
if (Build.VERSION.SDK_INT < 26) {
return;
}
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel("default", "Channel name", NotificationManager.IMPORTANCE_LOW);
channel.setDescription("Channel description");
channel.setImportance(NotificationManager.IMPORTANCE_LOW);
channel.setSound(null, null);
notificationManager.createNotificationChannel(channel);
}
private class InternetDisconnectedException extends Exception {
public InternetDisconnectedException(String message) {
super(message);
}
}
public static class DownloadMap<String, Downloadables> extends HashMap<String, Downloadables> {
public int insertedCount;
#Override
public Downloadables put(String key, Downloadables value) {
insertedCount++;
return super.put(key, value);
}
}
}
And From activity I start service like this.
ArrayList<Downloadables> list = helper.GetVideoFileList();
Intent intent1 = new Intent(SRDownloadActivity.this, DownloadService.class);
intent1.putExtra("downloadList", list);
startService(intent1);
I can even Add files to HashMap while downloading is in progress. It also worked fine.
I am unable to figure it out why it is not running in background since it is on different thread.
Here is my notification look like
Threads are killed by the Android OS even if the main app is still working and they dont have access to the UI thats why u should use AsyncTask class its a like a thread that works in background but it has access to the UI of the main. And the jobdispatcher class can help u assign jobs in the background
Here is a good tutorial https://youtu.be/das7FmQIGik
I am trying to run a timer on the activity's onCreate() method but its not running that way. the timer runs on the click of the button. I tried to call the runButtonClick() method in the onCreate() but its not running. in am passing the value through intent from another activity.
Here is my code:
public class TimerActivity extends AppCompatActivity {
private static final String TAG = TimerActivity.class.getSimpleName();
private TimerService timerService;
private boolean serviceBound;
private Button timerButton;
String GetTime;
private TextView timerTextView;
String replaceString;
// Handler to update the UI every second when the timer is running
private final Handler mUpdateTimeHandler = new UIUpdateHandler(this);
// Message type for the handler
private final static int MSG_UPDATE_TIME = 0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
Intent in = getIntent();
GetTime = in.getStringExtra("order_name");
replaceString = GetTime.replaceAll(" Minutes","");
timerButton = findViewById(R.id.delivered_to_driver);
timerTextView = findViewById(R.id.timer);
timerButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// runButtonClick();
}
});
}
#Override
protected void onStart() {
super.onStart();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Starting and binding service");
}
Intent i = new Intent(this, TimerService.class);
i.putExtra("order_time",replaceString);
startService(i);
bindService(i, mConnection, 0);
}
#Override
protected void onStop() {
super.onStop();
updateUIStopRun();
if (serviceBound) {
// If a timer is active, foreground the service, otherwise kill the service
if (timerService.isTimerRunning()) {
timerService.foreground();
}
else {
stopService(new Intent(this, TimerService.class));
}
// Unbind the service
unbindService(mConnection);
serviceBound = false;
}
}
public void runButtonClick() {
if (serviceBound && !timerService.isTimerRunning()) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Starting timer");
}
timerService.startTimer();
updateUIStartRun();
}
else if (serviceBound && timerService.isTimerRunning()) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Stopping timer");
}
timerService.stopTimer();
updateUIStopRun();
}
}
/**
* Updates the UI when a run starts
*/
private void updateUIStartRun() {
mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);
//timerButton.setText(R.string.timer_stop_button);
}
/**
* Updates the UI when a run stops
*/
private void updateUIStopRun() {
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
//timerButton.setText(R.string.timer_start_button);
}
/**
* Updates the timer readout in the UI; the service must be bound
*/
private void updateUITimer() {
if (serviceBound) {
timerTextView.setText(timerService.elapsedTime());
}
}
/**
* Callback for service binding, passed to bindService()
*/
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Service bound");
}
TimerService.RunServiceBinder binder = (TimerService.RunServiceBinder) service;
timerService = binder.getService();
serviceBound = true;
// Ensure the service is not in the foreground when bound
timerService.background();
// Update the UI if the service is already running the timer
if (timerService.isTimerRunning()) {
updateUIStartRun();
}
}
#Override
public void onServiceDisconnected(ComponentName name) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Service disconnect");
}
serviceBound = false;
}
};
/**
* When the timer is running, use this handler to update
* the UI every second to show timer progress
*/
static class UIUpdateHandler extends Handler {
private final static int UPDATE_RATE_MS = 1000;
private final WeakReference<TimerActivity> activity;
UIUpdateHandler(TimerActivity activity) {
this.activity = new WeakReference<>(activity);
}
#Override
public void handleMessage(Message message) {
if (MSG_UPDATE_TIME == message.what) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "updating time");
}
activity.get().updateUITimer();
sendEmptyMessageDelayed(MSG_UPDATE_TIME, UPDATE_RATE_MS);
}
}
}
/**
* Timer service tracks the start and end time of timer; service can be placed into the
* foreground to prevent it being killed when the activity goes away
*/
public static class TimerService extends Service {
private long totalTimeCountInMilliseconds;
private long timeBlinkInMilliseconds;
private CountDownTimer countDownTimer;
private boolean blink;
int time;
private static final String TAG = TimerService.class.getSimpleName();
String thisTime;
// Start and end times in milliseconds
private String startTime, endTime;
// Is the service tracking time?
private boolean isTimerRunning;
// Foreground notification id
private static final int NOTIFICATION_ID = 1;
// Service binder
private final IBinder serviceBinder = new RunServiceBinder();
public class RunServiceBinder extends Binder {
TimerService getService() {
return TimerService.this;
}
}
#Override
public void onCreate() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Creating service");
}
startTime = "0";
endTime = "0";
isTimerRunning = false;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Starting service");
}
thisTime = intent.getStringExtra("order_time");
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Binding service");
}
return serviceBinder;
}
#Override
public void onDestroy() {
super.onDestroy();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Destroying service");
}
}
/**
* Starts the timer
*/
public void startTimer() {
if (!isTimerRunning) {
if (thisTime != null) {
time = Integer.parseInt(thisTime);
} else
Toast.makeText(TimerService.this, "",
Toast.LENGTH_LONG).show();
totalTimeCountInMilliseconds = 60 * time * 1000;
timeBlinkInMilliseconds = 30 * 1000;
// startTime = System.currentTimeMillis();
isTimerRunning = true;
countDownTimer = new CountDownTimer(totalTimeCountInMilliseconds, 500) {
#Override
public void onTick(long leftTimeInMilliseconds) {
long seconds = leftTimeInMilliseconds / 1000;
if (leftTimeInMilliseconds < timeBlinkInMilliseconds) {
if (blink) {
// mTextField.setVisibility(View.VISIBLE);
// if blink is true, textview will be visible
} else {
// mTextField.setVisibility(View.INVISIBLE);
}
blink = !blink;
}
String a = String.format("%02d", seconds / 60) + ":" + String.format("%02d", seconds % 60);
startTime = a;
isTimerRunning = true;
}
#Override
public void onFinish() {
Toast.makeText(TimerService.this, "Finished", Toast.LENGTH_SHORT).show();
}
}.start();
}
else {
Log.e(TAG, "startTimer request for an already running timer");
}
}
/**
* Stops the timer
*/
public void stopTimer() {
if (isTimerRunning) {
endTime = String.valueOf(System.currentTimeMillis());
isTimerRunning = false;
}
else {
Log.e(TAG, "stopTimer request for a timer that isn't running");
}
}
/**
* #return whether the timer is running
*/
public boolean isTimerRunning() {
return isTimerRunning;
}
/**
* Returns the elapsed time
*
* #return the elapsed time in seconds
*/
public String elapsedTime() {
// If the timer is running, the end time will be zero
return startTime;
}
/*Integer.parseInt(endTime) > Integer.parseInt(startTime) ?
(Integer.parseInt(endTime) - Integer.parseInt(startTime)) / 1000 :
(System.currentTimeMillis() - Integer.parseInt(startTime)) / 1000;*//*
}
/**
* Place the service into the foreground
*/
public void foreground() {
startForeground(NOTIFICATION_ID, createNotification());
}
/**
* Return the service to the background
*/
public void background() {
stopForeground(true);
}
/**
* Creates a notification for placing the service into the foreground
*
* #return a notification for interacting with the service when in the foreground
*/
private Notification createNotification() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle("Timer Active")
.setContentText("Tap to return to the timer")
.setSmallIcon(R.mipmap.ic_launcher);
Intent resultIntent = new Intent(this, TimerActivity.class);
PendingIntent resultPendingIntent =
PendingIntent.getActivity(this, 0, resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(resultPendingIntent);
return builder.build();
}
}
}
I don't understand what the problem might me..your help will be appreciated.thank you in advance...
You startService() called in onStart() method and serviceBound will true after starting service. so that in oncreate() method if condition in runButtonClick will not execute.
I am new to Android development and I would like to understand why my application is using this much memory when it starts. Right now, it starts off with 18MB of memory (using the memory monitor in Android Studio). I have added Firebase Analytics and Admob to it. I am worried that these are culprits of using this large amount of memory.
By doing some digging in, I found out that SharedPreferenceImpl and its children objects (FastXmlSerializer and ByteBuffer) were taking up 50% of the memory! I am pretty shocked. Next, I try to see which thread is generating these objects. I found out that the thread name is "thread-1-pool-1".
Here is the stacktrace responsible for creating this large amount of memory:
at java.nio.ByteBuffer.allocate(ByteBuffer.java:56)
at com.android.internal.util.FastXmlSerializer.<init>(FastXmlSerializer.java:62)
at com.android.internal.util.XmlUtils.writeMapXml(XmlUtils.java:188)
at android.app.SharedPreferencesImpl.writeToFile(SharedPreferencesImpl.java:600)
at android.app.SharedPreferencesImpl.-wrap2(SharedPreferencesImpl.java)
at android.app.SharedPreferencesImpl$2.run(SharedPreferencesImpl.java:515)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
I have a LoopThread that calls a method in a service. I am wondering if this could cause the large consumption of memory. My service class and loop thread contains no references to SharedPreferences though. So, I am very confuse why this happens. Any pointers would be appreciated.
The code may not be very clean and it is long. But here it is. The idea of this app is to bring up a screen saver when the power button is pressed.
This is the service class.
//SaverService.java
#Override
public void onCreate() {
Log.d("SaverService","Created saver service...");
super.onCreate();
loopThread = new LoopThread();
loopThread.createHandler(this);
loopThread.start();
startSaverMethod();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, StartSaver.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.pokeball)
.setContentTitle("Go-Saver's running")
.setContentText("Tap here to cancel")
.setContentIntent(pendingIntent);
Notification notification = mBuilder.build();
startForeground(1001, notification);
return START_STICKY;
}
//SharedPreference should be a singleton
private void startSaverMethod() {
//sharedPreferences = getSharedPreferences("GoSaverPrefs", Context.MODE_PRIVATE);
try {
//SharedPreferences.Editor editor = sharedPreferences.edit();
screenBrightness = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS);
previousScreenBrightness = screenBrightness;
//editor.putInt("screenBrightness", screenBrightness);
//editor.commit();
screenBrightnessMode = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE);
previousScreenBrightnessMode = screenBrightnessMode;
//editor.putInt("screenBrightnessMode",screenBrightnessMode);
//editor.commit();
screenOffTimeout = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT);
previousScreenOffTimeout = screenOffTimeout;
//editor.putInt("screenOffTimeout",screenOffTimeout);
//editor.commit();
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
}
//Registerting receiver
screenOffReceiver = new ScreenOffReceiver();
screenOffReceiver.setSaverService(this);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
registerReceiver(screenOffReceiver, filter);
Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT, 3600000);
pressedMillisec = getPressedMillisec();
}
#Override
public void onDestroy() {
Log.d("SaverService","Destroying saver service...");
disableSaverSettings();
//screenOffTimeout = sharedPreferences.getInt("screenOffTimeout", 30000);
screenOffTimeout = previousScreenOffTimeout;
Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT, screenOffTimeout);
unregisterReceiver(screenOffReceiver);
removeScreenSaver();
//sharedPreferences = null;
windowManager = null;
screenOffReceiver = null;
Message killMessage = loopThread.handler.obtainMessage(LoopThread.KILL);
loopThread.handler.sendMessage(killMessage);
loopThread.handler.getLooper().quit();
super.onDestroy();
System.gc();
}
public boolean isScreenSaver() {
if(screenSaver != null) {
return screenSaver.isShown();
} else {
return false;
}
}
private boolean isSaverBrightness() {
try {
int brightness = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS);
int brightnessMode = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE);
return brightness == 1 && brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
} catch (Exception e) {
Log.e("SaverService", "Cannot get brightness");
return false;
}
}
public void startScreenSaverAsync() {
Message onMessage = loopThread.handler.obtainMessage(LoopThread.ON);
loopThread.handler.sendMessage(onMessage);
}
public void startScreenSaver() {
Log.d("SaverService","######################################");
boolean isRightState = !isScreenSaver() && !isSaverBrightness();
if (!isRightState) {
Log.e("SaverService","Invalid state. Stopping service");
stopSelf();
}
long currentMillisec = getPressedMillisec();
if (currentMillisec - pressedMillisec < 2000) {
Log.d("SaverService","Pressed too fast. Ignoring the second press.");
return;
}
//Creating screenSaver
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
LayoutInflater li = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
screenSaver = li.inflate(R.layout.screen_saver, null);
long startTime = System.currentTimeMillis();
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_IMMERSIVE,
PixelFormat.TRANSLUCENT);
long totalTime = System.currentTimeMillis() - startTime;
Log.d("SaverService","params Time: " + totalTime);
startTime = System.currentTimeMillis();
enableSaverSettings();
totalTime = System.currentTimeMillis() - startTime;
Log.d("SaverService","enableSaverSettings Time: " + totalTime);
startTime = System.currentTimeMillis();
removeScreenSaver();
totalTime = System.currentTimeMillis() - startTime;
Log.d("SaverService","removeScreenSaver Time: " + totalTime);
Log.d("SaverService", "Adding Screen Saver");
startTime = System.currentTimeMillis();
windowManager.addView(screenSaver, params);
totalTime = System.currentTimeMillis() - startTime;
Log.d("SaverService","addView Time: " + totalTime);
Log.d("SaverService", "Turning On Screen");
startTime = System.currentTimeMillis();
turnOnScreen(this);
totalTime = System.currentTimeMillis() -startTime;
Log.d("SaverService","turnOnScreen Time: " + totalTime);
pressedMillisec = getPressedMillisec();
Log.d("SaverService", "Current time - " + pressedMillisec);
Log.d("SaverService","######################################");
}
public void stopScreenSaverAsync() {
Message offMessage = loopThread.handler.obtainMessage(LoopThread.OFF);
loopThread.handler.sendMessage(offMessage);
}
public void stopScreenSaver() {
Log.d("SaverService","######################################");
long currentMillisec = getPressedMillisec();
if (currentMillisec - pressedMillisec < 2000) {
Log.d("SaverService","Pressed too fast. Ignoring the second press.");
return;
}
disableSaverSettings();
removeScreenSaver();
turnOnScreen(this);
pressedMillisec = getPressedMillisec();
Log.d("SaverService", "Current time - " + pressedMillisec);
Log.d("SaverService","######################################");
}
private long getPressedMillisec() {
return Calendar.getInstance().getTimeInMillis();
}
private void removeScreenSaver() {
try {
windowManager.removeViewImmediate(screenSaver);
screenSaver = null;
} catch (Exception e) {
Log.w("SaverService", "Could not remove screen saver...");
}
}
private void enableSaverSettings() {
Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 1);
}
private void disableSaverSettings() {
screenBrightness = previousScreenBrightness;
screenBrightnessMode = previousScreenBrightnessMode;
//screenBrightness = sharedPreferences.getInt("screenBrightness", 50);
//screenBrightnessMode = sharedPreferences.getInt("screenBrightnessMode", Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, screenBrightnessMode);
Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, screenBrightness);
}
private void turnOnScreen(Context context) {
//Wait for screen is off
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
boolean isScreenOn = pm.isScreenOn();
while (isScreenOn) {
try {
Thread.sleep(2);
isScreenOn = pm.isScreenOn();
} catch (Exception e) {
Log.i("Issue", "Could not sleep...");
}
}
Intent intentNew = new Intent(context.getApplicationContext(), Transparent.class);
intentNew.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intentNew);
}
This is the class that listens for a Power button press:
public class ScreenOffReceiver extends BroadcastReceiver {
private SaverService saverService = null;
private static boolean anyThreadRunning = false;
void setSaverService(SaverService saverService){
this.saverService = saverService;
}
#Override
public void onReceive(Context context, Intent intent) {
runMethod2(context, intent);
}
private void runMethod2(Context context, Intent intent) {
String action = intent.getAction();
Log.d("SaverService", action);
if(anyThreadRunning) {
Log.d("SaverService","Another thread is using ScreenOffReceiver. Skipping... ");
return;
}
if(action.equals(Intent.ACTION_SCREEN_OFF) && !saverService.isScreenSaver()){
anyThreadRunning = true;
Log.d("SaverService","Turning on screen saver.");
saverService.startScreenSaverAsync();
}
else if(action.equals(Intent.ACTION_SCREEN_OFF) && saverService.isScreenSaver()){
anyThreadRunning = true;
Log.d("SaverService","Turning off screen saver." );
saverService.stopScreenSaverAsync();
}
anyThreadRunning = false;
}
}
This the loop thread class:
public class LoopThread extends Thread {
public Handler handler = null;
public static final int ON = ScreenOffHandler.ON;
public static final int OFF = ScreenOffHandler.OFF;
public static final int KILL = ScreenOffHandler.KILL;
private SaverService saverService;
public void run(){
Looper.prepare();
handler = new ScreenOffHandler(saverService);
Looper.loop();
}
public void createHandler(SaverService saverService){
this.saverService = saverService;
}
private static class ScreenOffHandler extends Handler {
private SaverService saverService;
public static final int ON = 1;
public static final int OFF = 0;
public static final int KILL = -100;
public ScreenOffHandler(SaverService saverService) {
this.saverService = saverService;
}
public void handleMessage(Message msg) {
// ...Run in background
int what = msg.what;
switch (what){
case ON:
saverService.startScreenSaver();
break;
case OFF:
saverService.stopScreenSaver();
break;
case KILL:
saverService = null;
break;
}
}
}
}