2017-08-06 50 views
2

我读了documentation关于通过创建单例类并将应用程序上下文传递给它来独立完成网络请求活动。我以类似的方式实现它,但是我仍然发现,在轮播时,应用程序会在显示任何数据之前再次等待电话完成。那么,我在做什么错误以及如何正确设置它,以便调用将持续应用程序的生命周期,以便它不会每次根据文档调用方向更改。我知道它可以使用装载机或改造或okhttp做,但我想知道如何使用凌空如何在Android中使独立的排球请求活动独立?

实现它MainActivity.java

package com.example.imnobody.photosearch; 

import android.content.Intent; 
import android.support.v7.app.AppCompatActivity; 
import android.os.Bundle; 
import android.view.View; 
import android.widget.AdapterView; 
import android.widget.GridView; 
import android.widget.ImageView; 
import android.widget.TextView; 
import android.widget.Toast; 

import com.android.volley.Request; 
import com.android.volley.RequestQueue; 
import com.android.volley.Response; 
import com.android.volley.VolleyError; 
import com.android.volley.toolbox.StringRequest; 
import com.android.volley.toolbox.Volley; 

import java.util.ArrayList; 
import java.util.List; 

public class MainActivity extends AppCompatActivity { 

    private ImageGridAdapter imageGridAdapter; 
    private List<String> imageList; 

    public static final String URL = "API_HERE"; 

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

     imageList = new ArrayList<>(); 
     //imageList = QueryUtils.extractImages(SAMPLE_JSON_RESPONSE); 


     GridView gridView = (GridView) findViewById(R.id.gridview); 
     final TextView emptyTextView = (TextView)findViewById(android.R.id.empty); 
     gridView.setEmptyView(emptyTextView); 

     imageGridAdapter = new ImageGridAdapter(MainActivity.this,imageList); 

     gridView.setAdapter(imageGridAdapter); 

     gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 
      @Override 
      public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) { 

       Intent intent = new Intent(MainActivity.this,ImageActivity.class); 
       intent.putExtra("imageuri",imageList.get(position)); 
       startActivity(intent); 

      } 
     }); 

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

         imageList = QueryUtils.extractImages(response); //extract needed things from json 
         imageGridAdapter.clear(); 
         imageGridAdapter.addAll(imageList); 

        } 
       }, new Response.ErrorListener() { 
      @Override 
      public void onErrorResponse(VolleyError error) { 

       emptyTextView.setText("Unknown error occured"); 
      } 
     }); 

     VolleySingleton.getInstance(this.getApplicationContext()).addToRequestQueue(stringRequest); 


    } 
} 

VolleySingleton.java

package com.example.imnobody.photosearch; 

import android.content.Context; 
import android.graphics.Bitmap; 

import com.android.volley.Request; 
import com.android.volley.RequestQueue; 
import com.android.volley.toolbox.ImageLoader; 
import com.android.volley.toolbox.Volley; 

import android.support.v4.util.LruCache; 

/** 
* Created by imnobody on 7/8/17. 
*/ 

public class VolleySingleton { 

    private static VolleySingleton mInstance; 
    private RequestQueue mRequestQueue; 
    private ImageLoader mImageLoader; 
    private static Context mCtx; 

    private VolleySingleton(Context context) { 
     mCtx = context; 
     mRequestQueue = getRequestQueue(); 

     mImageLoader = new ImageLoader(mRequestQueue, 
       new ImageLoader.ImageCache() { 
        private final LruCache<String, Bitmap> 
          cache = new LruCache<String, Bitmap>(20); 

        @Override 
        public Bitmap getBitmap(String url) { 
         return cache.get(url); 
        } 

        @Override 
        public void putBitmap(String url, Bitmap bitmap) { 
         cache.put(url, bitmap); 
        } 
       }); 
    } 

    public static synchronized VolleySingleton getInstance(Context context) { 
     if (mInstance == null) { 
      mInstance = new VolleySingleton(context); 
     } 
     return mInstance; 
    } 

    public RequestQueue getRequestQueue() { 
     if (mRequestQueue == null) { 

      mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext()); 
     } 
     return mRequestQueue; 
    } 

    public <T> void addToRequestQueue(Request<T> req) { 
     getRequestQueue().add(req); 
    } 

    public ImageLoader getImageLoader() { 
     return mImageLoader; 
    } 
} 

回答

1

那么,几点:

你正在提出一个请求,每次你的acti vity被重新创建,在onCreate。从理论上讲,如果确实需要在活动打开时刷新您的数据,这并不一定是坏事,因为在创建RequestQueuenewRequestQueue(请参阅here)时,似乎Volley会自动为其自身设置DiskBasedCache

这意味着即使您在每次方向更改后发出新请求,Volley都会为您提供缓存响应,而不是击中网络。通过启用详细日志记录,您应该看到Volley何时使用网络或缓存来提供请求。

要启用详细日志记录,在这里看到:https://stackoverflow.com/a/23654407/2220337

不过,默认的缓存仍然只是一个Disk缓存,这是比在内存缓存慢。如果您需要缓存更快,则可以通过实现Cache界面,然后按照here所述创建RequestQueue,并在构造函数中提供您自己的自定义内存缓存,从而实现自己的内存缓存。

更好的方法是在方向更改后根本不发出请求,而是依靠onSaveInstanceState/onRestoreInstanceState来保留并恢复数据。这样,如果请求已完成,则在重新创建活动时不会激活新的请求。

取而代之,您只需显示保存在onSaveInstanceState中的数据。

public class MainActivity extends AppCompatActivity { 

public static final String URL = "API_HERE"; 
private static final String SAVED_RESPONSE = "SAVED_RESPONSE"; 
private ImageGridAdapter imageGridAdapter; 
private List<String> imageList; 
private GridView gridView; 
private String savedResponse; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    imageList = new ArrayList<>(); 
    gridView = (GridView) findViewById(R.id.gridview); 
    final TextView emptyTextView = (TextView) findViewById(android.R.id.empty); 
    gridView.setEmptyView(emptyTextView); 
    imageGridAdapter = new ImageGridAdapter(MainActivity.this, imageList); 
    gridView.setAdapter(imageGridAdapter); 

    gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 
     @Override 
     public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) { 

      Intent intent = new Intent(MainActivity.this, ImageActivity.class); 
      intent.putExtra("imageuri", imageList.get(position)); 
      startActivity(intent); 
     } 
    }); 

    if (savedInstanceState == null) { 
     //activity was created for the first time, fetch images 
     getImages(); 
    } else { 
     //everything in this else branch can be moved in onRestoreInstanceState, this is just a matter of preference 
     savedResponse = savedInstanceState.getString(SAVED_RESPONSE); 
     if (savedResponse != null) { 
      refreshImages(savedResponse); 
     } else { 
      //an error occurred when the request was fired previously 
      ((TextView) gridView.getEmptyView()).setText("Unknown error occured"); 
     } 
    } 
} 

@Override 
protected void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 
    outState.putString(SAVED_RESPONSE, savedResponse); 
} 

private void getImages() { 
    StringRequest stringRequest = new StringRequest(Request.Method.GET, URL, 
      new Response.Listener<String>() { 
       @Override 
       public void onResponse(String response) { 
        savedResponse = response; 
        refreshImages(response); 
       } 
      }, new Response.ErrorListener() { 
     @Override 
     public void onErrorResponse(VolleyError error) { 
      savedResponse = null; 
      ((TextView) gridView.getEmptyView()).setText("Unknown error occured"); 
     } 
    }); 

    VolleySingleton.getInstance(this.getApplicationContext()).addToRequestQueue(stringRequest); 
} 

private void refreshImages(String response) { 
    imageList = QueryUtils.extractImages(response); //extract needed things from json 
    imageGridAdapter.clear(); 
    imageGridAdapter.addAll(imageList); 
} 

此外,请留意有关的以下几点:

  • 如果你开始的请求,并改变方向时它完成之前,你将有内存泄漏,你的活动将不会发生垃圾收集。这是因为你的stringRequest是隐式引用MainActivity的匿名内部类实例。

    为了避免这种情况,我在过去有很好的结果,我的Volley请求&响应由Android服务管理,响应通过粘性广播转发给用户界面。事件公共汽车也适用于此目的。

    广播需要粘性,以便在活动重新创建完成时不会丢失响应,因为在重新创建时它不会被注册为广播接收器。通过发送粘性广播,他们会坚持并让我在Android完成重新创建活动后读取数据。

  • 我提到的应该是罚款,如果你的反应字符串是不是非常大的JSON它指向将在以后下载了一些在线图像第二种方法。但是,如果它包含BASE64附加映像,则Volley的默认DiskBasedCache可能更适合缓存其数据。

希望这有助于!

+0

所以只有一个问题,所以这也会导致方向改变的内存泄漏,所以为什么会想要一个单独的singleton类来排除?或者有什么它的目的不是简单地有得[像这样(https://developer.android.com/training/volley/simple.html) – Nobody

+0

只是为了澄清,以“**这**也会导致内存泄漏......“,** ** **是指我的答案中的代码片段,还是代码片段后提到的服务方法? – cjurjiu

+0

否我的意思是[文件中提到的[singleton approach](https://developer.android.com/training/volley/requestqueue.html))与[前页面](https:// developer)中提到的常规方法。文档的文档。我只是想知道单一类方法比这个方法更好。 – Nobody