I have created an android application which has two activities, one of them is MainActivity and the other one is Article activity
In this application you can open unlimited number of activities. So it's like an article app, inside an article there are also other articles so you can follow unlimited number of articles. Every article opens a new Article activity.
Now what I want to do is this: When the 10th Article is opened I want to close the first Article activity (not MainActivity).
I have also read this question, but it works only if you have different activities (i.e. ActivityA, ActivityB, ActivityC...).
Is there any way I can do this?
When opening 10th article, do this:
Intent intent = new Intent(this,Your_Article.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
or if you only want to close a particular activity, get the instance of that activity and close it using finish().
this is a hack only to get it working but you may want to try fragment from your description;
Create a new class called ActivityHandler.java
public class ActivityHandler {
private static ActivityHandler uniqueInstance;
private static List<Activity> mListActivity;
private static int SIZE_LIMIT = 10;
public static ActivityHandler getInstance() {
if (uniqueInstance == null) {
synchronized (ActivityHandler.class) {
if (uniqueInstance == null) {
uniqueInstance = new ActivityHandler();
}
}
}
return uniqueInstance;
}
private Activityhandler() {
if (mListActivity == null ) {
mListActivity = new ArrayList();
}
}
public static void add (Activity activity){
mListActivity.add(activity);
if (mListActivity.size() > 10){
Activity firstActivity = mListActivity.remove(0);
firstActivity.finish();
}
}
}
In your ArticleActivity's OnCreateMethod:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.article_activity);
ActivityHandler.getInstance().add(this);
}
Related
I have a SplashScreen Activity which call Asynctask Class to get information in internet.
I want to wait while my Asynctask is not finish (time during on internet speed connection)
My activity:
public static boolean test = true;
[...]
final Liste en_ce_moment = new Liste("En ce moment au cinéma", nowMovie);
mesListes.add(en_ce_moment);
//call my Asynctask file
fetchNowMovie process = new fetchNowMovie();
process.execute();
while(test)
{
}
Intent i = new Intent(SplashScreenActivity.this, MainActivity.class);
startActivity(i);
My Asynctask:
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
SplashScreenActivity.test = false;
SplashScreenActivity.nowMovie.clear();
SplashScreenActivity.nowMovie.addAll(list);
}
Logically, the boolean became false in onPostExecute so the while loop stop and the intent have to start but the while loop never stop...
Let's do what you want to do in a safe way by using simple interface logic:
So we added our simple interface and we re-define your MyAsnycTask class's constructor like so:
public class MyAsnycTask extends AsyncTask
{
OnTaskFinished listener;
// Our simple interface
public interface OnTaskFinished {
void TimeToNextActivity();
}
// Your MyAsnycTask class constructor
public MyAsnycTask(OnTaskFinished l) {
listener = l;
}
. . .
As a last line of code in onPostExecute(), we're done whatever we're doing. So tell this via our listener:
listener.TimeToNextActivity();
To use our interface that we added earlier, your Activity must implements it. So we implements it. And in implemented method, we go to next Activity with Intent:
public class MyActivity extends Activity
implements MyAsnycTask.OnTaskFinished
{
#Override
public void TimeToNextActivity()
{
// Here go to next activity
Intent i = new Intent(this, MainActivity.class);
startActivity(i);
}
As we modified our MyAsnycTask class's constructor, we must initialize it like this:
MyAsnycTask process = new MyAsnycTask(this);
process.execute();
This looks like a problem hiding inside another problem, but to get through the first-level issue you could try using CountDownLatch instead of a static boolean:
CountDownLatch latch = new CountDownLatch(1);
fetchNowMovie process = new fetchNowMovie(latch);
process.execute();
latch.await();
Intent i = new Intent(SplashScreenActivity.this, MainActivity.class);
startActivity(i);
You'll have to accept the latch as part of your AsyncTask's constructor:
private final CountDownLatch latch;
public fetchNowMovie(CountDownLatch latch) {
this.latch = latch;
}
// ...
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
latch.countDown();
SplashScreenActivity.nowMovie.clear();
SplashScreenActivity.nowMovie.addAll(list);
}
Try to use very simple library:
https://github.com/Arasthel/AsyncJobLibrary
Just start your Splash activity:
Intent i = new Intent(context, SplashScreenActivity.class);
startActivity(i);
In "onCreate" method of Splash activity do, what you need in background and after mission complete, do on main thread (update list):
AsyncJob.doInBackground(new AsyncJob.OnBackgroundJob() {
#Override
public void doOnBackground() {
//load from local DB or http-request:
List<Movie> movieList = fetchNowMovie();
//You can convert movieList to Json string, for example and save in SharedPreferences. Or you can use local DB for saving new movies.
// Send the result to the UI thread and show it
AsyncJob.doOnMainThread(new AsyncJob.OnMainThreadJob() {
#Override
public void doInUIThread() {
Intent i = new Intent(splashScreenContext, MainActivity.class);
startActivity(i);
//load movies from shared preferences or local Database in MainActivity (onCreate)
}
});
}
});
Connect library (build.gradle in app-project directory):
dependencies {
...
implementation 'com.arasthel:asyncjob-library:1.0.3'
...
}
PlaylistFragment starts an adapter:
playlistsAdapter = new PlaylistRecyclerAdapter(playlistsListArray, addToPlaylist, mSong, getActivity(), this);
PlaylistRecyclerAdapter binds data to the PlaylistViewHolder, something like this:
((PlaylistViewHolder) viewHolder).bind(this, dataSet.get(position), addToPlaylist, mSong);
User clicks on an item in PlaylistViewHolder:
context.startActivity(PublicPlaylistActivity.createStartIntent(context, playlist));
Now here is the question, how can PublicPlaylistActivity talk back to the initial PlaylistFragment?
I suggest you'd better use Interface from fragment to adapter. So when user clicks anything in adapter, call function realization in fragment. If you need your activity to proceed some operation - ((YourActivity) getActivity()).someMethod() should be called from fragment.
Second trick is using broadcastreceiver to send events. A bit more complicated. You have to launch broadcast in view you need to recive message and send these messages from adapter. This approach is more complexible to debug and support if system is wide spread, so you'd better use interfaces.
There are several ways of doing that. The simplest way should be starting the PublicPlaylistActivity with startActivityForResult. In that way, then the activity finishes, you can set send some data to the caller fragment (which is PlaylistFragment in your case). Here is a nice tutorial about the implementation.
Another way of doing that is by using lifecycle methods. You might have a public static variable which can keep track of some status that you might observe in your onResume function of your PlaylistFragment when you are returning back from your PublicPlaylistActivity. You might consider a sample implementation as follows.
Define a public static variable in your PlaylistFragment. Then in your onResume function check the value of that variable and take actions accordingly.
public static boolean someIndicator = false; // Initialize with a default value
#Override
protected void onResume() {
super.onResume();
if(someIndicator == true) doSomething();
else doSomethingElse();
}
Now you can set the indicator variable from anywhere in your application actually which will have the effect on your PlaylistFragment. For example, from your PublicPlaylistActivity, you might consider doing something like this.
public void someFunctionInYourPublicPlaylistActivity() {
// ...
// Some code and then the following
PlaylistFragment.someIndicator = true;
}
Another way of achieving the same thing is by using a BroadcastReceiver. Here is a tutorial on how you can implement one.
It really depends on how you are structuring your whole activity-fragments communication. Hope that helps!
I would do a common "context" class (ComContext) with an interface. When you create your fragment, you also create this class. And from the activity you can check if it exists or not.
I assume that you already have a helper(AppHelper) class with static variables.
public class AppHelper {
public static ComContext comContext = null;
}
public class MainFragment {
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ConContext comContext = new ComContext();
comContext.listener = this;
AppHelper.comContext = comContext;
}
#Override
public void onDataChanged() {
}
#Override
public void onDestroyView() {
super.onDestroyView();
AppHelper.comContext = null;
}
}
public class MainActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (AppHelper.comContext != null) {
AppHelper.comContext.listener.onDataChanged();
}
}
}
public class ComContext {
public interface HelperListener {
void onDataChanged();
}
public HelperListener listener = null;
}
I'm working on an alarm clock and I can't figure out how to sendEvent to React Native from MainActivity. This is what I managed to do so far:
#Override
protected void onCreate(Bundle savedInstanceState) {
mInitialProps = new Bundle();
final Bundle bundle = mActivity.getIntent().getExtras();
ReactInstanceManager mReactInstanceManager = getReactNativeHost().getReactInstanceManager();
ReactApplicationContext context = (ReactApplicationContext) mReactInstanceManager.getCurrentReactContext();
if (context == null) {
mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
public void onReactContextInitialized(ReactContext context) {
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
LauncherModule.startAlarm(mActivity); // works
LauncherModule.sendAlarmEvent(); // doesn't work. Should run after alarm manager starts app which previously had been killed
}
}
}
});
} else {
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
LauncherModule.startAlarm(mActivity); // works
LauncherModule.sendAlarmEvent(); // works and sends event only when app was left open
}
}
}
super.onCreate(savedInstanceState);
}
The code works only If app is left open and alarm manager restarts app itself. If I close the app and alarm manager starts it then it seems that only startAlarm function (it has sound effect) is beeing triggered..
No matter what I do whether I put sendEvent function inside Mainactivity or elsewhere (e.g. external module) it simply won't send event if I close the app. I also tried getReactInstanceManager().getCurrentReactContext() combined with while from this question Send data from Android activity to React Native to no avail.
Also tried to create bolean beeing set to true onCreate and then send event onStart or onRestart. Also to no avail.
Any suggestions?
EDIT: Here is how sendEvent function looks like:
public final void sendEvent(String eventName, boolean isAlarmOn) {
getReactInstanceManager().getCurrentReactContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, isAlarmOn);
}
SOLUTION
Well I think that the answer is not to use sendEvent method onCreate because (I might be wrong) listener seems to be initialized after the event had been sent. So nothing is going to listen to this event.
It seems to work pretty well inside onStart, onRestart, onPause though.
What can we do? React Native provides ReactActivityDelegate with initial props. And it does the job!
ReactActivityDelegate in MainActivity should look as below:
public class ActivityDelegate extends ReactActivityDelegate {
private Bundle mInitialProps = null;
private final #Nullable Activity mActivity;
public ActivityDelegate(Activity activity, String mainComponentName) {
super(activity, mainComponentName);
this.mActivity = activity;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
mInitialProps = new Bundle();
final Bundle bundle = mActivity.getIntent().getExtras();
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
mInitialProps.putBoolean("alarmOn", true);
}
}
super.onCreate(savedInstanceState);
}
#Override
protected Bundle getLaunchOptions() {
return mInitialProps;
}
};
#Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ActivityDelegate(this, getMainComponentName());
}
Then in your main app component (usually index.android.js) call your propTypes and use them to run your code:
static propTypes = {
alarmOn: PropTypes.boolean
}
componentDidMount() {
if (this.props.alarmOn === true) {
// your code
}
}
Voila!
You can find full example here: https://github.com/vasyl91/react-native-android-alarms
I get an error
java.lang.NoSuchMethodError: android.app.Activity.isDestroyed
It has something to do with only running on devices which are api 17 or higher, is there anyway around this?
private WeakReference<Activity> mActivityRef;
#Override
public void onFinish() {
Activity activity = mActivityRef.get();
// if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
//do your thing!
if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
activity.startActivity(new Intent(activity, ListOfAlarms.class));
activity.finish();
}
mStarted = false;
// }
// Intent goBack = new Intent(CountDownAct.this, ListOfAlarms.class);
// startActivity(goBack);
// finish();
}
This API was added only in API Level 17 Check this!
To avoid the crash, you can verify with the following code
if(Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1){
//do your thing!
}
To get it working on lower API levels, you can create your own BaseActivity.java and add code like this
public class BaseActivity extends Activity {
private boolean mDestroyed;
public boolean isDestroyed(){
return mDestroyed;
}
#Override
protected void onDestroy() {
mDestroyed = true;
super.onDestroy();
}
}
Now, make all your activities extend this BaseActivity as follows
public class MainActivity extends BaseActivity {
...
}
Hope this helps! Please accept this answer if it worked for you :)
EDIT (After OP added code snippets)
First create a BaseActivity.java file as I have shown above.
Then, make all your activities extend BaseActivity instead of Activity.
Now, change
private WeakReference<Activity> mActivityRef;
to
private WeakReference<BaseActivity> mActivityRef;
And change
Activity activity = mActivityRef.get();
to
BaseActivity activity = mActivityRef.get();
I am creating an Activity which communicates with a Service to download some data from internet via POST method. To do this, I use Messenger. Here is my code to make it clearer for you:
My onCreated() method:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comments);
CommentsHandler commentsHandler = new CommentsHandler(this, savedInstanceState);
Messenger messenger = new Messenger(commentsHandler);
Intent serviceIntent = new Intent(this, WindowService.class);
serviceIntent.putExtra("messenger", messenger);
serviceIntent.putExtra("state", 888);
serviceIntent.putExtra("number", getIntent().getStringExtra("number"));
startService(serviceIntent);
}
The code in my Service's thread to post the result data to the Activity via the Messenger object:
/** ... **/
Messenger messenger = intent.getParcelableExtra("messenger");
/** ... **/
Message resultMsg = this.obtainMessage();
resultMsg.obj = jParser.getArrayList(); //This is an ArrayList of my downloaded data.
messenger.send(resultMsg);
The code in the Activity to handle the Message from the Service:
public static class CommentsHandler extends Handler {
Bundle mSavedInstanceState;
ActionBarActivity activity;
public CommentsHandler(Activity a, Bundle savedInstanceState) {
activity = (ActionBarActivity) a;
mSavedInstanceState = savedInstanceState;
}
#Override
public void handleMessage(Message msg) {
comments = (ArrayList<HashMap<String, String>>) msg.obj;
if (mSavedInstanceState == null && msg.arg1 != 793) {
activity.getSupportFragmentManager().beginTransaction()
.add(R.id.container, new CommentsFragment()).commit();
} else if (msg.arg1 == 793) { //793 is my preferred code to determine
//if the internet connection could not be
//established when the Service was trying
//to download the data.
activity.finish();
}
}
}
The problem is: if I open the Activity and close it before the data is downloaded, this code .add(R.id.container, new CommentsFragment()).commit(); gives me the error Can not perform this action after onSaveInstanceState, because this code only gets executed after the data in my Service is processed and sent via the Messenger object, but at that time the Activity is already closed by the user so the Fragment cannot be added. How to solve this issue? How to check if the Activity is not closed/being closed before adding the Fragment? Or, better, how to stop the thread in which that code is running on Activity's onDestroy() method so it doesn't get executed if the Activity is closed? Thanks in advance!
In your activity, you should create a boolean to check if the activity is visible or not:
public ActionBarActivity extends Activity {
private boolean isActivityVisible = false;
#Override
protected void onResume(){
isActivityVisible = true;
}
#Override
protected void onPause(){
isActivityVisible = false;
}
public boolean isVisible(){
return this.isActivityVisible;
}
}
And then you modify your Handler class definition:
public static class CommentsHandler extends Handler {
Bundle mSavedInstanceState;
ActionBarActivity activity;
public CommentsHandler(Activity a, Bundle savedInstanceState) {
activity = (ActionBarActivity) a;
mSavedInstanceState = savedInstanceState;
}
#Override
public void handleMessage(Message msg) {
// here you check if your activity is no longer visible and then break up
if(activity == null || !activity.isVisible())
return;
comments = (ArrayList<HashMap<String, String>>) msg.obj;
if (mSavedInstanceState == null && msg.arg1 != 793) {
activity.getSupportFragmentManager().beginTransaction()
.add(R.id.container, new CommentsFragment()).commit();
} else if (msg.arg1 == 793) { //793 is my preferred code to determine
//if the internet connection could not be
//established when the Service was trying
//to download the data.
activity.finish();
}
}
}
The smallest change would be to have a boolean field in the Activity, setting it to true in onResume() and to false in onPause(), and check its value in handleMessage() (i.e. ignore the message if the flag is currently false).
Another option, instead of using Messenger and handleMessage(), do this with a BroadcastReceiver. Register the receiver in onResume() and unregister it in onPause(). That way the broadcast from the service will be simply ignored.
Both solutions are basically the same, anyway, but broadcasts are somewhat "higher level".
This assumes that you're not interested in the Service's result if the activity is paused. If you are (for example, if you switch out of the application and back in, and you need to display the update) then you should put the received data in a field and process it on the following onResume().
Your way of doing this is different than how I would handle it but using what you have I would make these adjustments:
public static class CommentsHandler extends Handler {
Bundle mSavedInstanceState;
ActionBarActivity activity;
public CommentsHandler(Activity a, Bundle savedInstanceState) {
activity = (ActionBarActivity) a;
mSavedInstanceState = savedInstanceState;
}
public void setActivity(Activity a){
activity = a;
}
#Override
public void handleMessage(Message msg) {
if(activity == null){
return;
}
comments = (ArrayList<HashMap<String, String>>) msg.obj;
if (mSavedInstanceState == null && msg.arg1 != 793) {
activity.getSupportFragmentManager().beginTransaction()
.add(R.id.container, new CommentsFragment()).commit();
} else if (msg.arg1 == 793) { //793 is my preferred code to determine
//if the internet connection could not be
//established when the Service was trying
//to download the data.
activity.finish();
}
}
}
Then I would use your activities onPause()/onResume() methods to like this:
#Override
protected void onPause() {
super.onPause();
commentsHandler.setActivity(null);
}
#Override
protected void onResume() {
super.onResume();
commentsHandler.setActivity(this);
}