Android Volley教程
在此android volley教程中,我们将在应用程序中实现Volley库。
如果您不了解Volley的功能,建议您先阅读本文,然后再继续。
Android Volley
Volley中的网络请求被添加到" RequestQueue"中。
并非每次都创建一个RequestQueue的新实例,而是通过在整个应用程序中创建一个RequestQueue的单个实例来遵循单例模式。
在下面的应用程序中,我们将实现GET和POST StringRequest和JsonObjectRequests。
我们将使用Gson库解析响应并将结果显示在RecyclerView中。
此外,我们将实现CustomRequest并从ImageView中的URL加载图像。
Android Volley Gradle依赖关系
在开始学习本教程的编码方面之前,请在您的build.gradle文件中添加以下依赖项。
compile 'com.android.support:cardview-v7:26.1.0' compile 'com.android.support:recyclerview-v7:26.1.0' compile 'com.android.support:design:26.1.0' compile 'com.google.code.gson:gson:2.8.0' compile 'com.android.volley:volley:1.0.0'
Android Volley示例代码
布局– activity_main.xml下面给出了保存着" MainActivity.java"用户界面的" activity_main.xml"布局的代码。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.theitroad.volley.RecyclerViewActivity">
<Button
android:id="@+id/btnImageLoader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/networkImageView"
android:layout_alignStart="@+id/networkImageView"
android:layout_below="@+id/networkImageView"
android:layout_marginTop="16dp"
android:text="LOAD IMAGE"
<Button
android:id="@+id/btnImageRequest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/btnImageLoader"
android:layout_alignBottom="@+id/btnImageLoader"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_toEndOf="@+id/imageView"
android:layout_toRightOf="@+id/imageView"
android:text="IMAGE REQUEST"
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/networkImageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_below="@+id/imageView"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginTop="8dp"
<Button
android:id="@+id/btnGET"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/btnPOST"
android:layout_alignLeft="@+id/btnPOST"
android:layout_alignRight="@+id/btnPOST"
android:layout_alignStart="@+id/btnPOST"
android:layout_centerVertical="true"
android:text="GET String JSON"
<Button
android:id="@+id/btnPOST"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/btnGET"
android:layout_centerHorizontal="true"
android:layout_marginTop="8dp"
android:text="POST String JSON"
<Button
android:id="@+id/btnCustomRequest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/btnPOST"
android:layout_alignLeft="@+id/btnPOST"
android:layout_alignRight="@+id/btnPOST"
android:layout_alignStart="@+id/btnPOST"
android:layout_below="@+id/btnPOST"
android:layout_marginTop="8dp"
android:text="CUSTOM REQUEST"
</RelativeLayout>
SingletonRequestQueue.java
package com.theitroad.volley;
import android.content.Context;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
public class SingletonRequestQueue {
private static SingletonRequestQueue mInstance;
private Context mContext;
private RequestQueue mRequestQueue;
private SingletonRequestQueue(Context context) {
mContext = context;
mRequestQueue = getRequestQueue();
}
public static synchronized SingletonRequestQueue getInstance(Context context) {
if (mInstance == null) {
mInstance = new SingletonRequestQueue(context);
}
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(mContext);
}
return mRequestQueue;
}
}
getInstance()和getRequestQueue()方法分别分别创建SingletonRequestQueue和RequestQueue的实例,并在各处重复使用。
将布局链接到MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button btnGET, btnPOST, btnImageLoader, btnCustomRequest, btnImageRequest;
NetworkImageView networkImageView;
ImageView imageView;
ArrayList<UserList.UserDataList> mUserDataList = new ArrayList<>();
String BASE_URL = "https://reqres.in";
String IMAGE_URL = "https://www.android.com/static/2015/img/share/oreo-lg.jpg";
int numberOfRequestsCompleted;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnGET = findViewById(R.id.btnGET);
btnPOST = findViewById(R.id.btnPOST);
btnImageLoader = findViewById(R.id.btnImageLoader);
btnImageRequest = findViewById(R.id.btnImageRequest);
btnCustomRequest = findViewById(R.id.btnCustomRequest);
networkImageView = findViewById(R.id.networkImageView);
imageView = findViewById(R.id.imageView);
btnGET.setOnClickListener(this);
btnPOST.setOnClickListener(this);
btnImageLoader.setOnClickListener(this);
btnCustomRequest.setOnClickListener(this);
btnImageRequest.setOnClickListener(this);
networkImageView.setDefaultImageResId(R.mipmap.ic_launcher);
networkImageView.setErrorImageResId(R.drawable.ic_error);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnGET:
GETStringAndJSONRequest("2", "4");
break;
case R.id.btnPOST:
POSTStringAndJSONRequest();
case R.id.btnImageLoader:
imageLoader();
case R.id.btnImageRequest:
imageRequest();
case R.id.btnCustomRequest:
customRequest();
break;
}
}
}
有5个按钮用于GET,POST,ImageLoader,ImageRequest,CustomRequest处理。
每个功能都分别在onClickListener中触发。
我们将分别研究每种方法
- GETStringAndJSONRequest()
- POSTStringAndJSONRequest()
imageLoader()imageRequest()customRequest()
我们将使用www.reqres.in上的REST API。
" UserList.java"类是用于使用Gson序列化响应的POJO类。
UserList.java
package com.theitroad.volley;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.List;
public class UserList implements Serializable {
@SerializedName("page")
public Integer page;
@SerializedName("per_page")
public Integer perPage;
@SerializedName("total")
public Integer total;
@SerializedName("total_pages")
public Integer totalPages;
@SerializedName("data")
public List<UserDataList> userDataList;
public class UserDataList implements Serializable {
@SerializedName("id")
public Integer id;
@SerializedName("first_name")
public String first_name;
@SerializedName("last_name")
public String last_name;
@SerializedName("avatar")
public String avatar;
}
}
上面的类实现了" Serializable",因为它的一个实例将作为Intent中的Bundle传递
让我们看看封装了Volley Request的MainActivity.java中的Button单击触发的方法。
GETStringAndJSONRequest()
Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (error instanceof NetworkError) {
Toast.makeText(getApplicationContext(), "No network available", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(), error.toString(), Toast.LENGTH_LONG).show();
}
}
};
private void GETStringAndJSONRequest(String page_1, String page_2) {
mUserDataList.clear();
numberOfRequestsCompleted = 0;
VolleyLog.DEBUG = true;
RequestQueue queue = SingletonRequestQueue.getInstance(getApplicationContext()).getRequestQueue();
String uri_page_one = String.format(BASE_URL + "/api/users?page=%1$s", page_1);
final String uri_page_two = String.format(BASE_URL + "/api/users?page=%1$s", page_2);
StringRequest stringRequest = new StringRequest(Request.Method.GET, uri_page_one, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
VolleyLog.wtf(response, "utf-8");
GsonBuilder builder = new GsonBuilder();
Gson mGson = builder.create();
UserList userList = mGson.fromJson(response, UserList.class);
mUserDataList.addAll(userList.userDataList);
++numberOfRequestsCompleted;
}
}, errorListener) {
@Override
public Priority getPriority() {
return Priority.LOW;
}
};
queue.add(stringRequest);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(uri_page_two, null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
VolleyLog.wtf(response.toString(), "utf-8");
GsonBuilder builder = new GsonBuilder();
Gson mGson = builder.create();
UserList userList = mGson.fromJson(response.toString(), UserList.class);
mUserDataList.addAll(userList.userDataList);
++numberOfRequestsCompleted;
}
}, errorListener) {
@Override
public String getBodyContentType() {
return "application/json";
}
@Override
public Priority getPriority() {
return Priority.IMMEDIATE;
}
};
queue.add(jsonObjectRequest);
queue.addRequestFinishedListener(new RequestQueue.RequestFinishedListener<Object>() {
@Override
public void onRequestFinished(Request<Object> request) {
try {
if (request.getCacheEntry() != null) {
String cacheValue = new String(request.getCacheEntry().data, "UTF-8");
VolleyLog.d(request.getCacheKey() + " " + cacheValue);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (numberOfRequestsCompleted == 2) {
numberOfRequestsCompleted = 0;
startActivity(new Intent(MainActivity.this, RecyclerViewActivity.class).putExtra("users", mUserDataList));
}
}
});
}
上面代码中要注意的重点:
我们创建了一个ErrorListener实例,该实例将在整个Activity中使用。
我们在RequestQueue中链接了两个请求。
第一个请求是StringRequest。
在API/api/users中,页面是URL编码参数。
响应是一个通过Gson序列化的JSONObject。
我们已将StringRequest的优先级设置为低。
因此,此请求应最终完成(在服务器快速响应的情况下,优先级将不起作用)。第二个请求是JsonObjectRequest。
由于它是GET请求,因此我们将请求正文设置为null(请检查第二个参数)。
优先级设置为立即。
因此,JsonObjectRequest应该首先完成。我们将两个请求返回的列表加入到mUserDataList ArrayList中。
在
addRequestFinishedListener回调侦听器中,我们检查两个请求是否都结束了(通过检查numberOfRequestsCompleted计数器。此外,在addRequestFinishedListener回调中,我们可以从Cache.Entry中检索响应。
尝试在Requests上将setShouldCache()设置为false,然后在缓存中找到响应。
最后,将从这两个请求中检索到的用户数据列表传递到RecyclerViewActivity.java。
这是我们在RecyclerView中填充ArrayList的地方。
让我们看一下RecyclerViewActivity和布局代码。
Layout– activity_recyclerview.xml
RecyclerViewActivity.java
每个RecyclerView行的布局在recyclerview_row.xml中定义。
RecyclerViewAdapter.java
我们已经初始化了一个ImageLoader,它将显示NetworkImageView中每一行的URL中的图像。
LRU缓存用于通过实现ImageCache来缓存图像。
LruCache构造函数中的参数是缓存条目数限制。
POSTStringAndJSONRequest()
此方法将在RequestQueue中链接多个POST请求。
stringRequest –要在StringRequest中发布参数,我们需要覆盖getParams()并将这些参数作为键值对传递。
jsonObjectRequest –要在JsonObjectRequest中发布参数,我们将参数传递到JSONObject内,然后在构造函数的第二个参数中进行设置。
stringRequestPOSTJSON –要在StringRequest中发布JSON请求正文,我们将覆盖方法
getBody()。
imageLoader()
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.theitroad.volley.RecyclerViewActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
</android.support.constraint.ConstraintLayout>
getImageListener()处理显示默认图像的功能,直到收到网络响应为止,此时它将切换为实际图像或者错误图像。
在侦听器内部传递的参数是视图的实例,默认图像可绘制,错误图像可绘制。
imageRequest()
package com.theitroad.volley;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
public class RecyclerViewActivity extends AppCompatActivity {
RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recyclerview);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
ArrayList userDataLists = (ArrayList) getIntent().getSerializableExtra("users");
RecyclerViewAdapter adapter = new RecyclerViewAdapter(this, userDataLists);
recyclerView.setAdapter(adapter);
}
}
customRequest()
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="60dp">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent">
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/imgNetwork"
android:layout_width="48dp"
android:layout_centerVertical="true"
android:layout_height="48dp"
<TextView
android:id="@+id/txtLabel"
android:layout_toRightOf="@+id/imgNetwork"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:gravity="center"
</RelativeLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>
我们创建了一个名为GsonRequest的CustomRequest,该序列将响应内部序列化并将其转换为POJO类。
另外,CustomRequest也将标头用作构造函数参数。
让我们看看如何编写GsonRequest.java类。
GsonRequest.java
package com.theitroad.volley;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v7.widget.RecyclerView;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.NetworkImageView;
import java.util.List;
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.CustomRecyclerView> {
private List<UserList.UserDataList> itemList;
private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;
public RecyclerViewAdapter(Context context, List<UserList.UserDataList> itemList) {
this.itemList = itemList;
mRequestQueue = SingletonRequestQueue.getInstance(context).getRequestQueue();
mImageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap> mCache = new LruCache<>(10);
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
}
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
});
}
@Override
public CustomRecyclerView onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_row, null);
CustomRecyclerView rcv = new CustomRecyclerView(layoutView);
return rcv;
}
@Override
public void onBindViewHolder(CustomRecyclerView holder, int position) {
UserList.UserDataList myData = itemList.get(position);
holder.txtLabel.setText(myData.first_name + " " + myData.last_name);
holder.avatar.setImageUrl(myData.avatar, mImageLoader);
}
@Override
public int getItemCount() {
return this.itemList.size();
}
public class CustomRecyclerView extends RecyclerView.ViewHolder {
TextView txtLabel;
NetworkImageView avatar;
CustomRecyclerView(View itemView) {
super(itemView);
txtLabel = itemView.findViewById(R.id.txtLabel);
avatar = itemView.findViewById(R.id.imgNetwork);
}
}
}
自定义请求需要覆盖" parseNetworkResponse"和" deliverResponse"这两种方法。
parseNetworkResponse解析原始网络响应。
在上面的代码中,Gson库将其序列化为POJO类。
结果可以在deliverResponse方法中找到。
在deliverResponse方法中,我们触发了Response.Listener的onResponse方法的回调。
上面的GsonRequest自定义请求并没有简化和泛化用于在POJO类中存储响应的代码。
下面提供了MainActivity.java类的完整源代码。
private void POSTStringAndJSONRequest() {
RequestQueue queue = SingletonRequestQueue.getInstance(getApplicationContext()).getRequestQueue();
VolleyLog.DEBUG = true;
String uri = BASE_URL + "/api/users";
StringRequest stringRequest = new StringRequest(Request.Method.POST, uri, new Response.Listener() {
@Override
public void onResponse(String response) {
VolleyLog.wtf(response, "utf-8");
Toast.makeText(getApplicationContext(), response, Toast.LENGTH_LONG).show();
}
}, errorListener) {
@Override
public Priority getPriority() {
return Priority.LOW;
}
@Override
public Map getParams() {
Map params = new HashMap();
params.put("name", "Anupam");
params.put("job", "Android Developer");
return params;
}
@Override
public Map getHeaders() throws AuthFailureError {
HashMap headers = new HashMap();
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
return headers;
}
};
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("name", "theitroad.local");
jsonObject.put("job", "To teach you the best");
} catch (JSONException e) {
e.printStackTrace();
}
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(uri, jsonObject, new Response.Listener() {
@Override
public void onResponse(JSONObject response) {
VolleyLog.wtf(response.toString(), "utf-8");
Toast.makeText(getApplicationContext(), response.toString(), Toast.LENGTH_LONG).show();
}
}, errorListener) {
@Override
public int getMethod() {
return Method.POST;
}
@Override
public Priority getPriority() {
return Priority.NORMAL;
}
};
StringRequest stringRequestPOSTJSON = new StringRequest(Request.Method.POST, uri, new Response.Listener() {
@Override
public void onResponse(String response) {
VolleyLog.wtf(response);
Toast.makeText(getApplicationContext(), response, Toast.LENGTH_LONG).show();
}
}, errorListener) {
@Override
public Priority getPriority() {
return Priority.HIGH;
}
@Override
public Map getHeaders() throws AuthFailureError {
HashMap headers = new HashMap();
headers.put("Content-Type", "application/json; charset=utf-8");
return headers;
}
@Override
public byte[] getBody() throws AuthFailureError {
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("name", "Android Tutorials");
jsonObject.put("job", "To implement Volley in an Android Application.");
} catch (JSONException e) {
e.printStackTrace();
}
String requestBody = jsonObject.toString();
try {
return requestBody == null ? null : requestBody.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8");
return null;
}
}
};
queue.add(stringRequest);
queue.add(jsonObjectRequest);
queue.add(stringRequestPOSTJSON);
}

