.onErrorResumeNext(new HttpErrorHandler());//判断有没有400的错误
//这里还少了对异常
//订阅观察者
observable.subscribe(observer);
return observable;
}
};
}
拦截器中需要打印日志和时间转换,对此需要几个工具类,所以在com.llw.network下新建一个utils包,下面新建一个DateUitl
package com.llw.network.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
public class DateUtil {
//获取当前完整的日期和时间
public static String getNowDateTime() {
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
return sdf.format(new Date());
}
//获取当前日期
public static String getNowDate() {
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd”);
return sdf.format(new Date());
}
//前一天
public static String getYesterday(Date date) {
String tomorrow = “”;
Calendar calendar = new GregorianCalendar();
calendar.setTime(date);
calendar.add(Calendar.DATE, -1);
date = calendar.getTime();
SimpleDateFormat formatter = new SimpleDateFormat(“yyyy-MM-dd”);
tomorrow = formatter.format(date);
return tomorrow;
}
//后一天
public static String getTomorrow(Date date) {
String tomorrow = “”;
Calendar calendar = new GregorianCalendar();
calendar.setTime(date);
calendar.add(Calendar.DATE, +1);
date = calendar.getTime();
SimpleDateFormat formatter = new SimpleDateFormat(“yyyy-MM-dd”);
tomorrow = formatter.format(date);
return tomorrow;
}
//获取当前时间
public static String getNowTime() {
SimpleDateFormat sdf = new SimpleDateFormat(“HH:mm:ss”);
return sdf.format(new Date());
}
//获取当前日期(精确到毫秒)
public static String getNowTimeDetail() {
SimpleDateFormat sdf = new SimpleDateFormat(“HH:mm:ss.SSS”);
return sdf.format(new Date());
}
//获取今天是星期几
public static String getWeekOfDate(Date date) {
String[] weekDays = {“星期日”, “星期一”, “星期二”, “星期三”, “星期四”, “星期五”, “星期六”};
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
if (w < 0) {
}
w = 0;
return weekDays[w];
}
//计算星期几
private static int getDayOfWeek(String dateTime) {
Calendar cal = Calendar.getInstance();
if (dateTime.equals(“”)) {
cal.setTime(new Date(System.currentTimeMillis()));
} else {
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd”, Locale.getDefault());
Date date;
try {
date = sdf.parse(dateTime);
} catch (ParseException e) {
date = null;
e.printStackTrace();
}
if (date != null) {
cal.setTime(new Date(date.getTime()));
}
}
return cal.get(Calendar.DAY_OF_WEEK);
}
//根据年月日计算是星期几并与当前日期判断 非昨天、今天、明天 则以星期显示
public static String Week(String dateTime) {
String week = “”;
String yesterday = “”;
String today = “”;
String tomorrow = “”;
yesterday = getYesterday(new Date());
today = getNowDate();
tomorrow = getTomorrow(new Date());
if (dateTime.equals(yesterday)) {
week = “昨天”;
} else if (dateTime.equals(today)) {
week = “今天”;
} else if (dateTime.equals(tomorrow)) {
week = “明天”;
} else {
switch (getDayOfWeek(dateTime)) {
case 1:
week = “星期日”;
break;
case 2:
week = “星期一”;
break;
case 3:
week = “星期二”;
break;
case 4:
week = “星期三”;
break;
case 5:
week = “星期四”;
break;
case 6:
week = “星期五”;
break;
case 7:
week = “星期六”;
break;
}
}
return week;
}
//将时间戳转化为对应的时间(10位或者13位都可以)
public static String formatTime(long time) {
String times = null;
if (String.valueOf(time).length() > 10) {// 10位的秒级别的时间戳
times = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(new Date(time * 1000));
} else {// 13位的秒级别的时间戳
times = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(time);
}
return times;
}
//将时间字符串转为时间戳字符串
public static String getStringTimestamp(String time) {
String timestamp = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
Long longTime = sdf.parse(time).getTime() / 1000;
timestamp = Long.toString(longTime);
} catch (ParseException e) {
e.printStackTrace();
}
return timestamp;
}
}
同样再建一个KLog类,用于日志打印。
package com.llw.network.utils;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
- 自定义日志类
*/
public final class KLog {
private static boolean IS_SHOW_LOG = true;
private static final String DEFAULT_MESSAGE = “execute”;
private static final String LINE_SEPARATOR = System.getProperty(“line.separator”);
private static final int JSON_INDENT = 4;
private static final int V = 0x1;
private static final int D = 0x2;
private static final int I = 0x3;
private static final int W = 0x4;
private static final int E = 0x5;
private static final int A = 0x6;
private static final int JSON = 0x7;
public static void init(boolean isShowLog) {
IS_SHOW_LOG = isShowLog;
}
public static void v() {
printLog(V, null, DEFAULT_MESSAGE);
}
public static void v(String msg) {
printLog(V, null, msg);
}
public static void v(String tag, String msg) {
printLog(V, tag, msg);
}
public static void d() {
printLog(D, null, DEFAULT_MESSAGE);
}
public static void d(String msg) {
printLog(D, null, msg);
}
public static void d(String tag, String msg) {
printLog(D, tag, msg);
}
public static void i() {
printLog(I, null, DEFAULT_MESSAGE);
}
public static void i(String msg) {
printLog(I, null, msg);
}
public static void i(String tag, String msg) {
printLog(I, tag, msg);
}
public static void w() {
printLog(W, null, DEFAULT_MESSAGE);
}
public static void w(String msg) {
printLog(W, null, msg);
}
public static void w(String tag, String msg) {
printLog(W, tag, msg);
}
public static void e() {
printLog(E, null, DEFAULT_MESSAGE);
}
public static void e(String msg) {
printLog(E, null, msg);
}
public static void e(String tag, String msg) {
printLog(E, tag, msg);
}
public static void a() {
printLog(A, null, DEFAULT_MESSAGE);
}
public static void a(String msg) {
printLog(A, null, msg);
}
public static void a(String tag, String msg) {
printLog(A, tag, msg);
}
public static void json(String jsonFormat) {
printLog(JSON, null, jsonFormat);
}
public static void json(String tag, String jsonFormat) {
printLog(JSON, tag, jsonFormat);
}
private static void printLog(int type, String tagStr, String msg) {
if (!IS_SHOW_LOG) {
return;
}
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
int index = 4;
String className = stackTrace[index].getFileName();
String methodName = stackTrace[index].getMethodName();
int lineNumber = stackTrace[index].getLineNumber();
String tag = (tagStr == null ? className : tagStr);
methodName = methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(“[ (”).append(className).append(“:”).append(lineNumber).append(“)#”).append(methodName).append(" ] ");
if (msg != null && type != JSON) {
stringBuilder.append(msg);
}
String logStr = stringBuilder.toString();
switch (type) {
case V:
Log.v(tag, logStr);
break;
case D:
Log.d(tag, logStr);
break;
case I:
Log.i(tag, logStr);
break;
case W:
Log.w(tag, logStr);
break;
case E:
Log.e(tag, logStr);
break;
case A:
Log.wtf(tag, logStr);
break;
case JSON: {
if (TextUtils.isEmpty(msg)) {
Log.d(tag, “Empty or Null json content”);
return;
}
String message = null;
try {
if (msg.startsWith(“{”)) {
JSONObject jsonObject = new JSONObject(msg);
message = jsonObject.toString(JSON_INDENT);
} else if (msg.startsWith(“[”)) {
JSONArray jsonArray = new JSONArray(msg);
message = jsonArray.toString(JSON_INDENT);
}
} catch (JSONException e) {
e(tag, e.getCause().getMessage() + “\n” + msg);
return;
}
printLine(tag, true);
message = logStr + LINE_SEPARATOR + message;
String[] lines = message.split(LINE_SEPARATOR);
StringBuilder jsonContent = new StringBuilder();
for (String line : lines) {
jsonContent.append("║ ").append(line).append(LINE_SEPARATOR);
}
Log.d(tag, jsonContent.toString());
printLine(tag, false);
}
break;
default:
break;
}
}
private static void printLine(String tag, boolean isTop) {
if (isTop) {
Log.d(tag, “╔═══════════════════════════════════════════════════════════════════════════════════════”);
} else {
Log.d(tag, “╚═══════════════════════════════════════════════════════════════════════════════════════”);
}
}
}
在com.llw.network下新建一个Interceptor包,包下新建一个RequestInterceptor类,这是一个请求拦截器,里面的代码如下:
package com.llw.network.interceptor;
import com.llw.network.INetworkRequiredInfo;
import com.llw.network.utils.DateUtil;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
-
请求拦截器
-
@author llw
*/
public class RequestInterceptor implements Interceptor {
/**
- 网络请求信息
*/
private INetworkRequiredInfo iNetworkRequiredInfo;
public RequestInterceptor(INetworkRequiredInfo iNetworkRequiredInfo){
this.iNetworkRequiredInfo = iNetworkRequiredInfo;
}
/**
- 拦截
*/
@Override
public Response intercept(Chain chain) throws IOException {
String nowDateTime = DateUtil.getNowDateTime();
//构建器
Request.Builder builder = chain.request().newBuilder();
//添加使用环境
builder.addHeader(“os”,“android”);
//添加版本号
builder.addHeader(“appVersionCode”,this.iNetworkRequiredInfo.getAppVersionCode());
//添加版本名
builder.addHeader(“appVersionName”,this.iNetworkRequiredInfo.getAppVersionName());
//添加日期时间
builder.addHeader(“datetime”,nowDateTime);
//返回
return chain.proceed(builder.build());
}
}
还有一个返回拦截器或者说是响应拦截器。
package com.llw.network.interceptor;
import com.llw.network.utils.KLog;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Response;
/**
-
返回拦截器(响应拦截器)
-
@author llw
*/
public class ResponseInterceptor implements Interceptor {
private static final String TAG = “ResponseInterceptor”;
/**
- 拦截
*/
@Override
public Response intercept(Chain chain) throws IOException {
long requestTime = System.currentTimeMillis();
Response response = chain.proceed(chain.request());
KLog.i(TAG, “requestSpendTime=” + (System.currentTimeMillis() - requestTime) + “ms”);
return response;
}
}
这里面也很简单就是可记录当前这个接口的请求耗费时长,这个时间在网速正常的情况下自然是越短越好,当然这个就是后期的网络方面的优化了。
那么这两个拦截器有了,下面就他们放到OkHttp中,打开NetworkApi
现在这个拦截器就会在请求网络时生效了。
在上面的代码中完成了对OkHttp的优化,OkHttp负责网络访问,使用Retrofit发起网络请求,使用RxJava处理返回结果,在上面只是做了线程的切换和错误码的处理,所以还需要的返回做一个处理,下面在com.llw.network下新建一个observer包,该包下新建一个BaseObserver的抽象类,里面代码如下:
package com.llw.network.observer;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
/**
-
自定义Observer
-
@author llw
*/
public abstract class BaseObserver implements Observer {
//开始
@Override
public void onSubscribe(Disposable d) {
}
//继续
@Override
public void onNext(T t) {
onSuccess(t);
}
//异常
@Override
public void onError(Throwable e) {
onFailure(e);
}
//完成
@Override
public void onComplete() {
}
//成功
public abstract void onSuccess(T t);
//失败
public abstract void onFailure(Throwable e);
}
这里我并没有重写Observer的所有方法,只用了两个,onNext和onError。
在日常开发中,常常会有多个开发环境,比如测试环境、正式环境。他们的区别其实就是前面的地址不同而已,后面的参数都是一样的。举个例子,加入你是Android开发,你面对了两个后台开发,在项目初期后台的服务器都是在自己的电脑上,因此你需要配置他们电脑的ip地址才能去访问他们所写的接口API,普通做法就是对接A的接口时使用A的ip,对接B的接口时使用B的ip,你可能会觉得不就是修改一行代码的事情吗,不麻烦,那假如让你打包出来测试呢?因为一个APP的出现不能不去测试,开发的话要是能信,还要测试干什么?这是我一个测试朋友说的,一时间我竟无法反驳。因此为了避免不断需要我们去根据不同的网络环境打包测试,就体现出来这个网络环境的重要性了,说了这么多也是一个建议,当然你是否采纳取决于自己,起码我是这么做的。
在com.llw.network下新建一个environment包。包下新建一个NetworkEnvironmentActivity,然后先不管它,因为还需要配置一些东西才行。在res下创建一个layout文件下,在这个文件夹下创建一个activity_network_environment.xml文件,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android=“http://schemas.android.com/apk/res/android”
android:id=“@+id/content”
android:layout_width=“match_parent”
android:layout_height=“match_parent” />
然后在values下新建一个network_array.xml文件,用于网络配置数组参数,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?>正式
测试
1
2
这里我配置了两个环境,一个正式一个测试,实际开发中可能会更多,可根据实际情况进行增减。
然后在drawable下新建一个ic_network_settings.xml,这是一个图标,用路径写的。
<vector xmlns:android=“http://schemas.android.com/apk/res/android”
android:width=“56dp”
android:height=“56dp”
android:viewportWidth=“56”
android:viewportHeight=“56”>
<path
android:fillColor=“#FF000000”
android:pathData=“M8,41.08V2c0,-0.553 -0.448,-1 -1,-1S6,1.447 6,2v39.08C2.613,41.568 0,44.481 0,48c0,3.859 3.14,7 7,7s7,-3.141 7,-7C14,44.481 11.387,41.568 8,41.08zM7,53c-2.757,0 -5,-2.243 -5,-5s2.243,-5 5,-5s5,2.243 5,5S9.757,53 7,53z”/>
<path
android:fillColor=“#FF000000”
android:pathData=“M29,20.695V2c0,-0.553 -0.448,-1 -1,-1s-1,0.447 -1,1v18.632c-3.602,0.396 -6.414,3.456 -6.414,7.161s2.812,6.765 6.414,7.161V54c0,0.553 0.448,1 1,1s1,-0.447 1,-1V34.891c3.4,-0.577 6,-3.536 6,-7.098S32.4,21.272 29,20.695zM27.793,33c-2.871,0 -5.207,-2.336 -5.207,-5.207s2.335,-5.207 5.207,-5.207S33,24.922 33,27.793S30.664,33 27.793,33z”/>
<path
android:fillColor=“#FF000000”
android:pathData=“M56,8c0,-3.859 -3.14,-7 -7,-7s-7,3.141 -7,7c0,3.519 2.613,6.432 6,6.92V54c0,0.553 0.448,1 1,1s1,-0.447 1,-1V14.92C53.387,14.432 56,11.519 56,8zM49,13c-2.757,0 -5,-2.243 -5,-5s2.243,-5 5,-5s5,2.243 5,5S51.757,13 49,13z”/>
然后在strings.xml中增加一个值。
网络环境设置
您已经更改了网络环境,在您退出当前页面的时候APP将会重启切换环境!
下面对网络进行一些配置,在Android9.0及以后版本,默认使用Https访问网络,这导致了不能使用Http,因此要配置允许使用Http,在res下新建一个xml文件夹,在这个文件夹下新建一个network_security_config.xml,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?>然后在这个xml文件夹下再建一个environment_preference.xml文件,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?><ListPreference
android:defaultValue=“1”
android:entries=“@array/environmentName”
android:entryValues=“@array/environmentValues”
android:icon=“@drawable/ic_network_settings”
android:key=“network_environment”
android:summary=“请您选择您需要使用的网络环境,选择完后会重启APP生效”
android:title=“设置网络环境” />
这里默认的值为1,也就是正式环境。现在关于xml就配置完了,该进入这个NetworkEnvironmentActivity里面去写代码了,首先继承AppCompatActivity,重写父类的onCreate方法,然后设置布局。现在看起来这个Activity就和常规的Activity差不多了。
package com.llw.network.environment;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.llw.network.R;
/**
-
设置网络环境Activity
-
@author llw
*/
public class NetworkEnvironmentActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_network_environment);
}
}
当然这还是刚开始,下面一步一步完善这个Activity,首先增加两个成员变量。
//网络环境
public static final String NETWORK_ENVIRONMENT = “network_environment”;
//当前网络环境
private static String mCurrentNetworkEnvironment = “”;
下面会用到缓存,键就是NETWORK_ENVIRONMENT,常规这种键都是大写的。
先在NetworkEnvironmentActivity中创建一个内部类MyPreferenceFragment继承PreferenceFragmentCompat并实现Preference.OnPreferenceChangeListener。
/**
- 内部缓存变化监听类
*/
public static class MyPreferenceFragment extends PreferenceFragmentCompat
implements Preference.OnPreferenceChangeListener {
//创建缓存
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
//这个相当于Activity的setContentView,从资源文件中添Preferences ,选择的值将会自动保存到SharePreferences
addPreferencesFromResource(R.xml.environment_preference);
//设置缓存变化监听 , 通过键来设置监听
findPreference(NETWORK_ENVIRONMENT).setOnPreferenceChangeListener(this);
}
//缓存变化
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (!mCurrentNetworkEnvironment.equalsIgnoreCase(String.valueOf(newValue))) {
//当前值与缓存中不一致时,说明切换了网络,这时提醒一下
Toast.makeText(getContext(), R.string.network_change_tip, Toast.LENGTH_SHORT).show();
}
return true;
}
}
通过这个类,定义xml文件中,的操作方式,ListPreferenc这个控件中,默认是正式环境,当你修改之后,会将你修改的值存到缓存中,然后会进入这个缓存变化的回调中,此时提醒一下开发者,当然此时只是更换了缓存信息而已,此时应该退出当前应用,再重启,重启时读取缓存中的值,根据这个值去使用不同的环境,那么为了让这个过程显得不那么突兀,可以在页面返回的监听中做判断。
/**
- 页面返回
*/
@Override
public void onBackPressed() {
//获取缓存对象
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
//通过键获取缓存则,没有则使用默认值
String value = preferences.getString(NETWORK_ENVIRONMENT, “1”);
if (!mCurrentNetworkEnvironment.equalsIgnoreCase(value)) {
//不一致.说明有修改,从操作系统中结束掉当前程序的进程
android.os.Process.killProcess(android.os.Process.myPid());
} else {
//一致 没有修改则关闭当前页面
finish();
}
}
onBackPressed可以监听页面的返回按钮的点击事件,我在这里判断是否有修改网络环境,因为缓存值修改就意味着网络环境修改,如果已经修改过则在返回页面时结束当前程序的进程,如果没有修改只是关闭当前的Activity而已。
而假如要在启动App时判断当前环境是否为正式环境时,还是需要去缓存来对比的,因此可以再写一个方法来判断当前是否为正式环境,方法如下:
/**
- 是否为正式环境
*/
public static boolean isFormalEnvironment(Application application) {
//获取当前应用的缓存
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(application);
String networkEnvironment = preferences.getString(NETWORK_ENVIRONMENT, “1”);
return “1”.equalsIgnoreCase(networkEnvironment);
}
因为当前只有正式和测试两种情况,因此可以用boolean就可以,多种情况你可以返回一个key的结果,每个key对应不同的网络,自己区分好就行。
最后在onCreate中配置Fragment的replace
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_network_environment);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content, new MyPreferenceFragment())
.commit();
//获取默认缓存
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
//如果没有值就默认为 “1” 在这里 1 表示正式环境
mCurrentNetworkEnvironment = preferences.getString(NETWORK_ENVIRONMENT,“1”);
}
这样这个Activity就写完了,别忘了在AndroidManifest.xml中配置
<manifest xmlns:android=“http://schemas.android.com/apk/res/android”
package=“com.llw.network”>
<activity
android:name=“.environment.NetworkEnvironmentActivity”
android:label=“@string/network_environment_setting”
android:screenOrientation=“portrait” />
但是网络配置这一步还没有结束,之前在NetworkApi中配置了mBaseUrl这个成员变量,还记得吗?之前可是一直没有赋值的,我相信你已经猜到了,更改网络环境,实际上就是在更改mBaseUrl的值,只不过更改之前要根据缓存判断一下。
那么在NetworkApi中再增加一个成员变量
//是否为正式环境
private static boolean isFormal = true;
下面在NetworkApi中新增一个初始化的方法,代码如下:
/**
- 初始化
*/
public static void init(INetworkRequiredInfo networkRequiredInfo) {
iNetworkRequiredInfo = networkRequiredInfo;
//当初始化这个NetworkApi时,会判断当前App的网络环境
isFormal = NetworkEnvironmentActivity.isFormalEnvironment(networkRequiredInfo.getApplicationContext());
if (isFormal) {
//正式环境
mBaseUrl = “http://service.picasso.adesk.com”;
} else {
//测试环境
mBaseUrl = “https://cn.bing.com”;
}
}
同样还要创建一个Service的实例方法,代码如下:
/**
- 创建serviceClass的实例
*/
public static T createService(Class serviceClass) {
return getRetrofit(serviceClass).create(serviceClass);
}
OK,到目前为止,NetworkApi终于是写完了。
下面这个该进入使用环节了,回到app模块。
目前app模块下只有这一个孤零零的MainActivity。首先在app下的com.llw.network下新建一个application包,(在实际开发中尽量要避免包名重复的情况),在这个包下创建一个NetworkRequiredInfo类,然后实现network模块下的INetworkRequiredInfo接口。
你会发现,这个报红,这时因为你没有添加network模块的依赖,那么有三种方式可以添加,
1. 添加网络模块依赖
① 当前项目添加
第一种:
鼠标点击这个报红处,然后使用Alt+Enter,会出现如下弹窗,点击第一项Add dependency on module ‘network’,意思就是添加network模块的依赖。
点击之后,等待即可
然后发现报错了,这个报错是为什么呢?
你打开app的build.gradle就知道了,如下图所示:
我这里解释一下是为什么,随着Gradle版本的更新,以前的一些使用方式就弃用了,比如这个compile就过时了,因此在高版本中可以替换为implementation和api。那么将compile替换成为implementation之后点击右上角的Sync Now进行同步。
这样就编译成功了,上面通过Alt + Enter的方式虽然不用我们改动,但是这个内部机制还是低版本的,它做的无非就是加一句话而已,那么我们也可以自己来加不是吗?
第二种:
打开app的build.gradle,在dependencies{}闭包下添加如下依赖:
//依赖网络模块
implementation project(path: ‘:network’)
注意这个格式,所有的标点符号都是因为英文的,network对应你的模块的名字,它前面还有一个英文冒号。
然后就点击Sync Now同步就可以了。
第三种:
通过File→Project Structure…
或者点击这个图标
都会进入如下页面
然后通过下图这四步操作即可添加这个模块依赖。
然后勾选上,下面的下拉框中可以选择类型。
可以看到高版本AS中已经没有compile的选项了,
点击OK即可。
再点击OK,然后你打开app的build.gradle查看,里面一定多了一个依赖,如下图所示:
这种方式可以把错误和修改的可能性降到最低,推荐使用。
② 其他项目或新项目添加
同样你假如要在一个新的项目中使用这个network模块也可以这么做。比如我打开我之前写的关于高德地图的项目Demo。
里面没有网络模块,因此需要先导入模块才行,通过File→New→Import Module…
点击后出现
找到之前的模块所在路径。
然后点击Finish
然后你就可以通过上面的第三步进行添加依赖了。
新版的AS导入模块其实也差不多,下面的导入模块的图是AS4.2.1版本中导入
之前很多人说没有找到导入模块的地方,其实是观察不够仔细,注意上图标注的地方。
点击OK。
点击Finish完成导入。
这对于不熟悉的朋友来说还是不错的,因为有时候他们配置项目时会出现各种各样的问题,五花八门,最终就是表现为报错了,然后不知道为什么报错,因此详细一点也没有错。
OK下面进入当前项目的使用
2. 使用网络模块
上面由一个NetworkRequiredInfo引发出这么多内容,但是我觉得是有必要讲一下的,也是做一个笔记吧,那么回到这个NetworkRequiredInfo中,
你可以看到现在你就可以导包,然后使用这个INetworkRequiredInfo,导包也是使用Alt+Enter快捷键,如果你这个接口是唯一的,则会直接导包,如果不是唯一的则会出现一个弹窗供你选择要导入的包,所属,还记得上面使用Observer的时候吗?它就是不唯一的,androidx.lifecyle下有,io.reactivex下也有,你要是导错了包,那么后面怎么搞都是有问题的,这都是细节,需要注意才行。
实现里面的方法,最终里面的代码如下:
package com.llw.network.application;
import android.app.Application;
import com.llw.network.BuildConfig;
import com.llw.network.INetworkRequiredInfo;
/**
-
网络访问信息
-
@author llw
*/
public class NetworkRequiredInfo implements INetworkRequiredInfo {
private Application application;
public NetworkRequiredInfo(Application application){
this.application = application;
}
/**
- 版本名
*/
@Override
public String getAppVersionName() {
return BuildConfig.VERSION_NAME;
}
/**
- 版本号
*/
@Override
public String getAppVersionCode() {
return String.valueOf(BuildConfig.VERSION_CODE);
}
/**
- 是否为debug
*/
@Override
public boolean isDebug() {
return BuildConfig.DEBUG;
}
/**
- 应用全局上下文
*/
@Override
public Application getApplicationContext() {
return application;
}
}
相信应该很好理解,然后在这个application包下再创建一个MyApplication类,继承Application。重写onCreate方法,在里面完成对NetworkApi和NetworkRequiredInfo的初始化配置,里面的代码如下:
package com.llw.network.application;
import android.app.Application;
import com.llw.network.NetworkApi;
/**
-
自定义Application
-
@author llw
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//初始化
NetworkApi.init(new NetworkRequiredInfo(this));
}
}
然后打开AndroidManifest.xml中进行配置
在这里配置自定义的Application。
下面就要来显示数据了,
http://service.picasso.adesk.com/v1/vertical/vertical?limit=30&skip=180&adult=false&first=0&order=hot
可以在浏览器数据下面的这个地址,然后会返回如下所示JSON数据:
利用这些数据可以生成一个实体Bean。
在app的com.llw.network下新建一个bean包,里面新建一个GankResponse类,里面的代码如下:
public class WallPaperResponse {
private String msg;
private ResBean res;
private int code;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public ResBean getRes() {
return res;
}
public void setRes(ResBean res) {
this.res = res;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public static class ResBean {
private List vertical;
public List getVertical() {
return vertical;
}
public void setVertical(List vertical) {
this.vertical = vertical;
}
public static class VerticalBean {
private String preview;
private String thumb;
private String img;
private int views;
private String rule;
private int ncos;
private int rank;
private String source_type;
private String wp;
private boolean xr;
private boolean cr;
private int favs;
private double atime;
private String id;
private String store;
private String desc;
private List cid;
private List<?> tag;
private List<?> url;
public String getPreview() {
return preview;
}
public void setPreview(String preview) {
this.preview = preview;
}
public String getThumb() {
return thumb;
}
public void setThumb(String thumb) {
this.thumb = thumb;
}
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public int getViews() {
return views;
}
public void setViews(int views) {
this.views = views;
}
public String getRule() {
return rule;
}
public void setRule(String rule) {
this.rule = rule;
}
public int getNcos() {
return ncos;
}
public void setNcos(int ncos) {
this.ncos = ncos;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
public String getSource_type() {
return source_type;
}
public void setSource_type(String source_type) {
this.source_type = source_type;
}
public String getWp() {
return wp;
}
public void setWp(String wp) {
this.wp = wp;
}
public boolean isXr() {
return xr;
}
public void setXr(boolean xr) {
this.xr = xr;
}
public boolean isCr() {
return cr;
}
public void setCr(boolean cr) {
this.cr = cr;
}
public int getFavs() {
return favs;
}
public void setFavs(int favs) {
this.favs = favs;
}
public double getAtime() {
return atime;
}
public void setAtime(double atime) {
this.atime = atime;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getStore() {
return store;
}
public void setStore(String store) {
this.store = store;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public List getCid() {
return cid;
}
public void setCid(List cid) {
this.cid = cid;
}
public List<?> getTag() {
return tag;
}
public void setTag(List<?> tag) {
this.tag = tag;
}
public List<?> getUrl() {
return url;
}
public void setUrl(List<?> url) {
this.url = url;
}
}
}
}
下面在app的com.llw.network下新建一个api包,包下新建一个ApiService的接口,里面的代码如下:
/**
-
ApiService接口 统一管理应用所有的接口
-
@author llw
*/
public interface ApiService {
/**
- 获取热门壁纸列表
*/
@GET(“/v1/vertical/vertical?limit=30&skip=180&adult=false&first=0&order=hot”)
Observable getWallPaper();
}
现在基本上就配置完毕了,下面就来简单使用一下,修改一个activity_main.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”
tools:context=“.MainActivity”>
<ImageView
android:id=“@+id/imageView”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:scaleType=“centerCrop” />
里面也就一个图片控件而已,然后回到MainActivity中。
文末
不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊
小编将自己6年以来的面试经验和学习笔记都整理成了一个**937页的PDF,**以及我学习进阶过程中看过的一些优质视频教程。
其实看到身边很多朋友抱怨自己的工资很低,包括笔者也是一样的,其原因是在面试过程中没有给面试官一个很好的答案。所以笔者会持续更新面试过程中遇到的问题,也希望大家和笔者一起进步,一起学习。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
lean isXr() {
return xr;
}
public void setXr(boolean xr) {
this.xr = xr;
}
public boolean isCr() {
return cr;
}
public void setCr(boolean cr) {
this.cr = cr;
}
public int getFavs() {
return favs;
}
public void setFavs(int favs) {
this.favs = favs;
}
public double getAtime() {
return atime;
}
public void setAtime(double atime) {
this.atime = atime;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getStore() {
return store;
}
public void setStore(String store) {
this.store = store;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public List getCid() {
return cid;
}
public void setCid(List cid) {
this.cid = cid;
}
public List<?> getTag() {
return tag;
}
public void setTag(List<?> tag) {
this.tag = tag;
}
public List<?> getUrl() {
return url;
}
public void setUrl(List<?> url) {
this.url = url;
}
}
}
}
下面在app的com.llw.network下新建一个api包,包下新建一个ApiService的接口,里面的代码如下:
/**
-
ApiService接口 统一管理应用所有的接口
-
@author llw
*/
public interface ApiService {
/**
- 获取热门壁纸列表
*/
@GET(“/v1/vertical/vertical?limit=30&skip=180&adult=false&first=0&order=hot”)
Observable getWallPaper();
}
现在基本上就配置完毕了,下面就来简单使用一下,修改一个activity_main.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”
tools:context=“.MainActivity”>
<ImageView
android:id=“@+id/imageView”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:scaleType=“centerCrop” />
里面也就一个图片控件而已,然后回到MainActivity中。
文末
不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊
小编将自己6年以来的面试经验和学习笔记都整理成了一个**937页的PDF,**以及我学习进阶过程中看过的一些优质视频教程。
[外链图片转存中…(img-u80nMVHO-1714655507696)]
其实看到身边很多朋友抱怨自己的工资很低,包括笔者也是一样的,其原因是在面试过程中没有给面试官一个很好的答案。所以笔者会持续更新面试过程中遇到的问题,也希望大家和笔者一起进步,一起学习。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!