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.
Related
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.
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!
I'm developing an app, as mentioned in title. I need somehow to manage that application will be running/counting time even when user starts/switches to another application. Well, as I learned from another discussion at stackoverflow.com, there is no need to create service that works in background and pointlessly burden processor with counting time when app is not active.
Everything what is needed to be done is to store current time when user switch to another app, compare it to time when he switches back and update the UI according to difference between these times. That's for stopwatch mode. When in timer mode, I need to automatically switch back to application according to time, that is app's UI showing when going o background. What could be the best solution suitable for this and can you give me please some simple examples for this?
Use AlarmManager for that. AlarmManager allows you to schedule tasks and get notified when they are fired.
So use AlarmManager
public class MainActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//call function where you want
timeout();
}
public void timeout()
{ //time in milliseconds 1 minute
Long time = new GregorianCalendar().getTimeInMillis()+60*1000; //i.e.60*1000=1minute
// create an Intent and set the class which will execute when Alarm triggers, here we have
Intent intentAlarm = new Intent(this, AlarmReciever.class);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP,time, PendingIntent.getBroadcast(this,1, intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT));
}
}
Here is broadcast class:
public class AlarmReciever extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
// show dialog or what you want
}
}
Don't forgot to edit AndroidMainfest:
//permission
<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
//our receiver
<receiver android:name=".AlarmReciever"/>
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.
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...