如何在管理堆时克服OutOfMemoryError?

时间:2018-03-17 11:35:38

标签: android out-of-memory

我正在创建一个通过发出http请求来获取json数据的应用程序,在解析该数据之后,它会在布局中填充视图,还将该json数据存储在列表数据集合中的字符串中。在布局中有一个用Picasso填充的图像视图,最后我在ViewPager enter image description here中有两个片段。每个片段都有分页来加载更多数据,并通过获取json数据填充结果列表对于每个页面。

我也必须

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

  2. 回收站查看堆中两个片段的适配器。

  3. <小时/> 加载更多列表数据时遇到了以下胎儿错误。

    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
    

1 个答案:

答案 0 :(得分:1)

当dalvik VM拒绝允许分配更多内存资源时,会发生OutOfMemoryError。正如所讨论的问题,使用名为 RecyclerViewForMoiveDiscovery 的RecyclerViewAdapter中的Picasso从远程主机加载大量图像。甚至完成活动后,位图保留在内存中,导致大量内存使用,直到导致OutOfMemoryError。在研究了类似类型的不同案例之后,我知道我们可以在使用毕加索时选择以下。

  1. 我们应该设置一个stableKey ,同时将url设置为picasso以获取图片。

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

  2. on Activity destroy 我们必须通过提供 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);这是阻止缓存的更好方法毕加索启动花费更多时间加载图片。
    2. 第三种方式是请求VM为资源提供更多动态内存,为此我们通过在Application标签下的清单中编写android:largeHeap="true"来提出请求。这是一种更简单的方法,但不好,因为如果我们这样做,它可能导致内存泄漏。有效的方法是通过使用Runtime.getRuntime.gc()使内存不受动态分配的对象的影响来管理堆。