2017-08-06 46 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