Search code examples
androidandroid-fragmentshandleronbackpressed

Destroy Fragment with Chart Handler inside Activty onBackPressed


I currently have an activity that contains a viewpager with fragments. Inside these fragments are graphs that obtain are obtaining data from a URL. To achieve this I had to add a Handler in order to delay the process so the app wouldn't crash. Without it the data wasn't loading quick enough so I set it to 3 seconds. The problem I am having though is when I Backpress the app crashes if the charts haven't loaded yet.

I have tried implementing the override method for onBackPressed into my MainActivity but this only made the app crash anytime I hit back. I also tried using this code from another question on here but it also did not work. I really am not sure on how I can just destroy the Handler or Fragment when I go back from my activty. How can I solve this?

Code I used that didnt work:

@Override
 public void onBackPressed() {

int count = getFragmentManager().getBackStackEntryCount();

if (count == 0) {
    super.onBackPressed();
    //additional code
} else {
    getFragmentManager().popBackStack();
}
}

MainActivity

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_coin_detail);

    sectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
    viewPager = (ViewPager) findViewById(R.id.graph_container);
    setupViewPager(viewPager);
    viewPager.setOffscreenPageLimit(10);

    TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
    tabLayout.setupWithViewPager(viewPager);
    tabLayout.getTabAt(0).setText("1 Day");
    tabLayout.getTabAt(1).setText("7 Days");
    tabLayout.getTabAt(2).setText("30 Days");

    TextView header = findViewById(R.id.newtextViewHead);


    coinIMG = (ImageView) findViewById(R.id.imageView2);
    button_alert = (CircleButton) findViewById(R.id.button);
    button_fav = (CircleButton) findViewById(R.id.button_favorite);

    //Calls for intent
    Intent intent = getIntent();


//Tabs for fragment
private void setupViewPager(ViewPager viewPager){
    SectionsPagerAdapter adapter = new SectionsPagerAdapter(getSupportFragmentManager());
    adapter.addFragment(new Fragment_Graph1());
    adapter.addFragment(new Fragment_Graph2());
    adapter.addFragment(new Fragment_Graph3());
    viewPager.setAdapter(adapter);
}

Fragment Code

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.graph1_fragment, container, false);

    mChart = (LineChart) view.findViewById(R.id.chart1);
    mChart.setNoDataText("Getting Data From Server");
    mChart.setNoDataTextColor(Color.BLACK);
    makeChart();

    return view;
}

    private void makeChart() {

    StringRequest req = new StringRequest(Request.Method.GET, URL,
            new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {

                    Log.d(TAG, response.toString());

                    try {
                        jsonResponse = "";
                        JSONObject jObject = new JSONObject(response);
                        JSONArray jsonArray = jObject.getJSONArray("Data");
                        for (int i = 0; i <= jsonArray.length(); i++) {

                            JSONObject o = jsonArray.getJSONObject(i);

                            time = o.getString("time");
                            open = o.getString("close");

                            float val = Float.parseFloat(open);

                            yVals1.add(new Entry(i, val));

                            long unixSeconds = Long.parseLong(time);
                            Date date = new Date(unixSeconds * 1000L);
                            SimpleDateFormat sdf = new SimpleDateFormat("HH:MM");
                            sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
                            String formattedDate = sdf.format(date);

                            xValues.add(formattedDate);

                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }

            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            VolleyLog.d(TAG, "Error: " + error.getMessage());
        }
    });

    Graph_AppController.getInstance().addToRequestQueue(req);

    handler = new Handler();
    handler.postDelayed(new Runnable() {
        public void run() {

            mChart.setBackgroundColor(Color.WHITE);
            mChart.setGridBackgroundColor(Color.WHITE);
            mChart.setDrawGridBackground(true);
            mChart.setDrawBorders(true);

            Legend l = mChart.getLegend();
            l.setEnabled(false);

            XAxis xAxis = mChart.getXAxis();
            xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
            xAxis.setDrawGridLines(false);
            xAxis.setAxisLineColor(Color.BLACK);
            xAxis.setTextColor(Color.BLACK);

            xAxis.setValueFormatter(new DefaultAxisValueFormatter(0) {
                @Override
                public String getFormattedValue(float value, AxisBase axis) {
                    return xValues.get((int) value % xValues.size());
                }

                @Override
                public int getDecimalDigits() {
                    return 0;
                }
            });

            YAxis leftAxis = mChart.getAxisLeft();
            leftAxis.setTextColor(Color.BLACK);
            leftAxis.setDrawAxisLine(true);
            leftAxis.setDrawZeroLine(false);
            leftAxis.setDrawGridLines(false);
            leftAxis.setGridColor(Color.WHITE);
            leftAxis.setAxisLineColor(Color.BLACK);

            mChart.getAxisRight().setEnabled(false);
            LineDataSet set1;


            set1 = new LineDataSet(yVals1, "DataSet 1");

            set1.setAxisDependency(YAxis.AxisDependency.LEFT);
            set1.setMode(LineDataSet.Mode.CUBIC_BEZIER);
            set1.setColor(Color.parseColor("#008000"));
            set1.setDrawCircles(true);
            set1.setLineWidth(3f);
            set1.setCircleRadius(1f);
            set1.setCircleColor(Color.parseColor("#008000"));
            set1.setFillAlpha(50);
            set1.setDrawFilled(true);
            set1.setFillColor(Color.parseColor("#008000"));
            set1.setHighLightColor(Color.rgb(244, 117, 117));
            set1.setDrawCircleHole(false);

            dataSets.add(set1);

            LineData datab = new LineData(dataSets);
            datab.setDrawValues(false);

            mChart.setData(datab);
            mChart.setDrawMarkers(true);
            IMarker marker = new YourMarkerView(getContext(),R.layout.custom_marker);
            mChart.setMarker(marker);

        }
    }, 3000);
}
public class YourMarkerView extends MarkerView {

    private TextView tvContent;

    public YourMarkerView(Context context, int layoutResource) {
        super(context, layoutResource);

        // find your layout components
        tvContent = (TextView) findViewById(R.id.tvContent);
    }

    // callbacks everytime the MarkerView is redrawn, can be used to update the
    // content (user-interface)
    @Override
    public void refreshContent(Entry e, Highlight highlight) {

        tvContent.setText("$" + e.getY());

        // this will perform necessary layouting
        super.refreshContent(e, highlight);
    }

    private MPPointF mOffset;

    @Override
    public MPPointF getOffset() {

        if(mOffset == null) {
            // center the marker horizontally and vertically
            mOffset = new MPPointF(-(getWidth() / 2), -getHeight());
        }

        return mOffset;
    }


    @Override
    public void draw(Canvas canvas, float posX, float posY) {
        posX = 400;
        posY = -30;

        canvas.translate(posX,posY);
        draw(canvas);
        canvas.translate(-posX,-posY);

    }
}

Solution

  • The problem is that you're having a memory leak with your fragment. The network request will return to a fragment that doesn't exist any longer. Your handler will start a thread and try to access elements that have been destroyed. You shouldn't use handler with a fixed amount of time, what if you request takes more than 3 seconds to return? You need to fill you chart data only when you receive the result, change your makechart method to this:

    private void makeChart() {
    
        StringRequest req = new StringRequest(Request.Method.GET, URL,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
    
                        Log.d(TAG, response.toString());
    
                        try {
                            jsonResponse = "";
                            JSONObject jObject = new JSONObject(response);
                            JSONArray jsonArray = jObject.getJSONArray("Data");
                            for (int i = 0; i <= jsonArray.length(); i++) {
    
                                JSONObject o = jsonArray.getJSONObject(i);
    
                                time = o.getString("time");
                                open = o.getString("close");
    
                                float val = Float.parseFloat(open);
    
                                yVals1.add(new Entry(i, val));
    
                                long unixSeconds = Long.parseLong(time);
                                Date date = new Date(unixSeconds * 1000L);
                                SimpleDateFormat sdf = new SimpleDateFormat("HH:MM");
                                sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
                                String formattedDate = sdf.format(date);
    
                                xValues.add(formattedDate);
    
                            }
                        //finished receiving data
                        fillChartData();
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
    
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                VolleyLog.d(TAG, "Error: " + error.getMessage());
            }
        });
    
        Graph_AppController.getInstance().addToRequestQueue(req); 
    }
    

    Create a new method called fillChartData, move all your handler code to this method and call it when you've finished receiving the data. That way you don't have to use handler.

    public void fillChartData(){
      mChart.setBackgroundColor(Color.WHITE);
                    mChart.setGridBackgroundColor(Color.WHITE);
                    mChart.setDrawGridBackground(true);
                    mChart.setDrawBorders(true);
    
                    Legend l = mChart.getLegend();
                    l.setEnabled(false);
    
                    XAxis xAxis = mChart.getXAxis();
                    xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
                    xAxis.setDrawGridLines(false);
                    xAxis.setAxisLineColor(Color.BLACK);
                    xAxis.setTextColor(Color.BLACK);
    
                    xAxis.setValueFormatter(new DefaultAxisValueFormatter(0) {
                        @Override
                        public String getFormattedValue(float value, AxisBase axis) {
                            return xValues.get((int) value % xValues.size());
                        }
    
                        @Override
                        public int getDecimalDigits() {
                            return 0;
                        }
                    });
    
                    YAxis leftAxis = mChart.getAxisLeft();
                    leftAxis.setTextColor(Color.BLACK);
                    leftAxis.setDrawAxisLine(true);
                    leftAxis.setDrawZeroLine(false);
                    leftAxis.setDrawGridLines(false);
                    leftAxis.setGridColor(Color.WHITE);
                    leftAxis.setAxisLineColor(Color.BLACK);
    
                    mChart.getAxisRight().setEnabled(false);
                    LineDataSet set1;
    
    
                    set1 = new LineDataSet(yVals1, "DataSet 1");
    
                    set1.setAxisDependency(YAxis.AxisDependency.LEFT);
                    set1.setMode(LineDataSet.Mode.CUBIC_BEZIER);
                    set1.setColor(Color.parseColor("#008000"));
                    set1.setDrawCircles(true);
                    set1.setLineWidth(3f);
                    set1.setCircleRadius(1f);
                    set1.setCircleColor(Color.parseColor("#008000"));
                    set1.setFillAlpha(50);
                    set1.setDrawFilled(true);
                    set1.setFillColor(Color.parseColor("#008000"));
                    set1.setHighLightColor(Color.rgb(244, 117, 117));
                    set1.setDrawCircleHole(false);
    
                    dataSets.add(set1);
    
                    LineData datab = new LineData(dataSets);
                    datab.setDrawValues(false);
    
                    mChart.setData(datab);
                    mChart.setDrawMarkers(true);
                    IMarker marker = new YourMarkerView(getContext(),R.layout.custom_marker);
                    mChart.setMarker(marker);
    }