Android NoHttp网络请求

介绍

      NoHttp的底层默认使用HttpURLConnection实现,但是NoHttp网络层接口允许在初始化的时候配置,所以它允许无缝替换底层框架,NoHttp作者也提供了一个基于OKHttp的底层接口实现。
官方文档http://doc.nohttp.net/

依赖        

  • 如果仅仅使用HttpURLConnection作为网络层,在app的gralde中添加以下依赖即可:
implementation 'com.yanzhenjie.nohttp:nohttp:1.1.1'
  • 如果要使用OkHttp作为网络层,请再依赖(注意两个lib的版本需要一致):
implementation 'com.yanzhenjie.nohttp:okhttp:1.1.1'

    注意:不论使用基于HttpURLConnection还是OkHttp的版本,NoHttp的使用方法都不会变,这是NoHttp的优点之一。 

初始化与配置

NoHttp初始化需要一个Context,最好在Application#onCreate()中初始化,记得在manifest.xml中注册Application

Application:

package com.yanzhenjie.simple;

public class MyApplication extends android.app.Application {

    @Override
    public void onCreate() {
        super.onCreate();
        ...
    }

}

 manifest.xml:

...

<application
	android:name="com.yanzhenjie.simple.MyApplication"
	...
 />

 默认初始化

        如果使用默认始化后,一切采用默认设置。如果你需要配置全局超时时间、缓存、Cookie、底层为OkHttp的话,请看高级初始化。

...
public class MyApplication extends android.app.Application {

    @Override
    public void onCreate() {
        super.onCreate();
        
        NoHttp.initialize(this); // NoHttp默认初始化。
    }

}

高级初始化

超时配置

如果不设置,默认全局超时时间是10s。

NoHttp.initialize(this, new NoHttp.Config()
    .setConnectTimeout(30 * 1000) // 全局连接超时时间,单位毫秒。
    .setReadTimeout(30 * 1000) // 全局服务器响应超时时间,单位毫秒。
);

配置缓存

默认是开启状态,且保存在数据库。

  • 设置缓存到数据库、禁用缓存
NoHttp.initialize(this, new NoHttp.Config()
	...
	.setCacheStore(
        new DBCacheStore(this) // 配置缓存到数据库。
        .setEnable(true) // true启用缓存,fasle禁用缓存。
    )
);
  • 设置缓存到本地SD卡
NoHttp.initialize(this, new NoHttp.Config()
	...
    .setCacheStore(
        new DiskCacheStore(this) // 配置缓存到SD卡。
    )
);

 配置网络层

        NoHttp的网络层是通过NetworkExecutor接口来配置的,内部提供了一个基于HttpURLConnection的接口实现类URLConnectionNetworkExecutor,在NoHttp项目中用另一个module提供了一个基于OkHttp的接口实现类OkHttpNetworkExecutor,二者选其一即可,关于二者该如何使用选择请看项目如何引入NoHttp

**值得注意的是:**切换了NoHttp的网络底层后,NoHttp的上层代码不需要任何改动,你的应用层代码也不需要任何改动。默认采用HttpURLConnection的实现做底层,既URLConnectionNetworkExecutor

NoHttp.initialize(this, new NoHttp.Config()
    ...
    .setNetworkExecutor(new URLConnectionNetworkExecutor()) // 使用HttpURLConnection做网络层。
);

 如果要使用OkHttp作为网络层,请在app的gradle中添加依赖:

implementation 'com.yanzhenjie.nohttp:okhttp:1.1.11'

然后在初始化的时候这么做:

NoHttp.initialize(this, new NoHttp.Config()
    ...
    .setNetworkExecutor(new OkHttpNetworkExecutor())  // 使用OkHttp做网络层。
);

到底该用OKHttp还是URLConnection

HttpURLConnection是Android系统自带的api,无需依赖其它任何第三方库。

  • HttpURLConnection
  • 不用依赖第三方底层框架,相应的apk的体积也不会增大。
  • 在5.0以下的系统中DELETE请求方法不允许发送body,因此会在http协议的实现上做一些妥协。
  • Android4.4以后HttpURLConnection的底层使用OkHttp2.7.5来实现。
  • OkHttp
  • square开发的第三方框架(非系统集成),相对高效、稳定。
  • 写文档的时候OkHttp已经更新到3.4.1了。
  • 使用OkHttp的好处是第三方框架有bug可以改代码,不像系统集成的api没办法改动。

我个人比较推荐使用OkHttp作为NoHttp的底层,我们公司的所有项目也是用nohttp的,全都是配置okhttp为底层的。

调试模式

        NoHttp的调试模式,主要是提供一个合理的日志来供开发者查看和排查错误,默认的Log的TAG是“NoHttp”字符串。NoHttp的调试模式的控制NoHttp初始化与配置一样,最好在Application#onCreate()中设置。

package com.yanzhenjie.simple;

public class MyApplication extends android.app.Application {

    @Override
    public void onCreate() {
        super.onCreate();
        
        Logger.setDebug(true); // 开启NoHttp调试模式。
		Logger.setTag("NoHttpSample"); // 设置NoHttp打印Log的TAG。
		...
    }

}

权限 

        因为要请求网络、监听网络状态、从SD卡读写缓存、下载文件到SD卡等等,所以需要在manifest.xml中配置以下几个权限,如果你已经配置过了这些权限,请不要重复配置:        

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

 请求方法

这里的请求方法指的是Http的请求方法,例如GET、POST、DELETE等。

NoHttp支持以下8种请求方法,以RequestMethod枚举的形式给出:

public enum RequestMethod {
     GET, POST, PUT, DELETE, HEAD, PATCH, OPTIONS, TRACE
}

所以我们调用的时候是通过:RequestMethod.GET这样来引用其中一个请求方法的。 

指定Request的Method

请求方法需要在构造的时候指定,下面以StringRequest举例说明:

  • GET
    如果不传入第二个参数,默认为GET方法,当然你也可以选择传入:
Request<String> stringReq = NoHttp.createStringRequest(url);
// 或者
Request<String> stringReq = NoHttp.createStringRequest(url , RequestMethod.GET);
  • POST
Request<String> stringReq = NoHttp.createStringRequest(url , RequestMethod.POST);

 同步请求

        Android同步网络请求就是在当前线程发起请求。当然同步请求不能直接在主线程使用,所以一般是在子线程使用这种方法。

NoHttp的核心还是同步请求,下一章要讲的异步请求也是基于同步请求的。

这里以StringRequest为例:

// 创建请求。
Request<String> request = NoHttp.createStringRequest(url, RequestMethod.GET);

// 调用同步请求,直接拿到请求结果。
Response<String> response = NoHttp.startRequestSync(request);

 异步请求

1、创建队列(队列特性讲解点我

RequestQueue queue = NoHttp.newRequestQueue();

2、创建请求

Request<String> request = new StringRequest(url);

// 添加url?key=value形式的参数
request.add("enName", "yanzhenjie");
request.add("zhName", "严振杰");
request.add("website", "http://www.yanzhenjie.com");

3、添加请求到队列

...

queue.add(0, request, new OnResponseListener<String>(){

    @Override
    public void onSucceed(int what, Response<String> response) {
        if(response.responseCode() == 200) {// 请求成功。
            String result = response.get();
        }
    }

    @Override
    public void onFailed(int what, Response<String> response) {
		Excepition exception = response.getException();
        if(exception instanceof NetworkError) {// 网络不好。
        }
        
        // 这里还有很多错误类型,可以看demo:
        https://github.com/yanzhenjie/NoHttp
        ...
    }
    
        @Override
    public void onStart(int what) {
        // 这里可以show()一个wait dialog。
    }

    @Override
    public void onFinish(int what) {
		// 这里可以dismiss()上面show()的wait dialog。
    }
});

这里对其中queue.add(what, request, listener)中的what做个说明,任意添加多个请求到队列时,使用同一个Listener接受结果,listener的任何一个方法被回调时都会返回在添加请求到队列时写的相应what值,可以用这个what来区分是哪个请求的回调结果,你可以理解为它的作用和handlerwhat一样的作用,就是用来区分信号来源的。

这样做的好处是不像其它框架一样,每个请求都new listener()来接受结果,这样及省了代码量,又让代码更加整洁。

当然如果你不想这么用,那么你可以每个请求都new listener()

NoHttp示例:

效果:

app/buil.gradle:

    //nohttp网络请求,默认底层基于HttpUrlConnection
    implementation 'com.yanzhenjie.nohttp:nohttp:1.1.1'
    //nohttp底层基于okhttp
    implementation 'com.yanzhenjie.nohttp:okhttp:1.1.1'

 权限:

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

 Application:

public class MyApp extends Application {

    private static RequestQueue mRequestQueue;

    public static RequestQueue getRequestQueue() {
        return mRequestQueue;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        //初始化nohttp请求
        NoHttp.initialize(this, new NoHttp.Config()
                .setConnectTimeout(30 * 1000) // 全局连接超时时间,单位毫秒。
                .setReadTimeout(30 * 1000)  // 全局服务器响应超时时间,单位毫秒。
                .setNetworkExecutor(new OkHttpNetworkExecutor() // 使用OkHttp做网络层。
                ));

        Logger.setDebug(true); // 开启NoHttp调试模式。
        Logger.setTag("NoHttpSample"); // 设置NoHttp打印Log的TAG。
        mRequestQueue = NoHttp.newRequestQueue();

    }

}

NetUtils:

public class NetUtils {

    private static NetUtils mNetUtils;
    private RequestQueue requestQueue;
    private NetResponseListener netResponseListener;
    private JsonObjectRequest request;

    private NetUtils() {
        requestQueue = NoHttp.newRequestQueue(8);
    }

    public static NetUtils getInstance() {
        if (mNetUtils == null) {
            synchronized (NetUtils.class) {
                if (mNetUtils == null) {
                    mNetUtils = new NetUtils();
                }
            }
        }
        return mNetUtils;
    }

    public void setNetResponseListener(NetResponseListener listener) {
        this.netResponseListener = listener;
    }

    /**
     * Get请求
     *
     * @param what
     * @param params -参数
     * @param url    -绝对路径
     */
    public void createGetStirngRequst(int what, Map<String, Object> params, String url) {
        // 请求String:
        request = new JsonObjectRequest(url, RequestMethod.GET);
        request.setCancelSign(url + params);
        if (params != null) {
            try {
                request.add(params);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        requestQueue.add(what, request, new JSONObjectResponseListener());
    }

    /**
     * Post请求
     *
     * @param what
     * @param params -参数
     * @param url    -绝对路径
     */
    public void createPostStirngRequst(int what, Map<String, Object> params, String url) {
        request = new JsonObjectRequest(url, RequestMethod.POST);
        request.setCancelSign(url + params);
        if (params != null) {
            try {
                request.add(params);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        requestQueue.add(what, request, new JSONObjectResponseListener());
    }

    /**
     * POST 请求
     *
     * @param what
     * @param jsonStr 请求体为JSON字符串
     * @param url
     */
    public void createPostStirngRequstJson(int what, String jsonStr, String url) {
        Log.e("POST", "jsonStr" + jsonStr);
        request = new JsonObjectRequest(url, RequestMethod.POST);
        request.setCancelSign(url);
        request.setDefineRequestBodyForJson(jsonStr);
        requestQueue.add(what, request, new JSONObjectResponseListener());
    }

    protected class JSONObjectResponseListener implements OnResponseListener<JSONObject> {

        @Override
        public void onStart(int what) {
            if (netResponseListener != null) {
                netResponseListener.onStart(what);
            }
        }

        @Override
        public void onSucceed(int what, Response<JSONObject> response) {
            if (netResponseListener != null) {
                Log.e("NetUtils", "请求成功:" + response.toString());
                int code;
                String msg = "";
                if (response != null) {
                    code = response.get().optInt("code");
                    msg = response.get().optString("msg");
                    if (code == 0) {
                        netResponseListener.onSuccess(what, response.get());
                    }else{
                        netResponseListener.onFailed(what, response.responseCode(), msg);
                    }
                }else{
                    netResponseListener.onFailed(what, response.responseCode(), msg);
                }
            }
        }

        @Override
        public void onFailed(int what, Response<JSONObject> response) {
            Log.e("NetUtils", "请求失败:" + response.toString());
            netResponseListener.onFailed(what, -1, "");
        }

        @Override
        public void onFinish(int what) {
            if (netResponseListener != null && requstFinish()) {
                netResponseListener.onFinish(what);
            }
        }
    }

    /**
     * 网络请求是否完成
     *
     * @return
     */
    public boolean requstFinish() {
        return requestQueue.unFinishSize() == 0 || requestQueue.unStartSize() == 0;
    }


}

NoHttpActivity:

public class NoHttpActivity extends AppCompatActivity {

    private TextView mTishiTv;
    private TextView mContentTv;
    private EditText mDateEdit;
    private SharedPreferences mSp;
    private SharedPreferences.Editor mEdit;

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

        mSp = getSharedPreferences("age_sp", MODE_PRIVATE);
        mEdit = mSp.edit();

        initView();

    }

    private void initView() {

        mDateEdit = findViewById(R.id.date_edit);
        mContentTv = findViewById(R.id.content_tv);
        mTishiTv = findViewById(R.id.tishi_tv);
        String age = mSp.getString("age", "01-01");
        mDateEdit.setText(age);
        findViewById(R.id.query_btn_tv).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (TextUtils.isEmpty(mDateEdit.getText().toString().trim())) {
                    Toast.makeText(NoHttpActivity.this, "还没输入年龄呢,先输一下年龄叭.....", Toast.LENGTH_SHORT).show();
                    return;
                }

                String getAge = mDateEdit.getText().toString().trim();

                query(getAge);

            }
        });
    }

    /**
     * 生日密码接口API
     * (http://apis.juhe.cn/fapig/birthdayPassword/query)
     * @param age
     */
    private void query(final String age) {
        String url = "http://apis.juhe.cn/fapig/birthdayPassword/query";
        NetUtils mInstance = NetUtils.getInstance();
        Map<String, Object> map = new HashMap<>();
        map.put("keyword", age);
        map.put("key", "245af794c4107807ca8d001d5b0b0285");

        mInstance.createGetStirngRequst(0, map, url);
        mInstance.setNetResponseListener(new NetResponseListener() {
            @Override
            public void onSuccess(int what, JSONObject jsonObject) {
                LogUtils.d("querytag", "succeed_jsonObject:" + jsonObject.toString());
                String json = jsonObject.toString();
                String str1 = json.replace("<p>", "");
                String str2 = str1.replace("<\\/p>", "");//反斜杠\  的转义字符\\表示
                LogUtils.d("querytag", "str2:" + str2);
                BirthdayCodeBean birthdayCodeBean = new Gson().fromJson(str2, BirthdayCodeBean.class);
                BirthdayCodeBean.ResultDTO result = birthdayCodeBean.getResult();
                mEdit.putString("age", age);
                mEdit.commit();
                mContentTv.setText(result.toString());
            }

            @Override
            public void onFinish(int what) {
                LogUtils.d("querytag", "start_what:" + what);
            }

            @Override
            public void onFailed(int what, int code, String msg) {
                LogUtils.d("querytag", "failed_response:" + msg + "__what:" + what);
            }

            @Override
            public void onStart(int what) {
                LogUtils.d("querytag", "start_what:" + what);
            }
        });
    }

 LogUtils:

public class LogUtils {

    private final static String APP_TAG = "appName";

    public static void v(String msg) {
        if (Constant.IS_PRINT_LOG) {
            Log.v(APP_TAG, getMsgFormat(msg));
        }
    }

    public static void v(String tag, String msg) {
        if (Constant.IS_PRINT_LOG) {
            Log.v(tag, getMsgFormat(msg));
        }
    }


    public static void d(String msg) {
        if (Constant.IS_PRINT_LOG) {
            Log.d(APP_TAG, getMsgFormat(msg));
        }
    }

    public static void d(String tag, String msg) {
        if (Constant.IS_PRINT_LOG) {
            Log.d(tag, getMsgFormat(msg));
        }
    }


    public static void i(String msg) {
        if (Constant.IS_PRINT_LOG) {
            Log.i(APP_TAG, getMsgFormat(msg));
        }
    }

    public static void i(String tag, String msg) {
        if (Constant.IS_PRINT_LOG) {
            Log.i(tag, getMsgFormat(msg));
        }
    }


    public static void w(String msg) {
        if (Constant.IS_PRINT_LOG) {
            Log.w(APP_TAG, getMsgFormat(msg));
        }
    }

    public static void w(String tag, String msg) {
        if (Constant.IS_PRINT_LOG) {
            Log.w(tag, getMsgFormat(msg));
        }
    }


    public static void e(String msg) {
        if (Constant.IS_PRINT_LOG) {
            Log.e(APP_TAG, getMsgFormat(msg));
        }
    }

    public static void e(String tag, String msg) {
        if (Constant.IS_PRINT_LOG) {
            Log.e(tag, getMsgFormat(msg));
        }
    }

    public static void e(String tag, String msg, Throwable e) {
        if (Constant.IS_PRINT_LOG) {
            Log.e(tag, getMsgFormat(msg), e);
        }
    }

    public static void e(String msg, Throwable e) {
        if (Constant.IS_PRINT_LOG) {
            Log.e(APP_TAG, getMsgFormat(msg), e);
        }
    }

    private static String getMsgFormat(String msg) {
        return getFunctionName() + " : " + msg;
    }

    /**
     * 格式 Thread:线程名 类名.方法名
     *
     * @return
     */
    private static String getFunctionName() {
        StackTraceElement[] sts = Thread.currentThread().getStackTrace();
        if (sts != null) {
            int index = -1;
            String className = "";
            for (StackTraceElement st : sts) {
                if (st.isNativeMethod()) {
                    continue;
                }
                if (st.getClassName().equals(Thread.class.getName())) {
                    continue;
                }

                if (st.getClassName().equals(LogUtils.class.getName())) {
                    continue;
                }

                //com.aa.bb.类名->类名
                if ((index = st.getClassName().lastIndexOf(".")) != -1) {
                    className = st.getClassName().substring(index + 1);
                }

                return "Thread:" + Thread.currentThread().getName()
                        + " " + className
                        + "." + st.getMethodName();
            }
        }
        return null;
    }

}

 Constant:

public class Constant {

    //是否输出log日志
    public static boolean IS_PRINT_LOG = true;
    
}
activity_no_http.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000"
    android:orientation="vertical"
    tools:context=".NoHttpActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="80dp"
        android:text="输入你的生日叭,我来帮你查查你的生日密码...."
        android:textColor="#03DAC6" />

    <EditText
        android:id="@+id/date_edit"
        android:layout_width="300dp"
        android:layout_height="40dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="15dp"
        android:background="#6200EE"
        android:hint="例如:01-01" />

    <TextView
        android:id="@+id/query_btn_tv"
        android:layout_width="50dp"
        android:layout_height="30dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:background="#EC70FA"
        android:gravity="center"
        android:text="查询"
        android:textColor="#ffffff" />

    <TextView
        android:id="@+id/tishi_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="生日密码会出现在这里→"
        android:textColor="#7459D3" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="20dp"
        android:orientation="vertical">

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="none">

            <TextView
                android:id="@+id/content_tv"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text=""
                android:textSize="16sp"
                android:textColor="#7459D3" />

        </ScrollView>
    </LinearLayout>


</LinearLayout>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值