1

I am making an app that fetches json data by making http requests and after parsing that data it populate the views in layout and also store that json data in string in a list data collection . In layout there is an image view that is filled with Picasso finally I have two fragments in ViewPager enter image description here .And each fragment has pagination to load more data and they fill result list by getting json data for each page.

I also have to

  1. Store coming http json response to list and for both fragments

  2. Recycler View Adapters for both fragments in heap.


While loading more list data have faced following fetal error.
Process: mashhood.meshsoft.com.gn_news, PID: 18465
    java.lang.OutOfMemoryError: Failed to allocate a 94784012 byte allocation with 4194304 free bytes and 87MB until OOM
    at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
    at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:655)
    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:483)
    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:1157)
    at android.content.res.ResourcesImpl.loadDrawableForCookie(ResourcesImpl.java:720)
    at android.content.res.ResourcesImpl.loadDrawable(ResourcesImpl.java:571)
    at android.content.res.Resources.loadDrawable(Resources.java:972)
    at android.content.res.TypedArray.getDrawable(TypedArray.java:931)
    at android.widget.ImageView.<init>(ImageView.java:157)
    at android.widget.ImageView.<init>(ImageView.java:145)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:60)
    at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:56)
    at android.support.v7.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:106)
    at android.support.v7.app.AppCompatDelegateImplV9.createView(AppCompatDelegateImplV9.java:1029)
    at android.support.v7.app.AppCompatDelegateImplV9.onCreateView(AppCompatDelegateImplV9.java:1087)
    at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:47)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:769)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:858)
    at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:861)
    at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:861)
    at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:518)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:426)
    at mashhood.meshsoft.com.gn_news.movies.Adapters.RecyclerViewAdapters.RecyclerViewForMoiveDiscovery.onCreateViewHolder(RecyclerViewForMoiveDiscovery.java:74)
    at mashhood.meshsoft.com.gn_news.movies.Adapters.RecyclerViewAdapters.RecyclerViewForMoiveDiscovery.onCreateViewHolder(RecyclerViewForMoiveDiscovery.java:39)
    at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6367)
    at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5555)
    at android.support.v7.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:282)
    at android.support.v7.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:336)
    at android.support.v7.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:349)
    at android.support.v7.widget.GapWorker.prefetch(GapWorker.java:356)
    at android.support.v7.widget.GapWorker.run(GapWorker.java:387)
    at android.os.Handler.handleCallback(Handler.java:836)
    at android.os.Handler.dispatchMessage(Handler.java:103)
    at android.os.Looper.loop(Looper.java:203)
    at android.app.ActivityThread.main(ActivityThread.java:6251)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)


While adapter for RecyclerView is as follows
/**
 * Created by Mashhood on 3/14/2018.
 */

public class RecyclerViewForMoiveDiscovery     extends RecyclerView.Adapter<RecyclerViewForMoiveDiscovery.MyViewHolder> {

    //this is for the class fields
    public Context ctx;
    public List<DiscoverMovies> list; //this is for the list of items
    //end of the class fields as it should be


    //this is for the holder for the thread reasoning
    public Handler  handler; //this  is for the handler  to handle  ui thread
    //end of the holder for the thread reasoning


    //this is for the constructor
    public RecyclerViewForMoiveDiscovery(Context context,List<DiscoverMovies>list){
         this.ctx=context; //this is for context
         this.list=list; //this is for the list
        //this is for the initialization of  handler
        handler=new Handler(); //this is for the handler
        //end of initialization for handler
    }
    //end of the constructor






    //this is for the RecyclerView Methods

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

            //this is for the LayoutInFlator
        LayoutInflater  layoutInflater=LayoutInflater.from(parent.getContext());
        View view=layoutInflater.inflate(R.layout.movie_item,parent,false);
            //end of the LayoutInFlator

        //this  is to create the view holder
        MyViewHolder  myViewHolder=new MyViewHolder(view);
        //end of view holder

        return myViewHolder; //this is for myViewHolder
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, int position) {

            try{


                    final DiscoverMovies   discoverMovies=list.get(holder.getAdapterPosition()); //this is for getting current object

                    //now to place it in views
                    holder.movie_rating.setText(discoverMovies.getVoteAverage());  //this is for average vote
                    holder.movie_name.setText(discoverMovies.getTitle());   //this  is for the movie name
                    holder.movie_year.setText(discoverMovies.getRelease_date());  //this is for the release date
                    holder.overview_text.setText(discoverMovies.getOverview());  //this is for the overview for movies
                    //end  of the placement



                    try{
                        //this is for the picasso work to get the image
                        Picasso.with(this.ctx).load(discoverMovies.getPoster_path()).error(R.drawable.moive_item_background).into(holder.picture);
                        //end of the picasso work to get the image
                    }
                    catch (Exception ex){
                        LogPrinting("Exception of type "+ex.getMessage()+"while loading the Image");
                    }//end of loading  the Picture




                    //this is for the click listener for the watch trailer button
                    holder.watch_trailer_button.setOnClickListener(new View.OnClickListener() {
                        public final String movie_id=discoverMovies.getId(); //this is for getting the movie id
                        @Override
                        public void onClick(View v) {

                            if(isOnline()){
                                FindVideoIdForThatMovie(movie_id);  //this is for finding the movie trailer
                            } //this is for the if
                            else{
                                ToastPrinting("Please get Internet Connection");
                            }//end of else
                        }
                    });
                   //end of the block for the click listener for the watch trailer button




                   //this is for the holder watch movie button
                   holder.watch_movie_button.setOnClickListener(new View.OnClickListener() {
                       public final String movie_id=discoverMovies.getId(); //this is for the movie id
                       @Override
                       public void onClick(View v) {
                                      try{
                                          if(isOnline())
                                          {
                                              Intent i=new Intent(ctx, MoviePlayer.class);
                                              i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);   //this is for the new task
                                              Bundle b=new Bundle();
                                              b.putString("id",movie_id);
                                              i.putExtras(b);
                                              ctx.startActivity(i);
                                          }
                                          else{
                                              ToastPrinting("Please get a Internet Connection");
                                          }
                                         } //end of the try
                                      catch (Exception ex){
                                         LogPrinting("Exception of type is as follows  "+ex.getMessage());
                                      }//end of the catch
                       }
                   });
                   //end  of watch movie button







               }//end of the try
            catch(Exception ex){
                    LogPrinting("Exception while binding the Values with view items "+ex.getMessage());
            }//end of catch


    }    // end of the  OnBindViewHolder

    @Override
    public int getItemCount() {
        return list.size();  //this is for the list Size
    }
    //end of RecyclerView Methods











    //this is for the  Log and Toast printing
    private static final String TAG = "RecyclerViewForMoiveDis";
    public void LogPrinting(String Line){
        Log.d(TAG,Line); //this is for the Log Printing
    }//this is  for Log

    public void ToastPrinting(String Line){
        Toast.makeText(this.ctx,Line,Toast.LENGTH_SHORT).show();
    }//end of Toast

    //end of Log And Toast Printing



    //this is for the ViewHolder
     class MyViewHolder extends RecyclerView.ViewHolder{

        //this is for the class fields
        public View view; //this is for the view
        // this is for the public view
        public TextView movie_rating;  //this is for the movie_rating
        public TextView  movie_name; //this is for the movie name
        public TextView  movie_year;  //this is for the movie year
        public TextView  overview_text; //this is for the movie overview
        public Button watch_trailer_button ;  //this is for the trailer
        public Button watch_movie_button; //this  is to watch movie
        public ImageView picture; //this is for the picture
        //end of the public view

        //end of view items

        public MyViewHolder(View itemView) {
            super(itemView);
            view=itemView;
            InitializeComponents(); //this is to initialize  the components
        }
        //end of the class fields


        //this  is for the initialization of the components
        public void InitializeComponents(){
               try{
                          movie_rating=(TextView) view.findViewById(R.id.movie_rating); //this is for rating
                          movie_name=(TextView) view.findViewById(R.id.movie_name); //this is for  the movie name
                          movie_year=(TextView) view.findViewById(R.id.movie_year); //this is for the movie year
                          overview_text=(TextView) view.findViewById(R.id.overview_text); //this is for overview text
                          watch_trailer_button=(Button) view.findViewById(R.id.watch_trailer_button); //this is for watch trailer button
                          watch_movie_button=(Button) view.findViewById(R.id.watch_movie_button); //watch movie button
                          picture=(ImageView) view.findViewById(R.id.movie_picture); //this is for the movie picture
                  }//end of the try
               catch (Exception ex){
                   LogPrinting("Exception   of type is as follows  "+ex.getMessage());
               }//end of catch
        }
        //end of the initialization of the components

    }
    //end of the ViewHolder


















    //this is for loading the video id for a given movie
    public void  FindVideoIdForThatMovie(final String movieId){
                   try{


                                  //there we will make the http request for that page
                                  Thread t=new Thread(new Runnable() {
                                      @Override
                                      public void run() {
                                                //inner try   catch
                                                  try{
                                                      URL   url=new URL(new UrlData().GiveMeUrlForVideo(movieId)); //this is for the url
                                                      URLConnection  urlConnection=url.openConnection(); //this is to open  http connection
                                                      BufferedReader  bufferedReader=new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); //this is for the buffered reader


                                                      String Line=""; //this is for the Line
                                                      String temporary_line=""; //this is for the temporary  line


                                                      //this is for the loop to get the data
                                                      while((temporary_line=bufferedReader.readLine())!=null){  //keep on reading the Line till no line

                                                          Line +=temporary_line; //this is for the temporary Line

                                                      }  //end  of the loop to get the data


                                                      //to print what we have got
                                                       LogPrinting("Coming Video data is as \n"+Line);  //this is for the Line
                                                      //end of printing what we have got



                                                      //this is for the parsing phase
                                                      try{

                                                          JSONObject   mainJSONObject=new JSONObject(Line); //this is for main JSONObject

                                                          JSONArray   jsonArray=mainJSONObject.getJSONArray("results");  //this is for getting the json array

                                                          JSONObject   very_first_object=(JSONObject)  jsonArray.get(0); //this is for json array



                                                             final      String youtube_video_id= very_first_object.get("key").toString(); //this is for the youtube video id




                                      //now we have to code for sending that video id to  new youtube activity
                                                         handler.post(new Runnable() {
                                                             @Override
                                                             public void run() {
                                                                 try{
                                                                     Intent i=new Intent(ctx, YouTubePlayerActivity.class);
                                                                     i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);   //this is for the new task
                                                                     Bundle b=new Bundle();
                                                                     b.putString("id",youtube_video_id);
                                                                     i.putExtras(b);
                                                                     ctx.startActivity(i);
                                                                 }//end of try
                                                                 catch (Exception ex3){
                                                                     LogPrinting("Exception while opening the youtube activity "+ex3.getMessage());
                                                                 }
                                                             }
                                                         });

                                      //end of the activity for sending that video id to new youtube activity







                                                         }//end of try block
                                                      catch (Exception exi){
                                                          LogPrinting("Exception of type "+exi.getMessage()+" while  parsing the json data for videos");
                                                      }
                                                      //end  of the parsing phase




                                                     }//end of try
                                                  catch (Exception ex){
                                                      LogPrinting("Exception of type "+ex.getMessage()+" while loading the videos data for that job ");
                                                  }
                                                //end of inner try catch
                                      }
                                  });     //end of Thread
                       t.start();
                                  //end of making the http request for that page


                      }//end  of the try block
                   catch (Exception ex){
                       LogPrinting("Exception of type  "+ex.getMessage()+" while loading the video id from source in Recycler Adapter for Movies");
                   }//end of the catch block for  loading the movies from movies sources
    }
    //end of loading the video id for a given movie



        //this is for checking either the user is online
        public boolean isOnline() {
            ConnectivityManager cm = (ConnectivityManager) this.ctx.getSystemService(ctx.CONNECTIVITY_SERVICE);
            return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnectedOrConnecting();
        }
      //end  of checking either user is online or not




}//end of the class

Mashhood
  • 392
  • 6
  • 18
  • Please paste your adapter here. – Shalu T D Mar 17 '18 at 11:38
  • I have placed adapter that manage recycler view but how can it cause that memory out of heap.I mean it is populating views for recycler view and recycler view recycle each view . – Mashhood Mar 17 '18 at 11:48
  • 1
    There is too little info to determine the cause. Check if: 1. You're assigning all of the Bitmaps at once, instead of in onBindViewHolder. 2. You're (not) releasing resources after view is recycled. Also having 17k pages in one view seems pretty useless to me... – Gotiasits Mar 17 '18 at 12:23
  • Sir thanks for guiding to release resources .I want your opinion if I make adapter null for second retrieval of data and manage String response in a text file instead of memory so that Can we overcome? – Mashhood Mar 17 '18 at 12:38
  • 1
    Setting adapter to null will not help you with objects in background threads, holding a reference to Bitmap. Reading and writing to file is much more risky and resource consuming than using List... Size of your list is not the problem, it is the amount of Bitmap data you store in memory. – Gotiasits Mar 17 '18 at 13:03
  • By getting reference of these bit maps from the list of the objects I have used for(DiscoverMovies d: list){ d.getBitmap().recycle(); }//end of the loop but still it is giving the java.lang.OutOfMemoryError: Failed to allocate a 94784012 byte allocation with 4194048 free bytes and 90MB until OOM – Mashhood Mar 17 '18 at 14:19
  • By managing Picasso cache I solve my problem.I found that solution at https://stackoverflow.com/a/39756183/8615158 – Mashhood Mar 17 '18 at 15:21
  • Glad to hear it, you can answer your own question. – Gotiasits Mar 18 '18 at 18:03

1 Answers1

1

OutOfMemoryError occurs when dalvik VM refuses to allow to allocate more memory resources.As in problem discussed, a huge number of images are being loaded from a remote host using Picasso in RecyclerViewAdapter named as RecyclerViewForMoiveDiscovery.Even after finishing the activity the bitmaps remain in memory that cause a large memory usage till it leads to OutOfMemoryError. After study different cases of similar type I come to know we can choose following while using Picasso.

  1. We should set a stableKey while setting url to picasso to get the image.

    Picasso.with(context).load(image_url).stableKey(stable_key).into(holder.picture);

And on Activity destroy we have to invalidate that file by giving the stableKey As follows

Picasso.with(context).invalidate(stable_key);
  1. Second way is to skip memory cache and to set network policy as NetworkPolicy.NO_CACHE to do so I use following code Picasso.with(context).load(path).skipMemoryCache().networkPolicy(NetworkPolicy.NO_CACHE).error(R.drawable.default_image).into(imageView);It is better approach to prevent cache but picasso starts to take more time to load images.
  2. Third way is to request VM to allow more dynamic memory for resource and for it we make request by writing android:largeHeap="true" in manifest under Application tag.This is an easier way but not good since if we do so it may cause memory leak.Effective approach is to manage heap by making memory free from dynamically allocated objects by using Runtime.getRuntime.gc().
Mashhood
  • 392
  • 6
  • 18