终于结束了接的毕设,虽然这几天好心心累啊,但是拿到全款还是很开心的,记录一下写完项目后的感受和小总结。
Retrofit相关
Retrofit封装
public class RetrofitFactory {
public static RetrofitFactory INSTANCE = new RetrofitFactory();
private static final String TAG = "RetrofitFactory";
private Retrofit mRetrofit;
private Interceptor mInterceptor;
private RetrofitFactory() {
mInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
.addHeader("Host", "120.79.196.225:8080")//坑爹坑爹坑爹 请求不影响 后台调getServerPort()那一系列方法是从Header掉的,加端口的时候获取的不是请求url的端口,是默认的80端口
.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0")
.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
.addHeader("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2")
.addHeader("Accept-Encoding", "gzip, deflate")
.addHeader("Connection", "keep-alive")
.addHeader("Upgrade-Insecure-Requests", "1")
.build();
return chain.proceed(request);
}
};
mRetrofit = new Retrofit.Builder()
.baseUrl(SERVER_ADDRESS)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(initClient())
.build();
}
private OkHttpClient initClient() {
return new OkHttpClient.Builder()
.addInterceptor(mInterceptor)
.addInterceptor(new AddCookiesInterceptor())
.addInterceptor(new ReceivedCookiesInterceptor())
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
}
public <T> T create(Class<T> service) {
Log.d(TAG, "create: " + "211111");
return mRetrofit.create(service);
}
}
拦截器实现cookie操作
项目中使用了cookie时可以使用增加两个拦截器来进行相关cookie的操作。
AddCookiesInterceptor、ReceivedCookiesInterceptor的实现:
public class AddCookiesInterceptor implements Interceptor {
private static final String TAG = "AddCookiesInterceptor";
public AddCookiesInterceptor() {
super();
}
@Override
public Response intercept(Chain chain) throws IOException {
final Request.Builder builder = chain.request().newBuilder();
SharedPreferences sharedPreferences = Constant.context.getSharedPreferences("cookie", Context.MODE_PRIVATE);
Observable.just(sharedPreferences.getString("cookie", ""))
.subscribe(new Action1<String>() {
@Override
public void call(String cookie) {
//添加cookie
if (!Objects.equals(cookie, "")) {
Log.d(TAG, "call: " + cookie);
builder.addHeader("Cookie", cookie);
}
}
});
return chain.proceed(builder.build());
}
}
public class ReceivedCookiesInterceptor implements Interceptor {
private static final String TAG = "ReceivedCookiesIntercep";
public ReceivedCookiesInterceptor() {
super();
}
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (!originalResponse.headers("Set-Cookie").isEmpty()) {
final StringBuffer cookieBuffer = new StringBuffer();
Observable.from(originalResponse.headers("Set-Cookie")).map(new Func1<String, String>() {
@Override
public String call(String s) {
String[] cookieArray = s.split(";");
Log.d(TAG, "call: " + cookieArray[0]);
return cookieArray[0];
}
}).subscribe(new Action1<String>() {
@Override
public void call(String cookie) {
cookieBuffer.append(cookie).append(";");
}
});
SharedPreferences sharedPreferences = Constant.context.getSharedPreferences("cookie", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("cookie", cookieBuffer.toString());
editor.commit();
}
return originalResponse;
}
}
BottomNavigationView去除动画
定义一个Helper类而不是去自定义,比较方便。通过反射去修改ShiftingMode。
public class BottomNavigationViewHelper {
@SuppressLint("RestrictedApi")
public static void removeShifMode(BottomNavigationView view) {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
try {
Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
shiftingMode.setAccessible(true);
shiftingMode.setBoolean(menuView, false);
shiftingMode.setAccessible(false);
for (int i = 0; i < menuView.getChildCount(); ++i) {
BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(i);
itemView.setShiftingMode(false);
itemView.setChecked(itemView.getItemData().isChecked());
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
文件相关操作
单文件上传
- 获取文件路径
在这一步,一加3t会打开最近文件列表,如果直接在这里选择文件,获得到的path并不是真实的文件路径,最后会文件选择失败。获取路径必须侧栏打开文件管理器再去选择文件才能拿到path,到目前还没有找到解决方案。
private void openFileManager() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");//无类型限制
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, 1);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
Uri uri = data.getData();
mFilePath = FileUtil.getPathByUri(this, uri);
if (mFilePath != null) {
mFile = new File(mFilePath);
if (mFile.exists()) {
mTvWenjian.setText("文件选择成功:" + mFile.getName());
return;
}
}
mTvWenjian.setText("文件选择失败");
}
}
- 封装成MultipartBody.Part
if (mFile != null) {
RequestBody requestFile = RequestBody.create(MediaType.parse("*/*"), mFile);
MultipartBody.Part body = MultipartBody.Part.createFormData("file", mFile.getName(), requestFile);
mPresenter.postKaiTFujian(body);
}
- 发送请求
public void postKaiTFujian(MultipartBody.Part body) {
mService.postKaiTiFujian(1, body)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new BaseSubscriber<ResponseBody>() {
@Override
public void onNext(ResponseBody responseBody) {
super.onNext(responseBody);
//...
}
});
}
@Override
public Observable<ResponseBody> postKaiTiFujian(int type, MultipartBody.Part body) {
return RetrofitFactory.INSTANCE.create(SubjectApi.class)
.postFujian(type, body);
}
@Multipart
@POST(Constant.FUJIAN)
Observable<ResponseBody> postFujian(@Part("type") int type, @Part MultipartBody.Part file);
多文件上传
和单文件唯一不一样的就是参数变为List<MultipartBody.Part> parts
RequestBody lunwenFile = RequestBody.create(MediaType.parse("*/*"), mLunwenFile);
MultipartBody.Part lunwen = MultipartBody.Part.createFormData("file", mLunwenFile.getName(), lunwenFile);
parts.add(lunwen);
if (mFujianPath != null) {
RequestBody fujianFile = RequestBody.create(MediaType.parse("*/*"), mFujianFile);
MultipartBody.Part fujian = MultipartBody.Part.createFormData("file", mLunwenFile.getName(), fujianFile);
parts.add(fujian);
}
mPresenter.postDinggao(parts);
@Multipart
@POST(Constant.DINGGAO)
Observable<ResponseBody> postDinggao(@Part("type") int type, @Part List<MultipartBody.Part> parts);
文件下载
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(Info.mKaitiData.attachment));
request.setDestinationInExternalPublicDir("/download/", "kaiti");
DownloadManager downloadManager = (DownloadManager) this.getSystemService(Context.DOWNLOAD_SERVICE);
downloadManager.enqueue(request);
代码复用
写了小万代码也说明我不太会复用,写到最后发现重复代码很多,我也有认识到,列举出来,下次开项目之前先想好再开始写。
Gson实体类封装
public class KaitiAll {
public int status;
public String msg;
public List<KaitiData> data;
}
public class DinggaoAll {
public int status;
public String msg;
public List<DinggaoData> data;
}
这种某某All的实体类我还有很多,写到最后意识到完全可以只写一个的。
class GsonAll<T> {
public int status;
public String msg;
public List<T> data;
}
Subscriber的简化
用rx的时候会传一个Subscriber,实现三个方法,但是平常大部分时间只需要写一个onNext(),所以可以先写一个实现好的BaseSubscriber类。
public class BaseSubscriber<T> extends Subscriber<T> {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(T t) {
}
}
用的时候传BaseSubscriber,重写需要重写的方法就可以了。
new TeacherServiceImpl().getDinggao()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new BaseSubscriber<DinggaoAll>() {
@Override
public void onNext(DinggaoAll dinggaoAll) {
super.onNext(dinggaoAll);
//...
}
});
关于mvp
整个项目都使用了mvp,虽然使用了mvp之后很好修改某个功能,但是代码量和类的数量增加了很多。是因为该去复用的Presenter和view回调接口没有去复用,几乎每个Activity都写了一套完整的mvp新代码,没有想着功能相似的去共用Presenter和回调接口。
关于UI
因为要求用java写,所以能看到我的Activity或者Fragment里面满屏的findViewById啥的,感想就是,如果能用kotlin那就太方便了,关于注入的框架因为不是特别熟悉就没有使用,以后有时间还是要再学一下的。另外,如果页面有类似表单的东西,不要一个一个TextView EditText,能用ListView RecyclerView就用吧,用了就知道会方便很多.....
网友评论