LocalBroadcastManager not receiving messages correctly - java

I have a, what I thought was a, simple task. I have a list of achievements in a recyclerView in my MainActivity. Clicking on one of the achievements launches the AchievementDetailActivity. You do an action in the AchievementDetailActivity and it completes the achievement. When that happens, I need the thumbnail on the MainActivity to be the achievement icon and not the locked icon. What I thought was I could just use the LocalBroadcastManager to send and receive messages from one activity to another, but that isn't working. From everything I have read you are supposed to unregister the listener in your activity onPause and onStop lifecycle methods, however, if you unregister the listener in the onPause of MainActivity, it gets unregistered when the AchievementDetailActivity starts and nothing gets delivered to the MainActivity. I don't understand how you can use LocalBroadCastManager with receivers to send information between activities when they get unregistered as soon as you start a new activity. The following example shows it unregisters as soon as the second activity is started, so the first activity will never get the broadcast...
public class MainActivity extends AppCompatActivity {
public static final String ACTION = "update";
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("MAIN ACTIVITY", "RECEIVED EVENT");
}
}
public void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver(receiver), new IntentFilter(ACTION));
}
public void onPause() {
Log.d("MAIN ACTIVITY", "REMOVING LISTENER");
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
}
//.. The rest of MainActivity
}
public class SecondActivity extends AppCompatActivity {
#Override
public void onStart() {
super.onStart();
//initialize view and such
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext());
Intent intent = new Intent(MainActivity.ACTION);
intent.putExtra("Something", "somewhere");
manager.sendBroadcast(intent);
}
)};
}
If you run the above, obviously with the buttons and such, you will get a message that shows it unregisters the listener as soon as SecondActivity starts and the MainActivity will never get the message it is supposed to get when you click a button on the second activity.
Is there a way to send information from activity to activity, or because of the lifecycle events is that not possible? Everything I read said LocalBroadcastManager was the right way to do it, but it gets unregistered so, how can it? Please help, this is driving me nuts. Thank you.

If you want to use your LocalBroadcastManager to fetch results, do not unregister in onPause, in which case must to unregister in onDestroy().
If you startAvtivity only to fetch some results, it is a good way to startActivityForResult.

Its not a great idea to use LocalBroadcastReceiver for this purpose. Use startActivityForResult instead. Follow this Official doc for implementation details.
The reason its not wokring is:
You are registering the receiver in onStart and unregistering it in onPause(). When your second activity is shown, onPause () will be called as a result the BroadcastReceiver is unregistered.
Its not a great idea to keep it registered since it would lead to memory leaks.

Related

Send data from BroadcastReceiver to activity, while app is in background

I am writing my code in Android Studio, but the project that I am writing has no GUI, and never updates views for a user. It simply listens for the last installed app with a Broadcast Receiver and sends info to another java file.
I am wondering how I can send a string from a broadcast receiver to an activity (maybe this should not be an "activity" but a "service"?), and have this work while the app is in the background.
Right now I have a broadcast receiver listening for the last installed app on my phone (this runs in the background). And I have a custom broadcast receiver set up to notify my main activity when the broadcast receiver gets an installed app. But this custom receiver doesn't work in the background. Again, maybe I shouldn't be using an Activity to receive info from the BroadcastReceiver?
Is there a way I can send information from my broadcast receiver to my main activity while the app is in the background? I also need my main activity to resume normal function while in the background (there are no GUI updates done while it's in the background).
My BroadcastReceiver which is sending data to my Main Activity through a new Intent
public class NewInstallReceiver extends BroadcastReceiver {
ApplicationInfo ai;
#Override
public void onReceive(Context context, Intent intent) {
final PackageManager pm = context.getPackageManager();
try {
// if app is added, get package info
ai = pm.getApplicationInfo(intent.getData().getSchemeSpecificPart(), 0);
Intent i = new Intent("broadcastName");
// Data pass to activity
i.putExtra("appInfo", ai);
context.sendBroadcast(i);
} catch (final PackageManager.NameNotFoundException e) {
ai = null;
}
}
}
In my MainActivity in onCreate I register the receiver:
registerReceiver(broadcastReceiver, new IntentFilter("broadcastName"));
I receive data from BroadcastReceiver
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// get applicationInfo
Bundle b = intent.getExtras();
Object applicationInfo = b.get("appInfo");
ApplicationInfo appInfo = (ApplicationInfo) applicationInfo;
getNewData(appInfo);
}
};
Then I have to unregister the receiver in MainActivity, but obviously this doesn't allow for me to receive info from BroadcastReceiver once the app goes in the background
#Override
public void onStop()
{
try{
unregisterReceiver(broadcastReceiver);
}
catch(Exception e){
System.out.println("Exception is " + e.toString());
}
super.onStop();
}
Honestly, I don't think that update an Activity that is in background is a good practice (even if possible).
According to DOCS:
An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with setContentView(View). While activities are often presented to the user as full-screen windows, they can also be used in other ways: as floating windows... or embedded inside of another activity (using ActivityGroup).
So, if the Activity is in background, it does not visible. So, it is useless to update its contents (because it is not visible to user).
In fact, you should implement following behavior instead:
Use your Service only to save the information (in a database/content provider) and to notifies the Activity that a new information is available. Then, if your activity is open, it immediately consumes that information. If Activity is not opened, the info remains saved for when the user needed it.
If app was only stopped, you can update the Activity content during onResume() method.
If your app was killed and user opened it again, you can update ALL Views during onCreate() method. onCreate() is called before the View is displayed to user.
This way, the info will be updated when needed: when the user wan's to check the info. Otherwise, it is a waste of time.

proper android activity flow?

I am trying to wrap my head around what proper activity flow convention is.
I currently have:
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
//do stuff
//clicklisteners setup etc
Intent intent = new Intent(this, ExampleActivity.class);
//putExtras
startActivity(intent);
}
}
public class ExampleActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
//getExtras
//objectA state lives here
//do stuff
}
}
If the user presses back when on the ExampleActivity view, and then clicks another listener that takes them to ExampleActivity, I want to be able to access "objectA" state again. How do I implement this? I am not understanding onResume or onRestart...
are these the methods to call? or is there a better convention to navigate the app activities?
Android has a mechanism for having an activity pass results back to the prior activity that started it. The documentation for that is here.
Basically, you use startActivityForResult to start the second activity, the second activity uses setResult to set results, and the first activity receives those results in the onActivityResult callback when the second activity finishes.
If the user presses back when on the ExampleActivity view, the ExampleActivity is dead and user is back in the MainActivity, which calls "onResume".
When your are back from activity1 to activity2, activity2's onResume method is called.
With that being said, after the user closed ExampleActivity objectA is destroyed.

Android - Activity finish() results black screen

I have an AlertActivity and an Activity. When a broadcast is received, both activities needs to finish. But the below code results Black screen if AlertActivity is on top of Activity.
Below is the code in Activity:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("BROADCAST_INTENT")){
if(alertActvity != null)
alertActivity.finish();
finish();
}
}
And code in AlertActivity:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("BROADCAST_INTENT"))
finish();
}
}
First, Activity's onStop() is getting called before AlertActivity's onStop() is called which results in Black screen, even AlertActivity's finish() called before Activity's finish().
Please help me in this regard.
Finally, I found a solution for this:
Finishing an Activity with a delay of 1 second which really works. By that time, AlertActivity finishes and black screen cannot be displayed.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
finish();
}
}, 1000);
as in both AlertActivity and Activity, you are checking for same action "BROADCAST_INTENT", I assume you registered both receiver in their own class.
If you did so, then actually you have two broadcast receiver waiting for same event. when this event occurs, both of your receiver are going to get it.
So in your AlertActivity is getting finished twice.
I think #Amit K. Saha, is right, your AlertActivity may be finishing twice
Solution :
If your application is running >= API 16 then you can use finishAffinity() method :
Finish this activity as well as all activities immediately below it in the current task that have the same affinity. This is typically used when an application can be launched on to another task (such as from an ACTION_VIEW of a content type it understands) and the user has used the up navigation to switch out of the current task and in to its own task. In this case, if the user has navigated down into any other activities of the second application, all of those should be removed from the original task as part of the task switch.
Note that this finish does not allow you to deliver results to the
previous activity, and an exception will be thrown if you are trying
to do so.
You can call finishAffinity() from AlertActivity because it is on top of Activity. This will finish AlertActivity as well as Activity
My transparent Activity finish results black screen, after a search, i find it is caused by activity switching animation in Android 4.4. But above android 5.1 the phenomenon does not show up.
So I add the below code:
#Override
public void finish() {
super.finish();
overridePendingTransition(0, 0);
}
The black screen after finish is gone.
I think this may be helpful.
try this
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("BROADCAST_INTENT"))
context.finish();
}
}

Android: how do I tell my activity to start new activity from my service?

I have a service that is, among other things, downloading images from internet. When this is done I want to show this image in a custom Activity that has a dialog theme. But I only want to use this pop up if the app is running, otherwise I just want to create a notification.
But I get an exception when I try to start an activity from my service and i feel that maybe this isn't the right way to do it?
It says:
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
So my question is if this is the right way to do this by setting that flag or how should I get my downloaded image from my service to an activity. Can I in some way tell an activity to start a new activity from my service class?
I think using Broadcast Receiver is better option for you.
Add Below Method in Service and call this method when image Download Complete.
private void updateMyActivity(Context context) {
if(MainActivity.activityStatusFlag){
//update the activity if activityStatusFlag=true;
Intent intent = new Intent("mUpdateActivity");
context.sendBroadcast(intent);
}else{
//display notification if activityStatusFlag=false;
}
}
In Activity Add Following Code.
public class MainActivity extends Activity{
public static boolean activityStatusFlag= false;
//define this variable to check if activity is running or not.
#Override
protected void onResume() {
super.onResume();
activityStatusFlag = true;
this.getApplicationContext().
registerReceiver(mMessageReceiver,new IntentFilter("mUpdateActivity"));
}
#Override
protected void onPause() {
super.onPause();
activityStatusFlag = false;
this.getApplicationContext().unregisterReceiver(mMessageReceiver);
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Display Popup or update Activity
}
};
}

Android: Starting an intent inside of onCreate is causing an infinite loop / crash

I'm pretty new to Android/java, and have come up against my first head-scratcher.
I'm trying to conditionally change the starting view based on if a user is logged in (I've set is_logged_in already, here, to more simply illustrate the problem).
Many thanks.
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
boolean is_logged_in = false;
if(is_logged_in) {
setContentView(R.layout.activity_main);
} else {
menu_sign_up(); // How would I pass the View?
}
}
// Doesn't work, at least when called inside of MainActivity's onCreate
public void menu_sign_up(/* View view */) {
Intent intent = new Intent(this, SignUp.class);
startActivity(intent);
}
// Example of a working method, at least when called from android:onClick in XML
public void menu_sign_in(View view) {
Intent intent = new Intent(this, SignIn.class);
startActivity(intent);
overridePendingTransition(0,0);
}
}
public class SignUp extends MainActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sign_up);
}
// ...
}
***** Truncated Stack Trace Below ******* (adb logcat *:W)
W/ActivityManager(19583): Activity idle timeout for HistoryRecord{40953fd8 com.example.digitalkarmaeclipse/.SignUp}
W/ActivityManager(): Activity idle timeout for HistoryRecord{}
W/ActivityManager(21900): Launch timeout has expired, giving up wake lock!
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a23580 com.example.digitalkarmaeclipse/.MainActivity}
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a2c6f8 com.example.digitalkarmaeclipse/.SignUp}
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a30100 com.example.digitalkarmaeclipse/.SignUp}
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a338e8 com.example.digitalkarmaeclipse/.SignUp}
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a376e0 com.example.digitalkarmaeclipse/.SignUp}
Your current app structure doesn't make a lot of sense. Does SignUp have to be an Activity as well? You could use an AlertDialog to do something similar. If it does need to be an Activity then have it extend Activity, it doesn't make much sense to extend HomeActivity in this context.
I also don't like the idea of putting setContentView() in a logical statement. onCreate() is only called when the Activity is created. So if you start your SignUp Activity and then press the back button, there won't be a layout for your HomeActivity.
edit: And like Shark said it doesn't look like you have a way to pass the data from SignUp back to HomeActivity. You should try to use startActivityForResult() and then use setResult() in SignUp to say whether it was successful or not.
You need to let onCreate() finish, then move your new Activity to onResume() or onStart(); those get called as your activity is shown to the user instead of it still initializing (itself).
Also, it looks like you need to set up a
onLoginFinished(int result);
callback... either try running your login with startActivityForResult() and catching the result in onActivityResult()
OR ...
you make your own interface and once the other activity finishes it, it invokes the onLoginFinished(isOK) callback where you determine which screen to use.

Categories