Singleton object is recreating - java

I am facing a problem, that I created a class Controller it is singleton but its object is recreating when I access in different activity of same application,
Main_Activity is my launching activity
public class Main_Activity extends Activity{
private Controller simpleController;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
simpleController = Controller.getInstance(this);
}
}
This is my Controller it is singleton, in it I am setting alarm which is of 10sec from now and my MyMainLocalReciever receives that alarm and notify using notification.
public class Controller {
private MediaPlayer mp;
public Context context;
private static Controller instance;
public static Controller getInstance(Context context) {
if (instance == null) {
instance = new Controller(context);
}
return instance;
}
private Controller(Context context) {
Log.d("TAG", "Creating Controller object");
mp = null;
this.context = context;
setAlarm(10);
}
public void setAlarm(int position) {
Intent intent = new Intent(context, MyMainLocalReciever.class);
intent.putExtra("alarm_id", "" + position);
PendingIntent sender = PendingIntent.getBroadcast(context,
position, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the AlarmManager service
AlarmManager am = (AlarmManager) context
.getSystemService(Activity.ALARM_SERVICE);
am.cancel(sender);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
+ (position*1000), sender);
}
}
This is my receiver MyMainLocalReciever it notify and I am binding an intent which starts an activity called NotificationDialog
public class MyMainLocalReciever extends BroadcastReceiver {
private NotificationManager notificationManager;
private int alarmId = 0;
#Override
public void onReceive(Context context, Intent intent) {
if (notificationManager == null) {
notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
}
Bundle bundle = intent.getExtras();
String alarm_Id = bundle.getString("alarm_id");
try {
alarmId = Integer.parseInt(alarm_Id);
} catch (Exception e) {
Log.d("Exception", "exception in converting");
}
Controller myC = Controller.getInstance(context);
if ((myC.getMp() != null)) {
myC.getMp().stop();
myC.setMp(null);
}
if (myC.getMp() == null) {
myC.setMp(MediaPlayer.create(context , R.id.mpFile));
myC.getMp().start();
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setTicker("Its Ticker")
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("Its Title")
.setContentText("Its Context")
.setAutoCancel(true)
.setContentIntent(
PendingIntent.getActivity(context, 0, new Intent(context,
NotificationDialog.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK), 0));
notificationManager.notify("interstitial_tag", alarmId,
builder.getNotification());
}
}
Till now(before NotificationDialog) code is working perfect MediaPlayer object which is in Controller class is working fine too, but when I access my singleton Controller here in NotificationDialog, it is creating new object of Controller, it should not do that, it should retain that Controller object which is singleton.
public class NotificationDialog extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notification_dialog);
}
public void onViewContent(View v) { //this method is invoked when I click on a button binded in xml file
Controller myC = Controller.getInstance(getApplicationContext());
if (myC.getMp() != null) {
myC.getMp().stop();
myC.setMp(null);
}
finish();
}
}
Kindly help me regarding this, I will appreciate your help.
Regards
EDIT:
Here is my Manifest
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".Main_Activity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="test.SettingsActivity"
android:label="#string/app_name" />
<activity
android:name="test.NotificationDialog"
android:label="#string/app_name" />
<service android:name="test.MyService" >
</service>
<receiver
android:name="test.MyMainLocalReciever"
android:process=":remote" />
</application>

Your process is getting killed by Android when it is idle in the background. Android will kill off your process if there are no active components (Activities, Services, etc.) or when it needs the memory (even if you have active components).
When the user uses your notification, Android creates a new process for you. That is why the Singleton is gone and needs to get recreated.
EDIT:
After you posted your manifest I immediately saw the problem. This is it:
<receiver
android:name="test.MyMainLocalReciever"
android:process=":remote" />
Your process isn't getting killed. Your BroadcastReceiver is running in another separate process. In that process, the singleton hasn't been set up yet.
Remove android:process=":remote" from your <receiver> tag in the manifest.

Please read about the Initialization-on-demand holder idiom. It's very clear and simple article about right Singleton in the Java programming language.

As the Singleton will be a static object used by many Activities, you don't have to pass the Context to the constructor. Passing it to the methods which will need it, is a better option.
public class Controller {
private static volatile Controller instance = null;
private Controller () { }
public static Controller getInstance() {
if (instance == null) {
synchronized (Controller .class)
if (instance == null) {
instance = new Controller();
}
}
return instance;
}
public void setAlarm(Context context, int position) {
// do stuff
}
}

Related

BroadcastReceiver for SCREEN_ON and SCREEN_OFF even after quitting the app

I'm trying to make an app that monitors the users phone usage by tracking time of screen lock and unlock. I tried to setup a BroadcastReceiver which works fine when the app is running the background. But won't work when I close the app. Is there a solution for this.
The code I'm using now is as follows :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, ScreenListenerService.class);
startService(intent);
}
}
ScreenListenerService class is as follows..
public class ScreenListenerService extends Service {
private BroadcastReceiver mScreenStateBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
// Save something to the server
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
// Save something to the server
}
}
};
#Override
public void onCreate() {
super.onCreate();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(mScreenStateBroadcastReceiver, intentFilter);
}
#Override
public void onDestroy() {
unregisterReceiver(mScreenStateBroadcastReceiver);
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
My AndroidManifest file is as follows :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.abbinvarghese.calculu">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<service android:name=".ScreenListenerService" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
To overcome the imposed limitations of 8.0 you could run a foreground service. Just like a service but a notification is posted to the foreground.
Then the service code would be like this (remember to unregister the receiver onDestory):
BroadcastReceiver screenReceiver;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startRunningInForeground();
detectingDeterminateOfServiceCall(intent.getExtras());
registerBroadcastReceivers();
return START_STICKY;
}
private void startRunningInForeground() {
//if more than or equal to 26
if (Build.VERSION.SDK_INT >= 26) {
//if more than 26
if(Build.VERSION.SDK_INT > 26){
String CHANNEL_ONE_ID = "sensor.example. geyerk1.inspect.screenservice";
String CHANNEL_ONE_NAME = "Screen service";
NotificationChannel notificationChannel = null;
notificationChannel = new NotificationChannel(CHANNEL_ONE_ID,
CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_MIN);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setShowBadge(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (manager != null) {
manager.createNotificationChannel(notificationChannel);
}
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.background_running);
Notification notification = new Notification.Builder(getApplicationContext())
.setChannelId(CHANNEL_ONE_ID)
.setContentTitle("Recording data")
.setContentText("ActivityLog is logging data")
.setSmallIcon(R.drawable.background_running)
.setLargeIcon(icon)
.build();
Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
notification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);
startForeground(101, notification);
}
//if version 26
else{
startForeground(101, updateNotification());
}
}
//if less than version 26
else{
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Activity logger")
.setContentText("data recording on going")
.setSmallIcon(R.drawable.background_running)
.setOngoing(true).build();
startForeground(101, notification);
}
}
private Notification updateNotification() {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), 0);
return new NotificationCompat.Builder(this)
.setContentTitle("Activity log")
.setTicker("Ticker")
.setContentText("recording of data is on going")
.setSmallIcon(R.drawable.activity_log_icon)
.setContentIntent(pendingIntent)
.setOngoing(true).build();
}
private void detectingDeterminateOfServiceCall(Bundle b) {
if(b != null){
Log.i("screenService", "bundle not null");
if(b.getBoolean("phone restarted")){
storeInternally("Phone restarted");
}
}else{
Log.i("screenService", " bundle equals null");
}
documentServiceStart();
}
private void documentServiceStart() {
Log.i("screenService", "started running");
}
private void registerBroadcastReceivers() {
screenReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
switch (Objects.requireNonNull(intent.getAction())){
case Intent.ACTION_SCREEN_ON:
//or do something else
storeInternally("Screen on");
break;
case Intent.ACTION_SCREEN_OFF:
//or do something else
storeInternally("Screen off");
break;
}
}
};
IntentFilter screenFilter = new IntentFilter();
screenFilter.addAction(Intent.ACTION_SCREEN_ON);
screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(screenReceiver, screenFilter);
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(screenReceiver);
}
and call it from the main activity:
private void startServiceRunning() {
if(!isMyServiceRunning(Background.class)){
if(Build.VERSION.SDK_INT >25){
startForegroundService(new Intent(this, Background.class));
}else{
startService(new Intent(this, Background.class));
}
}
}
As Background Execution Limit imposes on Android 8.0 (API level 26) so now it's not possible to listen SCREEN_OFF and SCREEN_ON action in background by running the service.
I have found a work around for same with the help of JobScheduler which works fine for listen broadcast in background without running any service.
Please check on this: Screen OFF/ON broadcast listener without service on Android Oreo
Instead of creating a new service for broadcast receiver, you can directly create a broadcast receiver class that will listen to system broadcasts even when the app is not running.
Create a new class which extends BroadcastReceiver.
public class YourReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Do your stuff
}
}
And register it in manifest.
<receiver
android:name=".YourReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_SCREEN_ON" />
<action android:name="android.intent.action. ACTION_SCREEN_OFF" />
<category android:name="android.intent.category.DEFAUL" />
</intent-filter>
</receiver>
Read about Manifest-declared receivers here.
Above solution won't work, here is the reason why. Problem is that your service is getting killed when the app is killed, so your receiver instance is removed from memory. Here is a little trick to re-start the service in background. Add the following code to your service.
#Override
public void onTaskRemoved(Intent rootIntent){
Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent);
super.onTaskRemoved(rootIntent);
}
Although this is not the right way to do it. Also in Android 26+ you won't be able to do this and you'd go for foreground service. https://developer.android.com/about/versions/oreo/background

Android NFC Reader in MVP - onNewIntent not firing

I have a working NFC reader/writer code. Using the same code, I added the reader function in another app which is following MVP architecture.
The activity is named NFCReaderActivity. A separate NFC class is created (NFCReader), which implements Sensor interface.
The app is supposed to work both in the foreground and launch showing the NFC tag info. The launch part is working fine and app launches and reads the tag and shows its content.
However, in the foreground, on scanning, it does nothing. I only hear the scan beep but no onNewIntent is firing.
Below are the log entries captured for foreground and launch actions. There is a difference in the class names:
When not launching
I/ActivityManager: START u0 {act=android.nfc.action.NDEF_DISCOVERED typ=application/com.abc.vi flg=0x14008000 cmp=com.abc.vi/.ui.reader.NFCReader (has extras)} from uid 10038 on display 0
When launching
I/ActivityManager: START u0 {act=android.nfc.action.NDEF_DISCOVERED typ=application/com.abc.vi cmp=com.abc.vi/.ui.reader.NFCReaderActivity (has extras)} from uid 1027 on display 0
Activity
onCreate
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "__onCreate__ " );
setContentView(R.layout.activity_nfc_reader);
VI.setNFCReaderActivityContext(this); //VI is the Application class
ButterKnife.bind(this);
presenter = new ReaderPresenter(this);
}
onNewIntent
#Override
public void onNewIntent(Intent intent) {
Log.i(TAG, "__onNewIntent__ " );
// onResume gets called after this to handle the intent
// setIntent(intent);
presenter.onNewIntent(intent);
}
onResume, onPause
#Override
protected void onResume() {
super.onResume();
Log.i(TAG, "__onResume__ " );
presenter.onResume();
}
#Override
protected void onPause() {
super.onPause();
Log.i(TAG, "__onPause__ " );
presenter.onPause();
}
Presenter
ReaderPresenter(ReaderContract.View view) {
this.view = view;
initSensor();
}
#Override
public void initSensor() {
nfcReader = new NFCReader(VI.getNFCReaderActivityContext(), this); //VI is the Application class
}
#Override
public void onNewIntent(Intent intent) {
nfcReader.resolveIntent(intent);
}
#Override
public void onResume() {
nfcReader.onResume();
}
#Override
public void onPause() {
nfcReader.onPause();
}
#Override
public void onDestroy() {
speech.onDestroy();
}
NFCReader
public class NFCReader implements Sensors {
private static final String TAG = NFCReader.class.getSimpleName();
private NfcAdapter nfcAdapter;
private PendingIntent nfcPendingIntent;
private NFCReaderActivity activity;
private ReaderPresenter presenter;
NFCReader(NFCReaderActivity nfcReaderActivity, ReaderPresenter readerPresenter) {
this.activity = nfcReaderActivity;
this.presenter = readerPresenter;
init();
}
#Override
public void init() {
//Initialize NFC adapter
nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
nfcPendingIntent = PendingIntent.getActivity(activity, 0, new Intent(activity,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP), 0);
}
public void onResume() {
if (nfcAdapter != null) {
nfcAdapter.enableForegroundDispatch(activity, nfcPendingIntent, null, null);
// if NFC not enabled
if (!nfcAdapter.isEnabled()) {
new AlertDialog.Builder(activity)
.setPositiveButton(activity.getString(R.string.update_setting_btn),
(dialog, which) -> {
Intent setNfc = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
activity.startActivity(setNfc);
})
.setOnCancelListener(
dialog -> activity.finish()
)
.create().show();
}
resolveIntent(activity.getIntent());
} else {
Toast.makeText(VI.getAppContext(),
activity.getString(R.string.error_no_nfc_found), Toast.LENGTH_LONG).show();
}
}
public void onPause() {
if (nfcAdapter != null) {
nfcAdapter.disableForegroundDispatch(activity);
}
}
public void resolveIntent(Intent intent){
Log.i(TAG, "__resolveIntent__");
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
NdefMessage[] messages = null;
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs != null) {
messages = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
messages[i] = (NdefMessage) rawMsgs[i];
}
}
if ((messages != null ? messages[0] : null) != null) {
StringBuilder result = new StringBuilder();
byte[] payload = messages[0].getRecords()[0].getPayload();
for (byte aPayload : payload) {
result.append((char) aPayload);
}
Log.i(TAG,"Decoded --> "+result.toString());
presenter.getData(result.toString());
}
}
}
}
Manifest
<activity android:name=".ui.reader.NFCReaderActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="#string/mime_type" />
</intent-filter>
</activity>
UPDATE
I moved all the code from NFCReader class to NFCReaderActivity and both foreground and launch modes are working. The issue is with MVP architecture. How to convert it back to MVP?
You seem to register the pending intent for the wrong (actually an invalid) component (not your activity class). The reason is that when you create the PendingIntent that you assign to nfcPendingIntent, you use getClass() to obtain the class of the NFCReader instance. Instead you would need to use activity.getClass() to obtain the class of your activity component.

Is sendBroadcast() the best way to keep a WakefulBroadcastReceiver alive in Android?

I try do do some background calculation tasks in an Android application.
My Main class :
public class MainActivity extends AppCompatActivity {
private CalculationReceiver calculationReceiver = new CalculationReceiver();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
final Context mContext = this;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
calculationReceiver.doAddition(mContext, 2, 2);
}
});
}
}
My service :
public class CalculationService extends IntentService {
public CalculationService() {
super("Calculation Service");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
int nb1 = intent.getIntExtra(NUMBER_1,0);
int nb2 = intent.getIntExtra(NUMBER_2,0);
doAddition(nb1,nb2);
CalculationReceiver.completeWakefulIntent(intent);
}
public void doAddition(int number1, int number2){
int result = number1+number2;
System.out.println("Result : " + result);
}
}
My receiver :
public class CalculationReceiver extends WakefulBroadcastReceiver {
public static final String NUMBER_1 = "NUMBER_1";
public static final String NUMBER_2 = "NUMBER_2";
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, CalculationService.class);
int receiverNumber1 = intent.getIntExtra(NUMBER_1,0);
int receiverNumber2 = intent.getIntExtra(NUMBER_2,0);
service.putExtra(NUMBER_1,receiverNumber1);
service.putExtra(NUMBER_2,receiverNumber2);
startWakefulService(context, service);
}
public void doAddition (Context context, int number1, int number2){
Intent intent = new Intent(context, CalculationReceiver.class);
intent.putExtra(NUMBER_1,number1);
intent.putExtra(NUMBER_2,number2);
context.sendBroadcast(intent);
}
}
My Manifest :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testservices">
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".ReductionService"
android:enabled="true" />
<receiver android:name=".ReductionReceiver"/>
<service android:name=".CalculationService"
android:enabled="true" />
<receiver android:name=".CalculationReceiver"/>
</application>
</manifest>
The calculations of the application are more complex than these additions, and can take several minutes (in average 15 minutes) to be done.
According to the Google documentation (https://developer.android.com/training/scheduling/wakelock.html), I decided to implement this architecture to make sure that the calculation is done to the end.
The idea is that the user starts his calculation and then waits for the application to give the result. In the meantime, he can launch other apps or lock his phone, the calculation must not stop.
This approach seems to work.
What bothers me here is the call to service in the receiver:
context.sendBroadcast (intent);
Is there a more "clean" way to start the service?
What strikes me is that it does not seem very "clean", especially the passage of several times the same parameter (number1 and number2)
Thanks
According to the Google documentation (https://developer.android.com/training/scheduling/wakelock.html), I decided to implement this architecture to make sure that the calculation is done to the end.
That is not how the documentation shows using WakefulBroadcastReceiver. Plus, WakefulBroadcastReceiver was deprecated in version 26.0.0 of the support libraries.
The idea is that the user starts his calculation and then waits for the application to give the result.
My interpretation of this is that the user is requesting, through your activity's UI, to start the calculation. This means that at this point in time, the screen is on and you have an activity in the foreground.
Is there a more "clean" way to start the service?
Call startService().
Step #1: Delete your use of the deprecated WakefulBroadcastReceiver
Step #2: Have your activity call startService() to start the service
Step #3: Have your service acquire a partial WakeLock through the PowerManager system service, in the service's onCreate() method
Step #4: Have your service release that WakeLock in the service's onDestroy() method
Step #5: Modify the service to be a foreground service, calling startForeground() in onCreate() with a suitable Notification to allow the user to control the behavior of the service
Note that:
If you skip Step #5, your service will stop running after ~1 minute on Android 8.0+.
This will still not work on Android 6.0+ if the device enters into Doze mode. That should not happen for ~1 hour, but you need to make sure that your calculations are done by then.
Consider offloading the calculation work to a server, rather than burning up the user's CPU for an extended period of time (through your calculation work plus the wakelock)
It was in this way that I resolved it, following the advice of #CommonsWare
Main Activity :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
final Context mContext = this;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent service = new Intent(mContext, CalculationService.class);
service.putExtra(NUMBER_1,2);
service.putExtra(NUMBER_2,2);
startService(service);
}
});
}
}
Calculation Service :
public class CalculationService extends IntentService {
public static final String NUMBER_1 = "NUMBER_1";
public static final String NUMBER_2 = "NUMBER_2";
private static final int FOREGROUND_ID = 42;
PowerManager.WakeLock wakeLock;
public CalculationService() {
super("Calculation Service");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
int nb1 = intent.getIntExtra(NUMBER_1,0);
int nb2 = intent.getIntExtra(NUMBER_2,0);
doAddition(nb1,nb2);
}
public void doAddition(int number1, int number2){
int result = number1+number2;
System.out.println("Result : " + result);
}
#Override
public void onCreate() {
super.onCreate();
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"CalculationServiceWakelockTag");
wakeLock.acquire();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Calculation App")
.setContentText("Calculation in progress")
.setContentIntent(pendingIntent).build();
startForeground(FOREGROUND_ID, notification);
}
#Override
public void onDestroy() {
super.onDestroy();
wakeLock.release();
}
}
This is working great :)
But can I call
startForegroundService(service)
instead of
startService(service);
or not ? And why ?

Adding Marshmallow permissions using "Permissions Dispatcher"

I'm successfully using Permission Dispatcher to handle Marshmallow permissions.
Is there a way to use it on a static class, where I cannot access to "this" in ${className}PermissionDispatcher.${method}WitchCheck(this) ?
Working example:
#RuntimePermissions
public class ContactsMapFragment extends mFragment implements OnMapReadyCallback {
#Override
public void onMapReady(#NonNull GoogleMap googleMap) {
if (DeviceUtils.isMarshmallow())
ContactsMapFragmentPermissionsDispatcher.setMapLocationWithCheck(this, googleMap);
else
googleMap.setMyLocationEnabled(true);
}
#NeedsPermission(Manifest.permission.ACCESS_FINE_LOCATION)
protected void setMapLocation(#NonNull GoogleMap gmap) {
gmap.setMyLocationEnabled(true);
}
}
But in this other snippet I have a static class, so this doesn't exist:
#RuntimePermissions
public class PhoneUtils
{
#NeedsPermission(Manifest.permission.CALL_PHONE)
public static void makeCall(String szNumber)
{
Intent intent = new Intent(
Intent.ACTION_CALL, // place a phone call directly
Uri.parse("tel:" + szNumber)
);
mActivity activity = Muffin.getInstance().getActivity();
if (activity == null)
return;
if (DeviceUtils.isMarshmallow())
PhoneHelperPermissionDispatcher.startCallWithCheck(this, activity, intent);
else
activity.startActivity(intent);
}
protected void startCall(mActivity activity, Intent intent)
{
activity.startActivity(intent);
}
}
I have not used Permission Dispatcher library but I think you can pass the Activity or Context to methods of static class and can use it.
or
You can use Application class to keep context global.
for example:
public class MyDemoApplication extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
MyDemoApplication .context = getApplicationContext();
}
public static Context getAppContext() {
return MyDemoApplication.context;
}
}
You have to call MyDemoApplication.getAppContext() to get your application context statically from any class file.
Note: If you are extending Application class in your project then don't forget to specify its name in application tag of manifest file.
<?xml version="1.0" encoding="utf-8"?>
<activity android:name=".MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
See below links for more details:
https://developer.android.com/reference/android/app/Application.html
http://www.devahead.com/blog/2011/06/extending-the-android-application-class-and-dealing-with-singleton/
Static class can't be annotated because only Activity and Fragment are allowed, as Android Framework just provides requestPermission method on Activity and Fragment

Sending data from service to activity

I am having issue when sending data from Service to Activity through Notification , I click a Notification an Activity get invoked but when i try to add some parameters through bundle i am not able to get the parameters in that called intent , I have gone through the link
How to send parameters from a notification-click to an activity?
But still no luck.
Has the same issue occurred with somebody else ?
Thanks in advance.
You have to modify the Manifest file as well.
Here is the example that works:
These variables and methods are members of Service class:
public static final String MOVEMENT_UPDATE = "com.client.gaitlink.AccelerationService.action.MOVEMENT_UPDATE";
public static final String ACCELERATION_X = "com.client.gaitlink.AccelerationService.ACCELERATION_X";
public static final String ACCELERATION_Y = "com.client.gaitlink.AccelerationService.ACCELERATION_Y";
public static final String ACCELERATION_Z = "com.client.gaitlink.AccelerationService.ACCELERATION_Z";
private void announceAccelerationChanges()//this method sends broadcast messages
{
Intent intent = new Intent(MOVEMENT_UPDATE);
intent.putExtra(ACCELERATION_X, accelerationX);
intent.putExtra(ACCELERATION_Y, accelerationY);
intent.putExtra(ACCELERATION_Z, accelerationZ);
sendBroadcast(intent);
}
And this are the methods from Main activity:
You have to register receiver in the onResume method:
#Override
public void onResume()
{
IntentFilter movementFilter;
movementFilter = new IntentFilter(AccelerationService.MOVEMENT_UPDATE);
accelerationReceiver = new AccelerationServiceReceiver();
registerReceiver(accelerationReceiver, movementFilter);
startAccelerationService();
super.onResume();
}
private void startAccelerationService()
{
startService(new Intent(this, AccelerationService.class));
}
public class AccelerationServiceReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)//this method receives broadcast messages. Be sure to modify AndroidManifest.xml file in order to enable message receiving
{
accelerationX = intent.getDoubleExtra(AccelerationService.ACCELERATION_X, 0);
accelerationY = intent.getDoubleExtra(AccelerationService.ACCELERATION_Y, 0);
accelerationZ = intent.getDoubleExtra(AccelerationService.ACCELERATION_Z, 0);
announceSession();
updateGUI();
}
}
This is the part of AndroidManifest.xml file that has to be set in order to receive broadcast messages:
<activity android:name=".GaitLink"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="com.client.gaitlink.CommunicationService.action.ACTIVITY_STATUS_UPDATE" />
</intent-filter>
</activity>

Categories