如何在管理堆时克服 OutOfMemoryError?

How to overcome OutOfMemoryError while managing heap?

我正在制作一个应用程序,它通过发出 http 请求来获取 json 数据,并在解析该数据后填充布局中的视图,并且 还将 json 数据存储在字符串中在列表数据集合 中。在布局中,有一个充满 Picasso 的图像视图,最后我在 ViewPager 中有两个片段 。每个片段都有分页以加载更多数据,它们通过以下方式填充结果列表获取每个页面的 json 数据。

我也要

  1. 存储即将到来的 http json 对列表和两个片段的响应

  2. 堆中两个片段的回收器视图适配器。


加载更多列表数据时遇到以下错误。


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)


而RecyclerView的适配器如下

/**
 * 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

OutOfMemoryError 当 dalvik VM 拒绝允许分配更多内存时发生 resources.As 在所讨论的问题中,正在使用名为 RecyclerViewForMoiveDiscovery 的 RecyclerViewAdapter 中的 Picasso 从远程主机加载大量图像。即使在完成 activity 之后, 位图 仍保留在内存中,导致大量内存使用,直到导致 OutOfMemoryError。在研究了类似类型的不同案例后,我知道我们可以在使用 Picasso 时选择跟随。

  1. 我们应该设置一个stableKey同时设置url到picasso来获取图像

    Picasso.with(上下文).load(image_url).stableKey(stable_key).into(holder.picture);

在Activity销毁我们必须通过给出stableKey来使该文件无效,如下

Picasso.with(context).invalidate(stable_key);
  1. 第二种方法是跳过内存缓存并将网络策略设置为NetworkPolicy.NO_CACHE这样做我使用以下代码Picasso.with(context).load(path).skipMemoryCache().networkPolicy(NetworkPolicy.NO_CACHE).error(R.drawable.default_image).into(imageView);这是防止缓存的更好方法但是 毕加索开始 [​​=23=] 需要更多时间来加载 图像。
  2. 第三种方法是请求 VM 为资源提供更多动态内存,为此我们通过在应用程序 tag.This 下的清单中写入 android:largeHeap="true" 来发出请求,这是一种更简单的方法,但不好,因为如果我们这样做可能会导致内存 leak.Effective 方法是通过使用 Runtime.getRuntime.gc().
  3. 使内存从动态分配的对象中释放出来来管理堆