21

My app normally works just fine, until I face a strange problem on specific device. There are 2 activities in App. After I start ActivityB inside of ActivityA, ActivityA starts with no issue. However, after I go back to the ActivityA with pushing back hardware button or calling finish(); inside of closeButton in ActivityB, ActivityA reloads itself. It triggers onCreate() again and reloads all its contents. And I'm not changing orientation of phone. This strange behavior only appears in 15 phones over 1.000 download of app.

This problem only occurs on Galaxy S3 Android OS 4.1.2. And this is also strange.

Do you have any idea why this is happening?

When I start a new Activity inside of button listener like this:

ActivityA.java (MesajlarListViewActivity)

    public class MesajlarListViewActivity extends TrackedActivity {

    Context context = null;

    // contacts JSONArray
    JSONArray contacts = null;

    ArrayList<Message> productArray = new ArrayList<Message>();

    private ProductAdapter adapter;
    private ListView productList;
    private Runnable viewOrders;
    private HoloProgressIndicator profilInfoProgress = null;

    ImageView kapatButton = null;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mesajlar_list);

        context = this;

        kapatButton = (ImageView) findViewById(R.id.kapat_button);
        /* kapat button onclick listener. */
        // =================================================================================================================
        kapatButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view)
            {
                // Set vibration on touch.
                KnetGenericClass.vibratePhone(context);

                finish();
            }

        });
        // =================================================================================================================
        //Progress bar.
        profilInfoProgress = (HoloProgressIndicator) findViewById(R.id.profil_info_progress);

        // cheking internet connectivity.
        if(KnetGenericClass.checkInternetConnection(context))
        {
            // start task!
            /* internet var ise web service baglantisi kurmaya baslayabiliriz. */
            startActivityIndicatorWithThread();
        }
        else
        {
            KnetGenericClass.printErrorMessage(context, "Bağlantı Hatası",
                    "Lütfen internet bağlantınızı kontrol ediniz.");
        }

        productList = (ListView) findViewById(R.id.product_list);
        adapter = new ProductAdapter(this, R.layout.message_row, productArray);
        productList.setAdapter(adapter);

        // When user click a view on list view new page is appearing.
        productList.setOnItemClickListener(new OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view, int position, long id)
            {

                // Set vibration on touch.
                KnetGenericClass.vibratePhone(context);

                /* Navigate to message detay activity class with ilan ID. */
                Intent myIntent = new Intent(view.getContext(), MesajDetayActivity.class);
                myIntent.putExtra("messageID", productArray.get(position).getId());
                startActivity(myIntent);

                // setting image of clicked message null.
                RelativeLayout relativeLayout = (RelativeLayout) view;
                ImageView unreadedImageView = (ImageView) relativeLayout.findViewById(R.id.unreaded_image);
                unreadedImageView.setImageResource(0);
            }
        });
    }

    public class ProductAdapter extends ArrayAdapter<Message> {
        ArrayList<Message> items;

        public ProductAdapter(Context context, int textViewResourceId, ArrayList<Message> objects) {
            super(context, textViewResourceId, objects);
            this.items = objects;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent)
        {
            if(convertView == null)
            {
                LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = vi.inflate(R.layout.message_row, null);
            }

            ImageView unreadedImageView = (ImageView) convertView.findViewById(R.id.unreaded_image);
            TextView productName = (TextView) convertView.findViewById(R.id.product_name);
            TextView productDetail = (TextView) convertView.findViewById(R.id.product_detail);
            // TextView productDate = (TextView)
            // convertView.findViewById(R.id.product_date);
            TextView sentDate = (TextView) convertView.findViewById(R.id.product_date);

            productName.setText(items.get(position).getSender());
            productDetail.setText(items.get(position).getTitle());
            // String bodyNoHTML = items.get(position).getBody();

            if(items.get(position).getIsReaded())
            {
                unreadedImageView.setImageResource(0);
            }
            else
            {
                unreadedImageView.setImageResource(R.drawable.bluedot);
            }

            String dateStr = items.get(position).getSentDate();
            try
            {
                sentDate.setText(dateStr.substring(6, 8) + "." + dateStr.substring(4, 6) + "." + dateStr.substring(0, 4)
                        +" "+dateStr.substring(8, 10)+":"+dateStr.substring(10, 12));
            }
            catch(Exception e)
            {
                sentDate.setText("");
            }


            return convertView;
        }

    }// @end of product adapter class.

    /* web service'e baglanti kurulan methodu threadin icerisinde cagiriyoruz. */
    public void startActivityIndicatorWithThread()
    {
        // ==============================================================================================
        // getting ilan details into arraylist.
        // setting up thread.
        viewOrders = new Runnable() {
            public void run()
            {
                getMessageListFromWebService();
            }
        };
        Thread thread = new Thread(null, viewOrders, "MagentoBackground");
        thread.start();
        profilInfoProgress.start();
        // ==============================================================================================
        // @end of the thread declaration.
    }

    public void getMessageListFromWebService()
    {
        // Creating JSON Parser instance
        JSONParser jParser = new JSONParser(context);

        // getting JSON string from URL
        JSONArray jsonArray = jParser.getAuthorizedInfoFromUrlToJSONArray(
                WebServiceInfo.getKnetWebServiceLink()+"/API/Member/GetInboxMessageList", MainActivity.getAccessToken());

        // if json is null then there is a problem.
        if(jsonArray == null)
        {
            // if json array is null then print error message.
            runOnUiThread(showAlertMessage);
            runOnUiThread(returnRes);
            return;
        }

        try
        {
            // Eger aranilan kritere gore ilan yok ise hata mesaji basiyoruz.
            if(jsonArray.length() == 0)
            {
                // if json array is null then print error message.
                runOnUiThread(showAlertIlanYokMessage);
                runOnUiThread(returnRes);
                return;
            }

            // looping through All Contacts
            for (int i = 0; i < jsonArray.length(); i++)
            {
                JSONObject c = jsonArray.getJSONObject(i);

                // Storing each json item in variable
                // String id = c.getString(TAG_ID);
                String id = c.getString("Id");
                String sender = c.getString("Sender");
                // String body = c.getString("Body");
                String title = c.getString("Title");
                String sentDate = c.getString("SentDate");
                Boolean isReaded = c.getBoolean("IsRead");

                Message productObject = new Message(id, sender, "", title, sentDate, isReaded);
                productArray.add(productObject);
            }
        }
        catch (Exception e)
        {
            Log.e("BACKGROUND_PROC", e.getMessage());
        }
        runOnUiThread(returnRes);
    }


    // @end of thread.
    private Runnable returnRes = new Runnable() {

        public void run()
        {
            profilInfoProgress.stop();
            adapter.notifyDataSetChanged();// refreshing data over adapter in
                                            // list view.
        }
    };

    // @end of thread.
    private Runnable showAlertMessage = new Runnable() {

        public void run()
        {
            // Bu hata genelde linkteki problemden, servera ulasilamamasindan
            // veya timeouttan meydana gelir.
            Toast.makeText(getApplicationContext(),
                    "Mesajlar alınamadı lütfen daha sonra tekrar deneyiniz.", 
                    Toast.LENGTH_LONG).show();
        }
    };

    private Runnable showAlertIlanYokMessage = new Runnable() {

        public void run()
        {
            // Bu hata aranilan kelimeye gore ilan bulunamazsa gelir.
            Toast.makeText(getApplicationContext(),
                    "Mesajlar bulunamadı.", 
                    Toast.LENGTH_LONG).show();
        }
    };

}

========================================================================

ActivityB.java (MesajDetayActivity.java)

public class MesajDetayActivity extends TrackedActivity {

    private HoloProgressIndicator profilInfoProgress = null;

    TextView titleTextView = null;
    TextView senderTextView = null;
    TextView dateTextView = null;
    WebView bodyWebView = null;

    Message messageObject = null;

    String messageID = null;

    ImageView kapatButton = null;

    Context context;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mesajdetaylari);

        context = this;

        kapatButton = (ImageView) findViewById(R.id.kapat_button);
        /* kapat button onclick listener. */
        // =================================================================================================================
        kapatButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view)
            {
                // Set vibration on touch.
                KnetGenericClass.vibratePhone(context);

                finish();
            }

        });
        // =================================================================================================================
        //Progress bar.
        profilInfoProgress = (HoloProgressIndicator) findViewById(R.id.profil_info_progress);

        Bundle extras = getIntent().getExtras();
        if(extras != null)
        {
            messageID = extras.getString("messageID");
        }

        titleTextView = (TextView) findViewById(R.id.title_textview);
        senderTextView = (TextView) findViewById(R.id.sender_textview);
        dateTextView = (TextView) findViewById(R.id.date_textview);
        bodyWebView = (WebView) findViewById(R.id.mesaj_webView);

        // Show the ProgressDialog on this thread
        profilInfoProgress.start();

        // Start a new thread that will download all the data
        new MakeItTask().execute();

    }

    // Async task.
    private class MakeItTask extends AsyncTask<String, Void, Object> {
        protected Object doInBackground(String... args)
        {
            Log.i("MyApp", "Background thread starting");

            // This is where you would do all the work of downloading your data
            // getting message detay
            /* connect to web service */
            getMessageDetayFromWebService();

            return null;
        }

        protected void onPostExecute(Object result)
        {
            // Pass the result data back to the main activity
            // TakipListeActivity.this.data = result;
            try
            {
                titleTextView.setText("Başlık: " + messageObject.getTitle());
                senderTextView.setText("Gönderen: " + messageObject.getSender());
                dateTextView.setText("Tarih: " + messageObject.getSentDate().substring(6, 8) + "."
                        + messageObject.getSentDate().substring(4, 6) + "."
                        + messageObject.getSentDate().substring(0, 4));

                if(!messageObject.getBody().contains("img"))
                {
                    bodyWebView.loadDataWithBaseURL(null, messageObject.getBody(), "text/html", "UTF-8", null);
                }

            }
            catch (Exception e)
            {
                Log.e(CONNECTIVITY_SERVICE, "Mesaj Detayi bilgileri basilamadi.");
            }

            profilInfoProgress.stop();
        }
    }

    /* web service'e baglanti kurulan methodu threadin icerisinde cagiriyoruz. */
    public void getMessageDetayFromWebService()
    {
        // Creating JSON Parser instance
        JSONParser jParser = new JSONParser(context);

        // getting JSON string from URL
        JSONObject jsonObject = jParser.getAuthorizedInfoFromUrlToJSONObject(
                WebServiceInfo.getKnetWebServiceLink()+"/API/Member/GetInboxMessage/" + messageID, MainActivity.getAccessToken());

        // if json is null then there is a problem.
        if(jsonObject == null)
        {
            return;
        }

        try
        {
            String title = jsonObject.getString("Title");
            String id = jsonObject.getString("Id");
            String sender = jsonObject.getString("Sender");
            String date = jsonObject.getString("SentDate");
            String body = jsonObject.getString("Body");

            messageObject = new Message(id, sender, body, title, date, true);

        }
        catch (Exception e)
        {
            Log.e("BACKGROUND_PROC", e.getMessage());
        }

    }// @end of getIlanDetayFromWebService.

}

Edit: Not only these two activities have this problem, all the activities acting same behavior on some phones.

OzBoz
  • 543
  • 1
  • 8
  • 23
  • What is the problem if ActivityA recreate itself? It's possible to happen.. – Zyoo Feb 21 '13 at 13:28
  • Everytime going back to the ActivityA, it recreate itself. An application that automatically restarts itself can be annoying to users. And we got report about this issue not from many users. – OzBoz Feb 21 '13 at 13:37
  • I still don't get it, Activity recreation doesn't need to be that annoying. I think the only possibility that ActivityA gets recreated is that it has been destroyed before. – Zyoo Feb 21 '13 at 14:02
  • Override `ActivityA`'s `onDestroy` method, put a breakpoint in there and see if this method is called and examine the stack-trace of how it is called. – Streets Of Boston Feb 26 '13 at 22:28
  • try using @Override public void onBackPressed() { this.finish(); return; } in activity B?? – Shiv Feb 27 '13 at 04:58
  • @StreetsOfBoston thank you. I override onDestroy method inside ActivityA and It calls onDestroy while creating ActivityB. (I mean whenever user press startButton.) How could it be possible? – OzBoz Feb 27 '13 at 12:50
  • The problem might be the size of your productArray List of Messages. If it's actually really large, the OS might well decide to aggressively destroy the corresponding activity when it's not visible. – Pierre Rust Mar 02 '13 at 08:14
  • I also noticed that some Samsung devices are quite aggresive when it comes to clean up system resources. So as already said here, it's most likely the system that decides to finish your activity. Either try `startActivityForResult()` or se the `saveInstanceState` to cache required data. – Trinimon Mar 02 '13 at 12:41

13 Answers13

49

Check to see whether Don't keep activities under Settings > System > Developer options > Apps is enabled or not.

Joe
  • 14,039
  • 2
  • 39
  • 49
  • 2
    Thank you for showing me the reason. Sometimes getting inside thing make you thing much complex. I did not believe how I can not see this and wasting my time on try to fix code. – OzBoz Mar 03 '13 at 02:16
  • Thank you for mentioning the reason, I was completely blank about the reason. – 1'hafs Apr 23 '16 at 16:23
  • it works , but why this is happening in specific activity ? – Jesus Dimrix Aug 04 '16 at 09:10
11

The Activity documentation (http://developer.android.com/reference/android/app/Activity.html) says the following about the lifecycle of a background activity:

A background activity (an activity that is not visible to the user and has been paused) is no longer critical, so the system may safely kill its process to reclaim memory for other foreground or visible processes. If its process needs to be killed, when the user navigates back to the activity (making it visible on the screen again), its onCreate(Bundle) method will be called with the savedInstanceState it had previously supplied in onSaveInstanceState(Bundle) so that it can restart itself in the same state as the user last left it.

In other words, ActivityA may or may not be destroyed by the operating system while ActivityB is active, so this situation has to be handled in the code. If ActivityA has been destroyed, onCreate(Bundle) will be called, when the user presses the back button in ActivityB.

Mads Frandsen
  • 523
  • 4
  • 12
  • Thanks Mads, but this situation does not occur while activity on background. I know how to handle low memory killed activity. This occurs while UI were appearing on screen. – OzBoz Feb 21 '13 at 13:06
  • 1
    I still think this answer applies. The UI was on the screen, but it was showing ActivityB, right? If ActivityA is not shown on the screen, it is paused and thereby considered a background activity which can be destroyed by the operating system. – Mads Frandsen Feb 21 '13 at 13:20
  • Yeah maybe it occurs because of low memory issue. But still, an application that automatically restarts itself can be annoying to users. Handling onSaveInstanceState and onRestoreInstanceState is not a solution for my problem. Because, every return back to the activity on stack makes restarting activity again. – OzBoz Feb 21 '13 at 13:30
  • 2
    I agree, that a restarting application can be annoying for the user. However, I do think, you should handle this in your code anyway, since it may happen to anyone due to the activity lifecycle (not just the ones experiencing the problem where it happens everytime). For the ones experiencing the problem on every return to the activity, it sounds more like a bug in their operating system, or like they have devices with extremely low memory. If the application works like the simple example in your question, it definitely should not happen on every return to the Activity. – Mads Frandsen Feb 21 '13 at 13:52
  • Dianne Hackborn of the core Android team wrote: Android decides that it is running low on memory and so need to kill background processes to get some back. If your application is sitting in the foreground starting more and more activities, it is never going into the background, so it will always hit its local process memory limit before the system ever comes close to killing its process. (And when it does kill its process, it will kill the process hosting all the activities, including whatever is currently in the foreground.) – OzBoz Feb 22 '13 at 12:09
  • 1
    @OzBoz , you can try a couple of things to test this answer. The easiest would be to override the `onLowMemory` method in the Activity, and print something when the memory is low. Also try adding something to `onDestroy`. You should be able to save the state in `onSaveInstanceState` - but you may also be able to add `android:largHeap="true"` to your Android Manifest. This will not guarantee anything - but it may help. You can also just animate in a new View Object, and move all your new Activity code to your old Activity. This is not the best approach, but would be a proper hack-around. – Phil Feb 27 '13 at 13:14
  • @Phil I override onLowMemory method. ActivityA did not invoke onLowMemory(). While ActivityB is creating, first onSaveInstanceState method, then onDestroy() invoked. – OzBoz Mar 01 '13 at 08:37
9

There's an Android developer setting called "Do not keep activities". The description for this option is "Destroy every activity as soon as the user leaves it." This sounds like a good description of what you're seeing, and since you're only seeing it on a few phones the idea that this is caused by a non-default system setting seems plausible.

Ideally your app would still work in this scenario, even if less optimally. But if this setting is a problem for your app, you may wish to document this problem for your users.

j__m
  • 9,392
  • 1
  • 32
  • 56
7

Have you tried changing the launchmode in the Android Manifest? Try adding this to your Activity declaration:

android:launchMode="singleTask"

Next, try using startActivityForResult, instead of startActivity. This will force Activity A to call its onActivityResult(int, int, Intent) method when Activity B finishes - which may skip this (buggy) call to onCreate. Then, in Activity A, implement the method to do something (such as printing a debug statement):

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    Log.i("Test", "Did this work???");
    //TODO send notification to your server to verify this works?
}
Phil
  • 35,852
  • 23
  • 123
  • 164
  • 1.android:launchMode="singleTask" does not change anything. 2.Yes. It works but still Activity recreate itself again. – OzBoz Feb 27 '13 at 12:47
  • I had the same issue, and using startActivityForResult solves it. I used "startActivity" in the first activity, followed by "finish()" to stop it. Now, I use "startActivityForResult()" in the first activity, and "finish()" in the "onActivityResult" after the second activity returns. Seems to work nicely. I think this makes sense, since the life cycle model of android assumes you start activities and return to the first activity. If that's not there, and the second activity finishes, leaving no activity on the screen, I can see why Android tries to "make things right" by restarting an activity. – Christine Jun 21 '13 at 09:52
5

I do not see any problem in this behaviour.

In case you wish to preserve the state of ActivityA, make use of the methods onSaveInstanceState and onRestoreInstanceState. See Activity Lifecycle at http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle for more details.

See also https://stackoverflow.com/a/10492967/332210 for a deeper understanding.

Community
  • 1
  • 1
gvaish
  • 9,374
  • 3
  • 38
  • 43
3

You can try one thing provide your layout in onCreate() and do the rest of the work in onStart() ?? if it works??

LIKE:

 public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.show);
   }

and

      @Override
       protected void onStart() {
       // TODO Auto-generated method stub
    super.onStart();
    Log.i(TAG, "On Start .....");

      }

See Activity Lifecycle enter image description here

Shiv
  • 4,569
  • 4
  • 25
  • 39
1

Perhaps you should use

Intent startIntent = new Intent(view.getContext(), ActivityB.class); 
startActivity(startIntent); 
finish() ;

And

Intent startIntent = new Intent(view.getContext(), ActivityA.class); 
startActivity(startIntent); 
finish() ;

each time you go back or forward.

Husnain Iqbal
  • 466
  • 6
  • 16
  • You got me wrong I guess. I'm not asking how I can recreate Activity. I'm asking how to stop recreating itself. – OzBoz Feb 22 '13 at 07:35
1

It too faced the exact problem and solved issue by Using android:launchMode="standard" in activity of manifest.

Avadhani Y
  • 7,566
  • 19
  • 63
  • 90
  • I putted android:launchMode="standard" on every activity and also put it under but still the problem did not disappear. – OzBoz Mar 01 '13 at 08:03
1

Override onStart() and onResume method in Activity A and check if the problem is still persist. and if possible please give your activtiy A and B code here.

Dinesh Prajapati
  • 9,274
  • 5
  • 30
  • 47
  • Please override onResume on both the activity. – Dinesh Prajapati Mar 01 '13 at 08:45
  • I overrided onResume, onStart and onStop inside both Activity but nothing changed. – OzBoz Mar 01 '13 at 09:19
  • What is TrackedActivity here? try to remove that and put only Acitivity with onResume and other methods as is in code – Dinesh Prajapati Mar 01 '13 at 09:24
  • TrackedActivity is subclass of Activity. It is part of GoogleAnalytics framework and used here for getting Analytics report. I remove it and compile again on buggy device, it still gives same problem. – OzBoz Mar 01 '13 at 09:56
1

Activity A uses layout R.layout.mesajlar_list

Activity B uses layout R.layout.mesajdetaylari

But both have the following line of code:

kapatButton = (ImageView) findViewById(R.id.kapat_button);

Which layout is R.id.kapat_button in? Using the same id in different layouts is a very risky thing to do. I can't guarantee it's causing what you're seeing, but it is the sort of thing that may cause weird behaviour.

Neil Townsend
  • 6,024
  • 5
  • 35
  • 52
1

I think it is not because of memory the limit.

https://www.box.com/s/7pd0as03bb8wwumuc9l9

You should test these two activities and check whether it is happening in this example too or not. Please share your AndroidManifest.xml file content too, it will help with debugging.

Marko
  • 20,385
  • 13
  • 48
  • 64
umesh
  • 1,148
  • 1
  • 12
  • 25
0

I got this issue recently, and this make me annoyed. I think that issue around 2 options solution to check but useless.

About the setting "Don't keep activities" corrected here, I used this code to check that it optional checked or not (my test device customize base on version 2.3.5 and not show this option):

private boolean isAlwaysFinishActivitiesOptionEnabled() {
    int alwaysFinishActivitiesInt = 0;
    if (Build.VERSION.SDK_INT >= 17) {
        alwaysFinishActivitiesInt = Settings.System.getInt(getApplicationContext().getContentResolver(), Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0);
    } else {
        alwaysFinishActivitiesInt = Settings.System.getInt(getApplicationContext().getContentResolver(), Settings.System.ALWAYS_FINISH_ACTIVITIES, 0);
    }

    if (alwaysFinishActivitiesInt == 1) {
        return true;
    } else {
        return false;
    }
}

Result check is false in my case. I also check the memory when running application and it nothing occur.

Fuong Lee
  • 175
  • 4
0

you can use android:launchMode="singleTop"in manifest.

<activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:launchMode="singleTop"
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:theme="@style/AppTheme.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>

            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
Abu Nayem
  • 101
  • 3
  • 11