如何在安卓中使凌空请求活动独立?



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

主活动.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);

}
}

凌空单打.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;
}
}

嗯,有几点:

每次重新创建活动时,您都会发出请求,onCreate.从理论上讲,如果您确实需要在打开活动时刷新数据,这不一定是坏事,因为 Volley 似乎在创建具有newRequestQueueRequestQueue时会自动为自己设置DiskBasedCache(请参阅此处(。

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

若要启用详细日志记录,请参阅此处:https://stackoverflow.com/a/23654407/2220337

但是,默认缓存仍然只是一个Disk缓存,它比内存中缓存慢。如果需要更快的缓存,可以通过实现 Cache 接口来实现自己的内存中缓存,然后创建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服务管理,响应通过粘性广播转发到UI。事件总线也可用于此目的。

    广播需要具有粘性,以便在重新创建活动时完成时不会丢失响应,因为在重新创建时,它未注册为广播接收器。但是,通过发送粘性广播,它们会保留下来,并允许我在Android完成重新创建活动后读取数据。

  • 我提到的第二种方法应该没问题,如果你的响应字符串是一个不是很大的JSON,它指向一些稍后将下载的在线图像。但是,如果它包含 BASE64 加密图像,那么来自 Volley 的默认 DiskBasedCache 可能更适合缓存其数据。

希望这有帮助!

最新更新