Android开发——网络请求


    网络请求可以说是Android开发的核心,也同样是Android开发中的难点。常用的是 retrofit2okhttp3,它们都是对最基础的网络请求的一层封装。

1.依赖配置

//引入retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
//顺便引入gson方便对请求中传递的JSON字符串进行对象转化处理
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.2'
api 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.google.code.gson:gson:2.9.1'

2.post登录验证举例

    我们以比较基础的登录注册为例来说明:
首先需要创建一个UploadService ,目的是为了统一管理各种请求接口,通知利用retrofit提供的路由注解方便进行接口请求与规范化。

package com.example.mqttledmenu;

import okhttp3.MultipartBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Path;
import retrofit2.http.Query;

public interface UploadService {
//文件上传使用Multipart注解传递上传的文件流信息
    @POST("upload")
    @Multipart
    Call<ResponseBody> upload(@Part MultipartBody.Part file);
    //post请求中传递的表单信息使用@Field注解
    //@Field("userName")中的userName为表单名称,
    //String username分别为调用此接口的请求参数类型和参数名(get方式也是一样的)
    @FormUrlEncoded//表单注解,用于传递key-value表单信息的post请求
    @POST("login")
    Call<ResponseBody> login(@Field("userName") String username, @Field("userPassword") String password);
    //get请求中传递的请求参数使用@Query注解
    @GET("getNews")
    Call<ResponseBody> getnews(@Query("userId") String user_id);
    //@Path注解,其实是和springboot配套的一种路由检索方式
    @GET("detail/{id}")
    Call<ResponseBody> getDetail(@Path("id") String news_id);
}

使用如下:

Retrofit retrofit = new Retrofit.Builder().baseUrl(Config.serverUrl).build();
UploadService uploadService = retrofit.create(UploadService.class);
Call<ResponseBody> call = uploadService.login(usernameStr, passwordStr);
call.enqueue(new Callback<ResponseBody>() {

    @Override
    public void onResponse (Call < ResponseBody > call, Response < ResponseBody > response){
        String result = null;
        try {
            result = response.body().string();
            //请求对象转化成json对象,然后利用get方法获取对象里面的详细信息
            JsonObject returnData = new JsonParser().parse(result).getAsJsonObject();
            if (!returnData.get("code").toString().equals("200")) {
                Toast.makeText(LoginActivity.this, "用户名或密码错误~", Toast.LENGTH_SHORT).show();
                return;
            }
            JsonObject temp = new JsonParser().parse(returnData.get("data").toString()).getAsJsonObject();
            Config.getInstance().setUserId(temp.get("userId").toString());//存储用户的id
            Config.getInstance().setUserAvatar(temp.get("userAvatar").toString());//存储用户的头像

            if (isRemember.isChecked()) {
                //存储用户用户名和密码
                SharedPreferences.Editor edit = sp.edit();
                edit.putString("username", usernameStr);
                edit.putString("password", passwordStr);
                edit.putBoolean("remember", true);
                edit.apply();
            }
            Intent intent = new Intent();
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setClass(getApplicationContext(), MainActivity.class);
            startActivity(intent);
        } catch (IOException e) {
            Toast.makeText(LoginActivity.this, "服务端异常~", Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }

    }

    @Override
    public void onFailure (Call < ResponseBody > call, Throwable t){

    }
});

3.网络请求列表处理

    通常情况下,我们不仅会获取基础的成功和错误的响应,更多的是返回一个包含JSON数组信息,对于这种响应需要使用gson依赖里面的JSONArray

Retrofit retrofit = new Retrofit.Builder().baseUrl(Config.serverUrl).build();
UploadService uploadService = retrofit.create(UploadService.class);
Call<ResponseBody> call = uploadService.getnews("");
//与后端约定,不传入id直接获取全部新闻
            call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        String result = null;
        try {
            result = response.body().string();
            JsonObject returnData = new JsonParser().parse(result).getAsJsonObject();
            if (!returnData.get("code").toString().equals("200")) {
                Toast.makeText(getActivity(), "服务端错误~", Toast.LENGTH_SHORT).show();
                return;
            }
            try {
                //直接将得到的JSON字符串数组转换成数组对象
                JSONArray myjsonArray = new JSONArray(returnData.get("data").toString());
                for (int i = 0; i < myjsonArray.length(); i++) {
                    JSONObject jsonObject = myjsonArray.getJSONObject(i);
                    //利用得到的信息构造需要使用的自定义NewsItem对象
                    newsList.add(new NewsItem(jsonObject.get("authorAvatar").toString().replace("\"", ""),
                            (String) jsonObject.get("newsAuthor"), (String) jsonObject.get("newsDepartment"),
                            (String) jsonObject.get("newsTitle"), (String) jsonObject.get("newsContent"), (String) jsonObject.get("newsPublishTime"),
                            jsonObject.get("newsId").toString(), 1));
                }
                adapter = new SimpleAdapter(getActivity(), getData(newsList),
                        R.layout.news_adapter_item, new String[]{"avatar", "publisher", "title", "brief", "time"},
                        new int[]{R.id.avatar, R.id.publisher, R.id.title, R.id.brief, R.id.time});
                //设置glide占位符
                RequestOptions requestOptions = new RequestOptions()
                        .placeholder(R.drawable.loading)
                        .error(R.drawable.unknow_person)
                        .fallback(R.drawable.unknow_person);
                //设置头像切换效果(其实比较耗性能)
                DrawableCrossFadeFactory factory = new DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build();
                adapter.setViewBinder(new SimpleAdapter.ViewBinder() {
                    @Override
                    public boolean setViewValue(View view, Object data, String textRepresentation) {
                        if (view instanceof ImageView) {
                            ImageView iv = (ImageView) view;
                            Glide.with(FragmentHome.this)
                                    .load(data.toString())
                                    .apply(requestOptions)
                                    .transition(DrawableTransitionOptions.with(factory)).transform(new CircleCrop()).into(iv);
                            return true;
                        }
                        if (view.getId() == R.id.brief) {
                            TextView tv = (TextView) view;
                            String str = data.toString();
                            if (str.length() > 18) {
                                tv.setText(str.substring(0, 16) + "...");
                                return true;
                            }
                        }
                        return false;
                    }
                });
                setListAdapter(adapter);
            } catch (JSONException e) {
                Log.d("nightowl", "onResponse: 接口调用异常~");
                e.printStackTrace();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {

    }
});

为了方便大家理解,加上NewsItem对象代码:

public class NewsItem {
    public String avatar;
    public String publisher;
    public String apartment;
    public String title;
    public String brief;
    public String time;
    public String news_id;
    public Boolean isRead;

    NewsItem(String avatar, String publisher, String apartment, String title, String brief, String time, String tag, int isRead) {
        this.avatar = avatar;
        this.publisher = publisher + "·" + apartment;
        this.apartment = apartment;
        this.title = title;
        this.brief = brief;
        this.time = time;
        this.news_id = tag;
        this.isRead = isRead == 1;
    }
}

下面对具体的关键逻辑代码详细说明:

adapter =new SimpleAdapter(getActivity(),getData(newsList),
R.layout.news_adapter_item,new String[]{"avatar","publisher","title","brief","time"},
        new int[]{R.id.avatar,R.id.publisher,R.id.title,R.id.brief,R.id.time});

这里其实调用了Android里面的滑动列表SimpleAdapter构造器,这里的news_adapter_item其实就是滑动列表的一个小组件块UI,然后利用实际需要被渲染进入的数组和R.id.*数组写入数据:
在这里插入图片描述
下面给出news_adapter_item单位UI块的实现代码:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:orientation="horizontal"
        android:padding="10dp"
        tools:ignore="MissingConstraints">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal">

            <ImageView
                android:id="@+id/avatar"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:layout_weight="6"
                android:src="@drawable/loading" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginLeft="10dp"
                android:layout_weight="1"
                android:orientation="vertical">

                <TextView
                    android:id="@+id/publisher"
                    android:layout_width="match_parent"
                    android:layout_height="18dp"
                    android:layout_gravity="left"
                    android:text="发布者"
                    android:textColor="@color/black"
                    android:textFontWeight="@integer/material_motion_duration_medium_1"
                    android:textSize="14dp" />
                <TextView
                    android:id="@+id/title"
                    android:layout_width="match_parent"
                    android:layout_height="16dp"
                    android:layout_gravity="left"
                    android:text="通知标题"
                    android:textColor="@color/title"
                    android:textSize="14dp" />

                <TextView
                    android:id="@+id/brief"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_gravity="left"
                    android:text="通知内容简介"
                    android:textColor="@color/brief" />
            </LinearLayout>

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="7"
            android:orientation="vertical">

            <TextView
                android:id="@+id/time"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="9"
                android:gravity="center"
                android:text="消息时间"
                android:textSize="10dp" />

            <ImageView
                android:id="@+id/read"
                android:layout_width="match_parent"
                android:layout_height="24dp"
                android:layout_gravity="center"
                android:layout_weight="1"
                android:src="@drawable/has_read" />
        </LinearLayout>
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

一般这里的数组需要进一步处理,设计成map对象:

//对数据进行加载
private List<? extends Map<String, ?>> getData(ArrayList<NewsItem> newsList) {
    List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
    for (NewsItem item : newsList) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("avatar", item.avatar);
        map.put("publisher", item.publisher);
        map.put("title", item.title);
        map.put("brief", item.brief);
        map.put("time", item.time);
        list.add(map);
    }
    return list;
}

    另外对于头像的处理,需要使用到glide优化头像的显示:

//设置glide占位符
 RequestOptions requestOptions = new RequestOptions()
 .placeholder(R.drawable.loading)
 .error(R.drawable.unknow_person)
 .fallback(R.drawable.unknow_person);
 //设置头像切换效果(其实比较耗性能)
  DrawableCrossFadeFactory factory = new DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build();
  adapter.setViewBinder(new SimpleAdapter.ViewBinder() {
         @Override
         public boolean setViewValue(View view, Object data, String textRepresentation) {
         //由于这里使用的是网络路径的图片,因此需要使用glide加载图片资源(当然还有其他的方法),
         //如果想更加友好的话,可以使用缓存进一步优化
               if (view instanceof ImageView) {
                   ImageView iv = (ImageView) view;
                   Glide.with(FragmentHome.this)
                   .load(data.toString())
                   .apply(requestOptions)
                   .transition(DrawableTransitionOptions.with(factory))
                   .transform(new CircleCrop()).into(iv);
                            return true;
                 }
                 //对新闻的展示作长度截取处理,后面显示...号,使界面更加友好
                if (view.getId() == R.id.brief) {
                        TextView tv = (TextView) view;
                        String str = data.toString();
                        if (str.length() > 18) {
                            tv.setText(str.substring(0, 16) + "...");
                            return true;
                         }
                 }
                  return false;
        }
  });

4.开发上的建议

    设计到网络请求往往需要涉及到生产环境和开发环境的切换,因此建议使用一手单例模式,通过setget函数来获取和修改全局使用的信息,例如用户id等多个页面都需要使用到的信息:

public class Config {//使用单例模式进行全局配置
    public static String serverUrl = "https://android.nightowl.top/";
    public String user_id = "1";
    public String avatar = "";
    private static Config ApplicationConfig = new Config();

    //让构造函数为 private,这样该类就不会被实例化
    private Config() {
    }

    public static Config getInstance() {
        return ApplicationConfig;
    }

    public void setUserAvatar(String avatar) {
        this.avatar = avatar.replace("\"", "");
    }


    public void setUserId(String user_id) {
        this.user_id = user_id;
    }

    public String getUserId() {
        return user_id;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jerry说前后端

请作者喝杯冰阔落~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值