I have a basic level at android, and I still have problems understanding the life cycle of apps. So I'm doing this test.
Let's say I have an app that has three classes: the MainActivity, class A and class B.
Class A receives "external" intents. I've defined the receiver in the manifest and it works well. Now, what I want is that, when class A receives and intent, it should send a "local" intent using LocalBroadcastManager. If I register the receiver on the Main Activity class it works (it receives the local intent), so I know the code for registering the receiver is ok. However, if I do it on class B, it never arrives. That is perfectly normal because I have never used/defined class B in any place, so, since class B is never executed in any way the receiver never gets registered.
Now, here are my questions (please, correct me if what I'm about to say is wrong, because I'm not sure I've understood it):
Since we don't know when the external intent will come, maybe the Main Activity (or the class B if it's defined as activity, for example) is destroyed. However, the intent will always arrive to class A because we put the receiver part in the manifest and it will broadcast the local intent. What should class A do so we are sure the other class receives it? (specially if I want that the receiver goes in class B) Should class B be something like a singleton, to be sure that one and only one class B exists, and then class A can check and create if needed class B before sending the intent? Would there be better way to do this? Maybe local broadcasts are not thought to be used in this cases (when the receiver maybe doesn't exist)?
In short, what I want is to have a class that is not in the main activity and that can be able to receive local broadcasted intents at any time, even if the app is not actively used and Android has destroyed/paused/etc it.
Thanks for your help!
Update: I'll put some code so I can show you what I've want.
First, this is class ExternalReceiver (what I previously called class A). This class just receives the external intent and broadcasts a local intent within my app:
public class ExternalReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent) {
Log.d("TestApp","ExternalReceiver: I've received the intent "+intent.getAction());
Intent localIntent = new Intent("TestIntent");
localIntent.putExtra("text", "Hello, I'm just a test");
LocalBroadcastManager.getInstance(context).sendBroadcast(localIntent);
}
}
Then, this is my MainActivity class. If I uncomment the registerReceiver, MainActivity would correctly receive the local intent and log the content of "text". My problem with this is that if the activity is destroyed, then the local intent wouldn't arrive here (I'd also prefere if another class is the receiver):
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("TestApp","MainActivity - OnCreate()");
// If I'd registered the receiver here, it would work as long as the activity exists:
//LocalBroadcastManager.getInstance(this).registerReceiver(localReceiver, new IntentFilter("TestIntent"));
}
private BroadcastReceiver localReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("TestApp","MainActivity: I've received a local intent with text: "+intent.getStringExtra("text"));
}
};
}
Now, this is LocalReceiver (the class I've called B before). What I want is to receive the local intent here. Since I've never used this class (not in the manifest and not in the code) it will never arrive here, so right now it will never receive the intent. I've extended Activity too, but I haven't put it in the manifest. In fact, I don't need it to be an activity, but I thought maybe it would be good because then I could override onCreate. Suggestions are very welcome as how should I define this class:
public class LocalReceiver extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// I register the receiver.
LocalBroadcastManager.getInstance(this).registerReceiver(localReceiver, new IntentFilter("TestIntent"));
}
private BroadcastReceiver localReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("TestApp","LocalReceiver: I've received a local intent with text: "+intent.getStringExtra("text"));
}
};
}
Finally this is my manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.testapp.localbroadcast"
android:versionCode="2"
android:versionName="#string/app_version" >
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="21" />
<application
android:allowBackup="true"
android:icon="#drawable/testapp"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.testapp.localbroadcast.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>
<receiver android:name="com.testapp.localbroadcast.ExternalReceiver" >
<intent-filter>
<action android:name="com.testapp.localbroadcast.TEST" />
</intent-filter>
</receiver>
</application>
</manifest>
Now, to test everything, I'd send this intent through adb:
adb shell am broadcast -a com.testapp.localbroadcast.TEST -n com.testapp.localbroadcast/.ExternalReceiver
What I want:
Have a receiver on ExternalReceiver so it just listens to some intent
(com.testapp.localbroadcast.TEST in my example). It works now.
ExternalReceiver then should broadcast a local intent to other class (LocalReceiver) within the app.
That class (LocalReceiver) receives the intent and then just do something
(probably without even updating any UI).
My problem:
I want to be sure that the class that receives the local intent works even after a long time. For example, if the activity doesn't exists, then ExternalReceiver should do something to assure the other class receives the local intent. My guess: make LocalReceiver an activity and then, if needed, the ExternalReceiver class should start it if it doesn't exist. I still don't get how the ExternalReceiver works even if the MainActivity is destroyed. Shouldn't MainActivity be created or resumed as soon as any ExternalReceiver starts working?
I did a previous version of this within the Main Activity and everything worked ok as long as everything existed. Now I want to separate my code and make it ready to work even if Android has closed it. Maybe I haven't understood how this kind of things works, so any suggestion is very much welcome.
Thanks again for your help!
You should put some line of code so that we can understand what you exactly need.
Also this is code of class which receive Broadcast.
Please put this as Global class.
And put some code or exact your requirement for perfect solution.
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Boradcast your local contents from here
}
}
Related
I have an issue that I have not found a solution to on this site, but if this is a duplicate question, I apologize.
I am developing an application that serves as a terminal for registering when employees start/finish work, among numerous other things. The way it works is that with NFC switched-on, they scan their NFC cards and my app reads them and ultimately sends the appropriate information to the server.
However, if the app is already open (it's supposed to be open all the time, so this is an issue) and an NFC card is scanned, it reopens the app. Of course, this is done because I have set it that way in the manifest. But I can not find a way to have my app recieve the NFC scan intent if I do not add all of these lines in the manifest:
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
I have tried just writing without the but in that case it does not read the card, but instead the program chooser comes up on the phone, or if the phone does not have an appropriate app it simply says "NFC read error".
Does anyone have a solution for this? This is the last step in my project, and I have had a lot of trouble with it, and would appreciate any help. It's probably something simple that I'm just not seeing, but I'd appreciate it either way.
Android activities have different launch modes. If you set single instance it will use already opened activity and doesn't create a new activity. You can read the new intent in override method onNewIntent()
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// read intent values here
}
For various activity elements
You can use broadcastReceiver,
- first initiate the receiver to your activity
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("whateveryouwant");
notificationBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// here you can read the intent and customize the action;
int usage = intent.getIntExtra("usage",1000);
}
}
};
second register the broadcast
registerReceiver(notificationBroadcastReceiver,intentFilter);
At end unregister to the broadcast in the onDestroy method
#Override
protected void onDestroy() {
if(notificationBroadcastReceiver != null){
unregisterReceiver(notificationBroadcastReceiver);
notificationBroadcastReceiver = null;
}
super.onDestroy();
}
after doing that instead of intenting activity you can sendBroadcast()
a little guide: https://developer.android.com/reference/android/content/BroadcastReceiver.html
hope it will be helpfull
I am making an android application, which has the following execution flow:
A service registers a PendingIntent with the AlarmManager
When the alarm is launched, a Receiver receives the intent, and (given some conditions) calls startsActivity() for my Main Activity, which in the manifest has been declared as android:launchMode="singleInstance". Note that for this call to work, the intent passed should have an Intent.FLAG_ACTIVITY_NEW_TASK
When started, Main Activity modifies itself a bit, and calls startActivityForResult for an Activity, which we'll call WebviewActivity (because it contains a webview, but that's besides the point)
When the user is done interacting with theWebViewActivity, setResult() and finish() are called on it, and one would expect for MainActivity.onActivityResult() to be called.
But of course this does not happen, as has been documented in many discussions here, the reason apparently being that an Activity launched from a singleInstance Activity, runs in a different Task.
A solution I think would be to have the WebActivity start the MainActivity instead.
The question is, is there a way to maintain onActivityResult being called at the right time? In that case, which aspects from the starting point of the execution flow should change?
Please note that MainActivity should not have multiple instances at the same time (it is basically an interface to the service) but if its launchMode is set to standard, the Receiver, because of the FLAG_ACTIVITY_NEW_TASK that is required, will do just that.
Manifest declaration of MainActivity:
<activity android:name=".activities.MainActivity"
android:label="#string/app_name"
android:launchMode="singleInstance"
android:configChanges="keyboardHidden|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
android:uiOptions=”splitActionBarWhenNarrow”
</activity>
Receiver launches MainActivity by calling
onReceive(Context context, Intent intent)
{
intent.setClass(context, MainActivity.class);
int flag = Intent.FLAG_ACTIVITY_NEW_TASK;
intent.setFlags(flag);
context.startActivity(intent);
}
I use the following workaround for this problem:
Activity A is the caller
Activity B is the singleInstance activity from which I want the result
In activity A I register a broadcast receiver as following
PickReceiver receiver=new PickReceiver();
IntentFilter filter=new IntentFilter();
filter.addAction("ActivityA_pick");
registerReceiver(receiver,filter);
class PickReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if(intent.getAction().equals("ActivityA_pick")){
//get data from intent extras
}
}
In ActivityB when it's time to send the data I use:
sendBroadcast("ActivityA_pick").putExtra("...data...");
finish();
This way i can get the result I want when I want a result from one of my own activities. If you want a result from the system or another app you can adjust this using a dummy activity that doesn't have launch mode singleInstance, have it start the activity for result and when it gets it onActivityResult it sends the broadcast to the caller.
Hope this helps
As the Main Activity is a single instance, is doing what it has been told.
So yes, you have to start the Main Activity from the Web Activity in order to be coherent with the tasks executions
I want to start a service in a static way. So from my activity I call
SpeechActivationService.makeStartServiceIntent(
this.getApplicationContext(),
"WordActivator");
Here is the actual class that extends from service class http://dpaste.com/hold/928115/ As you can see there are several log points, e.g. in the onCreate method.
This is not logged. Only if I put log text in makeStartServiceIntent method it appears, however not in the onCreate method.
Here's the makeStartServiceIntent method:
public static Intent makeStartServiceIntent(Context context,
String activationType) {
Intent i = new Intent(context, SpeechActivationService.class);
i.putExtra(ACTIVATION_TYPE_INTENT_KEY, activationType);
return i;
}
In manifest file I have
<service android:name="root.gast.speech.activation.SpeechActivationService"/>
Any ideas why the service is not started?
Aside from you not posting code showing startService(), it looks like the package name of your Service in the manifest doesn't match your SpeechActivationService class (assuming the code link you posted is the actual SpeechActivationService class in your project, and not just a class you copied from).
<service android:name="com.mkyong.android.SpeechActivationService"/>
Your makeStartService() just creates an Intent for you. You don't seem to actually be firing that intent off to start the service. Try like this
Intent i = SpeechActivationService.makeStartServiceIntent(this,"WordActivator");
startService(i);
Note that if this.getApplicationContext() works you are likely already inside of a Context object so simply using this should work also.
I have written a few Android apps, and have always declared a starting Activity as the:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
It would be great for scoping some global methods, statics, shared prefs, etc if I could start my app using an Application that then calls the first Activity from it's onCreate() after setting up prefs, etc, but I haven't been able to find any examples of this design pattern... when I try this in code, I get a ClassCastException:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
// do stuff (prefs, etc)
// start the initial Activity
Intent i = new Intent(this, InitialActivity.class);
startActivity(i);
}
}
InitialActivity.class is indeed an Activity that works fine if I set it to be MAIN, but trying to start it from MyApplication that is declared MAIN generates the error. Probably a very silly question, but am I tackling this all wrong?
Thanks,
Paul
You can fix this by using FLAG_ACTIVITY_NEW_TASK flag:
Intent intent = new Intent(this, ApplicationActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
That's because you need to start new task when Activity is started outside of Activity context. But I strongly recommend to not start Activity from your Application's onCreate().
Android has 4 components: Activity, Service, ContentProvider and Broadcast.
When Android needs to activate one of this components from your application, it looks if there is already existing running process with your application. If not, then Android starts new process, initializes it, then it initializes your custom Application instance. And then it activates one of needed components.
Now, let's consider next scenario: your application declared content provider in AndroidManifest.xml, and Android just about to start your application so you can provide some data to another foreground application.
Content Provider request is sent
Your application wasn't running, and Android starts new process for it.
Your custom Application instance is created
Application.onCreate() is called.
You start an activity
Your Content Provider receives request
Somebody just wanted to connect to your content provider, but your application started an Activity instead. Same true for starting background Service and sometimes broadcast receivers.
And also consider if some other application's activity A wanted to started activity X from your application. But in onCreate() you started activity Y, and then X is also started by Android. Then user presses back. What should happen? Its tricky...
Starting activities from Application's onCreate may result in quite weird user experience. So don't do it.
UPDATE:
Because Android guarantees that Application will be created only once and before any other component, you can use next code to access your Application's single instance:
public class MyApplication extends Application
{
private static MyApplication s_instance;
public MyApplication()
{
s_instance = this;
}
public static MyApplication getApplication()
{
return s_instance;
}
}
Did you set it in you manifest activity tag for this intent you are starting (another one besides your main) ?
</activity>
<activity android:name=".InitialActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="com.package.INITACT" /> <--- this is only name by which you activity can be called.
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
I created a broadcast receiver in the main activity and the background service which is sending broadcast intents. The application crashes each time I try to run it and the Log displays the following error message:
10-04 13:30:43.218:
ERROR/AndroidRuntime(695):
java.lang.RuntimeException: Error
receiving broadcast Intent {
action=com.client.gaitlink.CommunicationService.action.LOGIN_STATUS_UPDATE
(has extras) } in
com.client.gaitlink.GaitLink$LoginStatusReceiver#431690e8
The broadcast message is sent from CommunicationService class in the following method:
private void announceLoginStatus(){
Intent intent = new Intent(LOGIN_STATUS_UPDATE);
intent.putExtra(SERVER_MESSAGE, mServerResponseMessage);
intent.putExtra(SESSION_STRING, mSessionString);
sendBroadcast(intent);
}
where
String LOGIN_STATUS_UPDATE = "com.client.gaitlink.CommunicationService.action.LOGIN_STATUS_UPDATE"
in the main activity the following broadcast reveiver is defined:
public class LoginStatusReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String serverMessage = intent.getStringExtra(CommunicationService.SERVER_MESSAGE);
String sessionString = intent.getStringExtra(CommunicationService.SESSION_STRING);
userInfo.setSessionString(sessionString);
saveSettings();
}
}
and registered in onResume method:
IntentFilter loginStatusFilter;
loginStatusFilter = new IntentFilter(CommunicationService.LOGIN_STATUS_UPDATE);
loginStatusReceiver = new LoginStatusReceiver();
registerReceiver(loginStatusReceiver, loginStatusFilter);
And the manifest file includes the following:
<activity android:name=".GaitLink"
android:label="#string/app_name">
<intent-filter>
...
<action android:name="com.client.gaitlink.CommunicationService.action.LOGIN_STATUS_UPDATE" />
</intent-filter>
</activity>
I would really appreciate if anyone could explain why the Log displays the message above and the application crashes.
Thanks!
I solved it by adding intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); when you start a new activity.
If not started from an activity, intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); is needed.
You have two Intent filters; you only need one. If you register the BroadcastReceiver via registerReceiver(), only use the IntentFilter that is the second parameter to that API call -- do not also put an <intent-filter> element in the <activity> in the manifest.
I do not know for certain if that is your problem, but it certainly does not help.
Have you gone step by step in the debugger to find exactly where the crash occurs?
One thing to look out for is if you are overriding any Android lifecycle events that you are properly calling the base class's constructor when necessary.
when you are looking at error logs, you are just looking at first few lines.
but surprisingly actual problem is mentioned in lines way below it-
Fatal Error : Main
error receiving broadcast...bla bla bla <- you are just looking here only
at x.y.z.....
at x.y.z......
at x.y.z.....
at x.y.z......
caused by : ........................... <- but actual problem is here!
at x.y.z.....
at x.y.z......
at x.y.z.....
at x.y.z......
I'm not sure that you can understand what I want to say, because of my English.
I think this line of code is cause of a problem:
userInfo.setSessionString(sessionString);
My project was wrong because I want to:
int i = Integer.parseint(intent.getExtars("9"));
and you register two times.
i think you have to use life cycle method onPause() and onResume() Method for unregister and register broadcast intent.
I found that making sure the intent was run from the original activity and putting things into one class got rid of the entire issue.
This is a very old question, but I think many people would still be searching answers for it. My situation is quite similar to this one.
What I wanted to do, is to call finish() in the child activity when certain events occur in parent activity i.e. by sending the broadcast message from MainActivity to Child Activity.
Following is the code in MainActivity when the event is occurred (e.g. when network connection is broken etc etc):
Intent intent = new Intent("ACTIVITY_FINISH");
intent.putExtra("FinishMsg","ACTIVITY_FINISH: Network broken.");
sendBroadcast(intent);
In the child activity's OnResume() function:
IntentFilter quitFilter = new IntentFilter();
quitFilter.addAction("ACTIVITY_FINISH");
registerReceiver(m_quitReceiver, quitFilter);
In the child activity's OnPause() function:
unregisterReceiver(m_quitReceiver);
Within the child activity class:
BroadcastReceiver m_quitReceiver = new BroadcastReceiver()
{
public void onReceive(Context context, final Intent intent)
{
if (intent.getAction().equalsIgnoreCase("ACTIVITY_FINISH"))
{
Log.d("INFO", intent.getExtras().getString("FinishMsg"));
finish(); // do here whatever you want
}
}
};
Hope this helps.
It might because you keep registering BroadcastReceiver. I made that mistake before as a result it returns this error. Make sure BroadcastReceiver only registered once.
Ok what worked for me was declaring the onNotification method as follows:
window.onNotification = function(event) {
alert('onNotification called!');
};