Passing Activity Instance to IntentService - java

Im am trying to pass an instance of my activity to an intent service. The reason for this is the intent service does a lot of background server communication and if there is an network error or the server returns an error I want to display a pop up message.
When i create the service i use this
Intent service = new Intent(this, SyncService.class);
Bundle b2 = new Bundle();
b2.putParcelable(StringsConfig.OBJECT_DELIVERABLES, objects);
service.putExtras(b2);
startService(service);
Is there a way to pass an instance of an Activity over to it. I also have a method inside the SyncService class that accept an Activity but i dont know how to create an instance of the sync service class, pass the activity over via the method, and then start the sync service.
Any help is appreciated.

Its not a great idea to pass an Activity instance to an Intent Service. If your long running Background Service needs to show a dialog message, you are much better off modelling it as an Intent.
Just do:
Intent dialogIntent = new Intent(getApplicationContext(), YourDialogActivity.class);
dialogIntent.putStringExtra(Constants.TITLE, "Your Dialog Title");
dialogIntent.putIntExtra(Constants.MESSAGE, R.string.yourErrorMessageId);
startActivity(dialogIntent);
That way, the service contract is a lot cleaner.

The recommended way for an IntentService to communicate to an activity is via BroadcastReceiver. Take a look at this example:
In the activity that you want your IntentService to communicate with, create a BroadcastReceiver that listens for a specific intent action (a String). Here my example is called batchProcessReceiver, and listens for the BATCH_PROCESS_RECEIVER action. BATCH_PROCESS_RECEIVER can be a public static constant in your Activity.
private BroadcastReceiver batchProcessReceiver = new BroadcastReceiver() {
#Override public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BATCH_PROCESS_RECEIVER)) {
// do what you need to do here
}
}
};
In your activity's onResume:
registerReceiver(batchProcessReceiver, new IntentFilter(BATCH_PROCESS_RECEIVER));
onPause:
unregisterReceiver(batchProcessReceiver);
Then at a point in your IntentService, you can do
sendBroadcast(new Intent(MyActivity.BATCH_PROCESS_RECEIVER));
to trigger the action you want to do in your activity.

Related

Android using broadcastReceiver, but when I force close the app, I don't get anything in the Activity

Not sure how to get the receiver to work on the activity once the app is forced closed.
What am I missing to get this to work even if the app was forced closed? Any help would be appreciated.
I am getting the BroadcastReceiver service to work, Just not getting anything to pick up on the activity level.
I have my receiver (Service):
public class MyReceiver extends BroadcastReceiver {
public static final String SEND_NOTIFICATION_ACTION = "com.clover.sdk.app.intent.action.APP_NOTIFICATION";
#Override
public void onReceive(Context context, Intent intent) {
Log.i("MyReceiver", "Triggered MyReceiver");
String action = intent.getAction();
Bundle getIntent = intent.getExtras();
if (action.equals(SEND_NOTIFICATION_ACTION)) {
Log.i("MyReceiver Gotten", "Found");
intent = new Intent("broadCastName");
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("orderId", getIntent.getString("payload"));
Log.i("Receiver OrderID", getIntent.getString("payload"));
context.sendBroadcast(intent);
}
}
}
My Activity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerReceiver(broadcastReceiver, new IntentFilter("broadCastName"));
}
}
Then my broadcastReceiver in my activity:
// Add this inside your class
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("MyReceiver Gotten 2", "Found");
Bundle b = intent.getExtras();
Log.i("MyReceiver Gotten 3", b.getString("orderId"));
new SpecificOrderAsyncTask(MainActivity.this).execute(b.getString("orderId"));
}
};
Not sure how to get the receiver to work on the activity once the app is forced closed. What am I missing to get this to work even if the app was forced closed?
That's contradictory - you can't get a receiver to work in an Activity that registered it at runtime if that Activity that is hosting the receiver is killed. When you force close, every in the app process - including the Activity and the receiver you registered with it - disappears.
The point of calling registerReceiver is to listen for broadcasts only during a specific time frame or lifecycle.
If you want the receiver to work even when the app is closed, don't register it at runtime - register it in the manifest.
Simple,
Registering service in an activity is temporary, registering service in a manifest will run even after closing the application.
But the broadcast you use is a simple message transfer system, that won't work even after you register in manifest and close the application. You have to create a background service that runs always in background in android system and should awake listening to some events passed.

Periodically Updating my app's UI

I want my Android app to periodically update its UI based on the response from a REST service. I can't do this on the main thread because it's not permitted / bad practice to access the network on the main thread. The general wisdom on SO and the internet is to use a combination a BroadcastReceiver and AlarmManager. For example this is the advice here. I've tried two designs, neither of which I can make to work:
Define a class extending BroadcastReceiver as an inner class of my MainActivity.
Define the same class as an outer class.
With (1) I get this runtime error:
java.lang.RuntimeException: Unable to instantiate receiver com.dbs.alarm.MainActivity$AlarmReceiver: java.lang.InstantiationException: java.lang.Class<com.dbs.alarm.MainActivity$AlarmReceiver> has no zero argument constructor
With (2) the problem is I can't figure out how to access the view I want to modify in MainActivity.
Here is an example implementation of (1):
package com.dbs.alarm;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
// I tried making this its own class, but then findViewById isn't accessible.
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// I tried wrapping this in runOnUiThread() but it made no difference.
TextView myTextView = findViewById(R.id.my_text);
CharSequence myCharSequence = "Set from UpdateReceiver.onReceive()";
myTextView.setText(myCharSequence);
}
}
private void setRecurringAlarm(Context context) {
Intent intent = new Intent(context, AlarmReceiver.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + 1000,
1000, // Set so short for demo purposes only.
pendingIntent
);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setRecurringAlarm(this);
}
}
I also added this to my AndroidManifest.xml, and considering that I get an exception it seems to be registered successfully:
<receiver android:name="com.dbs.alarm.MainActivity$AlarmReceiver">
</receiver>
Since you need direct access to your text view, choosing an inner class for your receiver was the right thing to do. However, BroadcastReceivers that are declared as inner classes must be static to be declared in the manifest, which defeats the purpose of making it an inner class in the first place (in your scenario, at least). Because of this, I suggest registering/unregistering your BroadcastReceiver dynamically in the onStart() and onStop() lifecycle methods:
public class MainActivity extends AppCompatActivity {
private BroadcastReceiver alarmReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// the "MainActivity.this" tells it to use the method from the parent class
MainActivity.this.updateMyTextView();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setRecurringAlarm(this);
}
#Override
protected void onStart() {
super.onStart();
final IntentFilter filter = new IntentFilter();
filter.addAction("YOUR_ACTION_NAME");
registerReceiver(alarmReceiver, filter);
}
#Override
protected void onStop() {
unregisterReceiver(alarmReceiver);
super.onStop();
}
private void updateMyTextView(){
final TextView myTextView = findViewById(R.id.my_text);
if (myTextView != null){
CharSequence myCharSequence = "Set from UpdateReceiver.onReceive()";
myTextView.post(()-> myTextView.setText(myCharSequence));
}
}
private void setRecurringAlarm(Context context) {
Intent intent = new Intent("YOUR_ACTION_NAME");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + 1000,
1000, // Set so short for demo purposes only.
pendingIntent
);
}
}
You'll also notice that rather than pass in the receiver class when creating the intent for the alarm, I changed it to use a string ("YOUR_ACTION_NAME") that you can use to define the intent filter your BroadcastReceiver will use to listen for broadcasts.
As for the issue of running the updates on the UI thread, you can always call post() from a view to run something on the UI thread, or use an activity's runOnUiThread like you attempted to do within the BroadcastReceiver. I made the "update" method belong to the activity rather than the broadcast receiver, since it seemed to make more sense that way in my head.
EDIT: When writing this answer, I was more focused on solving the issues you were encountering while implementing your solution rather than actually trying to help solve the larger problem of performing periodic UI updates. #Ashikee AbHi's suggestion of using a Handler for this rather than alarm is definitely something you should consider. An alarm/broadcast receiver is great when you have to notify something in a different process, but if everything is contained in a single activity, it would be much cleaner to use Handler.postDelayed.
You can user Handler and call it recursively to perform periodic operations.Check the following
Repeat a task with a time delay?
If updating view you need to initialize it with new Handler(Looper.getMainLooper())
In the simplest way you have to run REST request in the background thread like an AsyncTask doInBackground and send result of the request to UI-thread in onPostExecute. You can do that by means of different ways, but the most convinient for me is a usage of Bus'es, for example Otto.
Okay so looking at your requirement I would say that you're fetching some data and you want to let your app know that new data has been fetched so the app can make the necessary UI changes. I would suggest using a Local Broadcast Manager , it allows you to send broadcasts within your app.
The implementation can be found pretty easily, you can check this out.
Basically the idea is you fetch data from the REST API, broadcast to your app that data has been fetched and every activity that needs to respond to this event will have a receiver that will get notified.

Fitting uri parse / intent into service [duplicate]

Android:
public class LocationService extends Service {
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
startActivity(new Intent(this, activity.class));
}
}
I launched this service from Activity
In Activity if condition satisfies start
startService(new Intent(WozzonActivity.this, LocationService.class));
from my LocationService mentioned above could not launch Activity, how can I get context of current running Activity in service class?
From inside the Service class:
Intent dialogIntent = new Intent(this, MyActivity.class);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(dialogIntent);
But, this does not work from Android 10+ due to battery optimisation restrictions
UPDATE ANDROID 10 AND HIGHER
Start an activity from service (foreground or background) is no longer allowed.
There are still some restrictions that can be seen in the documentation
https://developer.android.com/guide/components/activities/background-starts
I had the same problem, and want to let you know that none of the above worked for me.
What worked for me was:
Intent dialogIntent = new Intent(this, myActivity.class);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(dialogIntent);
and in one my subclasses, stored in a separate file I had to:
public static Service myService;
myService = this;
new SubService(myService);
Intent dialogIntent = new Intent(myService, myActivity.class);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
myService.startActivity(dialogIntent);
All the other answers gave me a nullpointerexception.
Another thing worth mentioning: while the answer above works just fine when our task is in the background, the only way I could make it work if our task (made of service + some activities) was in the foreground (i.e. one of our activities visible to user) was like this:
Intent intent = new Intent(storedActivity, MyActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
storedActivity.startActivity(intent);
I do not know whether ACTION_VIEW or FLAG_ACTIVITY_NEW_TASK are of any actual use here. The key to succeeding was
storedActivity.startActivity(intent);
and of course FLAG_ACTIVITY_REORDER_TO_FRONT for not instantiating the activity again. Best of luck!
one cannot use the Context of the Service; was able to get the (package) Context alike:
Intent intent = new Intent(getApplicationContext(), SomeActivity.class);
You can also use getApplicationContext() method in your Service to run the startActivity() method as written below:
Intent myIntent = new Intent();
myIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(myIntent);
If you need to recal an Activity that is in bacgrounde, from your service, I suggest the following link. Intent.FLAG_ACTIVITY_NEW_TASK is not the solution.
https://stackoverflow.com/a/8759867/1127429
Alternately,
You can use your own Application class and call from wherever you needs (especially non-activities).
public class App extends Application {
protected static Context context = null;
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
public static Context getContext() {
return context;
}
}
And register your Application class :
<application android:name="yourpackage.App" ...
Then call :
App.getContext();
SYSTEM_ALERT_WINDOW.requrid permission in manifest and enable from from mobile seting manully its works from android 10+.
Xiaomi Redmi 4A, Android 7, MIUI 10.2.3. Main activity for app in debug version could not start to foreground from service. There was MIUILOG- Permission Denied Activity error in logcat. Rebuilding this app in release version fixed the problem.

Why calling a class that extends Activity does not work when called from one that extends Plugin?

I have a java Class that extends Plugin (PhoneGap), but when inside this class, i call another class that extends Activity, it just doesn't work !. i mean, it seems like it doesn't get called. To confirm this, i have change my second class, this time, not extending from Activity and it works fine. i need teh second one to extends from Activity because i am using this two utilities (getFileStreamPath and openFileOutput) to create a file
File filepath = getFileStreamPath("filename.CPCL"); and openFileOutput
FileOutputStream os = this.openFileOutput(fileName, Context.MODE_PRIVATE);
I have an app with a class which extends a custom Service that calls another class which extends Activity.
First I instantiate the Activity. In the onCreate of your Plugin class use:
// get a handle on your Application
Application app = getApplication();
Intent intent = new Intent(getApplicationContext(), YourActivity.class);
app.startActivity(intent);
This will start your Activity and call the standard Lifecycle events.
The way I handle continued communication with the running Activity is by using a Handler to send a broadcast from your plugin which the Activity picks up in its receiver. In the onCreate of your plugin:
mHandler.post(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Call the Activity");
Intent intent = new Intent(YourActivity.CALL_FROM_PLUGIN);
intent.putExtra("request", <<Any extras you might want to send through>>);
sendBroadcast(intent);
}
});
In the Activity I declare the variable:
public static final String CALL_FROM_PLUGIN= "CALL_FROM_PLUGIN";
then in onCreate() I added the following;
IntentFilter filter = new IntentFilter();
filter.addAction(CALL_FROM_PLUGIN);
registerReceiver(mBroadcastReceiver, filter);
and then implemented a BroadcastReceiver:
BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "BroadcastReceiver.onReceive()");
if (CALL_FROM_PLUGIN.equals(action)) {
Log.d(TAG, "Received call from Plugin");
// DO YOUR STUFF HERE
}
}
};
Someone else might be able to point out why this is necessary from a framework point of view, but this is the way I understand that Activities should be called. I hope this applies to your plugin class the way it does with my service class!

Call Server function and popup on Broadcast receiver android

I have an application in which I have to call off an alarm/notification each 30 Minutes.
I want the feature like
1- If app is closed, it open the app, Call a dialog box. On click it will call a serverFunction and if MainActivity is running, update its UI.
2- If the app is already opened , Call a dialog box. On click it will call a serverFunction. Since MainActivity is may or may NOT on the top, update its UI Or NOT.
In My MainActivity.class
private void callNotification()
{
AlarmManager service = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pending = PendingIntent.getBroadcast(this, 0, i,PendingIntent.FLAG_CANCEL_CURRENT);
Calendar time = Calendar.getInstance();
time.setTimeInMillis(System.currentTimeMillis());
time.add(Calendar.SECOND, Constants.TIME_CONSTANT);
service.set(AlarmManager.RTC_WAKEUP ,time.getTimeInMillis(), pending);
}
public class AlarmReceiver extends BroadcastReceiver
{
#Override
public void onReceive(final Context context, Intent intent)
{
}
}
The problem here is , I can't put a dialog box in onReceive since context is not Activity context. What If the app is opened , Now how am I suppose to implement above features.
In your onReceive place this to call your activity:
Intent i = new Intent(context, AlertActivity.class);
i.setFlags
startActivity(i);
Once you are in your activity you can open up a dialog.
I recommend you use a different activity than your main one to handle displaying the alert, as it makes sense from a design standpoint and it also makes implementation easier. Remember you can make Activities look like dialogs...

Categories

Resources