前言

文本已经收录到我的Github个人博客,欢迎大佬们光临寒舍:我的GIthub博客

看完本篇文章的,可以看下带你封装自己的MVP+Retrofit+RxJava2框架(二),里面封装得到了改进

本篇文章需要已经具备的知识:

  • MVP的概念和基本使用
  • Retrofit框架的基本使用
  • RxJava2框架的基本使用
  • ButterKnife框架的基本使用
  • Base基类的概念

学习清单:

  • ActivityFragment基类的封装
  • MVP的封装使用

一.为什么要封装这套框架呢?

在搞清楚这个问题之前,我们回顾一下基本概念

RxJava: ReactiveXJVM上的一个实现,ReactiveX使用Observable序列组合异步和基于事件的程序;掌握了它,你可以优美地处理异步任务和事件的回调

Retrofit:一个 RESTfulHTTP网络请求框架的封装,网络请求的工作本质上是OkHttp 完成,而 Retrofit仅负责 网络请求接口的封装:掌握了它,你能优美地进行网络请求。

MVP:一种解耦模型和视图的模式,是现在很多公司的主流模式。

由此可见,在平时的开发中熟练运用这种模式,不仅可以满足生活中大部分应用程序的场景,还可以为将来的工作积攒宝贵的实战经验。

二.核心用法

本项目基于Android X 进行构建,完整代码可在我的Github上下载:带你封装自己的MVP+Retrofit+RxJava2框架

首先,看一下我们项目的基本结构,下面笔者将为大家详细介绍每个类的相关信息

2.1 基类Base

Base基类是封装了一些基类,方便后面新建新的Activity或者Fragment,减少耦合

2.1.1 BaseActivity

这个类是Activity的基类,注意与下面的BaseMvpActivity区分开

/**
* Description : BaseActivity 基类活动
*
* @author XuCanyou666
* @date 2020/2/2
*/ public abstract class BaseActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
initPresenter();
initViews();
ButterKnife.bind(this);
} /**
* 抽象方法:实例化Presenter
*/
protected abstract void initPresenter(); /**
* 抽象方法:初始化控件,一般在BaseActivity中通过ButterKnife来绑定,所以该方法内部一般我们初始化界面相关的操作
*
* @return 控件
*/
protected abstract void initViews(); /**
* 抽象方法:得到布局id
*
* @return 布局id
*/
protected abstract int getLayoutId(); /**
* 启动Fragment
*
* @param id id
* @param fragment 碎片
*/
protected void startFragment(int id, Fragment fragment) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(id, fragment);
fragmentTransaction.commit();
} }

2.1.2 BaseView

一个接口,说明了每一个View基本需要的一些操作

package com.users.xucanyou666.rxjava2_retrofit_mvp.base;

/**
* created by xucanyou666
* on 2020/1/31 18:26
* email:913710642@qq.com
*/
public interface BaseView { /**
* 显示进度框
*/
void showProgressDialog(); /**
* 关闭进度框
*/
void hideProgressDialog(); /**
* 出错信息的回调
*
* @param result 错误信息
*/
void onError(String result); }

2.1.3 BaseMvpActivity

  • MVP活动的基类

  • 继承自BaseActivity,它是MVP活动的基类,封装好了Presenter的相关操作

package com.users.xucanyou666.rxjava2_retrofit_mvp.base;

/**
* created by xucanyou666 MVP活动的基类,封装好了presenter的相关操作
* on 2019/12/24 20:53
* email:913710642@qq.com
*/ public abstract class BaseMvpActivity<V extends BaseView, P extends BasePresenter> extends BaseActivity { private P presenter; /**
* 初始化presenter
*/
@Override
protected void initPresenter() {
presenter = createPresenter();
if (presenter != null) {
presenter.attachView((V) this);
}
} /**
* 创建presenter
*
* @return Presenter
*/
protected abstract P createPresenter(); /**
* 得到presenter
*
* @return presenter
*/
protected P getPresenter() {
return presenter;
} /**
* 销毁
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (presenter != null) {
presenter.detachView();
}
} }

2.1.4 BaseFragment

  • Fragment的基类

  • 需要注意的是,这里用了ButterKnife框架,对碎片进行了绑定和解绑操作

/**
* Fragment的基类,封装了一些Fragment的相关操作
* created by xucanyou666
* on 2020/1/31 16:21
* email:913710642@qq.com
*/
public abstract class BaseFragment<T extends BasePresenter> extends Fragment implements BaseView {
protected T mPresenter;
protected Context mContext;
protected Bundle mBundle;
protected Unbinder unbinder;
protected View view; /**
* 恢复数据
*
* @param outState bundle
*/
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (mBundle != null) {
outState.putBundle("bundle", mBundle);
}
} /**
* 绑定activity
*
* @param context context
*/
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
mContext = context;
} /**
* 运行在onAttach之后,可以接收别人传递过来的参数,实例化对象
* 可以解决返回的时候页面空白的bug
*
* @param savedInstanceState
*/
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mBundle = savedInstanceState.getBundle("bundle");
} else {
mBundle = getArguments() == null ? new Bundle() : getArguments();
}
//初始化presenter
mPresenter = initPresenter();
} protected T getPresenter() {
return mPresenter;
} /**
* 运行在onCreate之后,生成View视图
*
* @param inflater
* @param container
* @param savedInstanceState
* @return
*/
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = initView(inflater, container, savedInstanceState);
unbinder = ButterKnife.bind(this, view);
return view;
} /**
* 运行在onCreateView之后
* 加载数据
*
* @param savedInstanceState
*/
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mPresenter.attachView(this); } /**
* 跳转Fragment
*
* @param toFragment 跳转去的fragment
*/
public void startFragment(Fragment toFragment) {
Log.d(TAG, "haha");
startFragment(toFragment, null);
} /**
* 跳转Fragment
*
* @param toFragment 跳转到的fragment
* @param tag fragment的标签
*/
public void startFragment(Fragment toFragment, String tag) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.hide(this).add(android.R.id.content, toFragment, tag);
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.commitAllowingStateLoss();
} @Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
} /**
* fragment进行回退
* 类似于activity的OnBackPress
*/
public void onBack() {
getFragmentManager().popBackStack();
} @Override
public void onDetach() {
mPresenter.detachView();
super.onDetach();
} /**
* 初始化Fragment应有的视图
*
* @return view
*/
public abstract View initView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState); /**
* 创建presenter
*
* @return <T extends BasePresenter> 必须是BasePresenter的子类
*/
public abstract T initPresenter(); /**
* 得到context
*
* @return context
*/
@Override
public Context getContext() {
return mContext;
} /**
* 得到bundle
*
* @return bundle
*/
public Bundle getBundle() {
return mBundle;
} /**
* 得到fragment
*
* @return fragment
*/
public Fragment getFragment() {
return this;
} }

2.1.5 BasePresenter


/**
* created by xucanyou666
* on 2020/1/16 17:12
* email:913710642@qq.com
*/
public abstract class BasePresenter<V extends BaseView> {
//将所有正在处理的Subscription都添加到CompositeSubscription中。统一退出的时候注销观察
private CompositeDisposable mCompositeDisposable;
private V baseView; /**
* 和View绑定
*
* @param baseView
*/
public void attachView(V baseView) {
this.baseView = baseView;
} /**
* 解绑View,该方法在BaseMvpActivity类中被调用
*/
public void detachView() {
baseView = null;
// 在界面退出等需要解绑观察者的情况下调用此方法统一解绑,防止Rx造成的内存泄漏
if (mCompositeDisposable != null) {
mCompositeDisposable.dispose();
}
} /**
* 获取View
*
* @return view
*/
public V getMvpView() {
return baseView;
} /**
* 将Disposable添加,在每次网络访问之前初始化时进行添加操作
*
* @param subscription subscription
*/
public void addDisposable(Disposable subscription) {
//csb 如果解绑了的话添加 sb 需要新的实例否则绑定时无效的
if (mCompositeDisposable == null || mCompositeDisposable.isDisposed()) {
mCompositeDisposable = new CompositeDisposable();
}
mCompositeDisposable.add(subscription);
} }

2.1.6 MyApplication

  • 封装了一个可以全局获取Context的方法,参考写法自:《第一行代码--第二版》
  • 注意:记得在AndroidManifest中注册Application
package com.users.xucanyou666.rxjava2_retrofit_mvp.base;

import android.app.Application;
import android.content.Context; /**
* 基类
* created by xucanyou666
* on 2019/11/2 14:46
* email:913710642@qq.com
* @author xucanyou666
*/
public class MyApplication extends Application {
private static Context context; @Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
} public static Context getContext() {
return context;
}
}

2.2 工具类 Util

2.2.1 RetrofitManager

Retrofit单例工具类

/**
* Retrofit单例工具类
* created by xucanyou666
* on 2020/1/16 16:38
* email:913710642@qq.com
*/
public class RetrofitManager {
private Retrofit mRetrofit; //构造器私有,这个工具类只有一个实例
private RetrofitManager() {
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
httpClientBuilder.connectTimeout(15, TimeUnit.SECONDS);
mRetrofit = new Retrofit.Builder()
.client(httpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(BASE_URL)
.build();
} /**
* 静态内部类单例模式
*
* @return
*/
public static RetrofitManager getInstance() {
return Inner.retrofitManager;
} private static class Inner {
private static final RetrofitManager retrofitManager = new RetrofitManager();
} /**
* 利用泛型传入接口class返回接口实例
*
* @param ser 类
* @param <T> 类的类型
* @return Observable
*/
public <T> T createRs(Class<T> ser) {
return mRetrofit.create(ser);
}
}

2.2.2 RxJavaUtil

RxJava的工具类,执行线程调度工作

/**
* created by xucanyou666
* on 2019/11/17 19:20
* email:913710642@qq.com
*
* @author xucanyou666
*/
public class RxJavaUtil {
/**
* 线程调度工作
*
* @param observable 被观察者
* @param <T> 类型
*/
public static <T> Observable toSubscribe(Observable<T> observable) {
return observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
} }

2.3 常量类 Contant

常量池,特别感谢api open网提供的免费API

/**
* created by xucanyou666
* on 2019/11/17 19:01
* email:913710642@qq.com
*/
public class StaticQuality {
public static final String BASE_URL="https://api.gushi.ci/";
}

2.4 接口管理器 Contract

这里集中了一些Model层,Presenter层,View层的与诗歌相关的接口

/**
* 诗歌的接口管理器
* created by xucanyou666
* on 2020/2/2 15:33
* email:913710642@qq.com
*/
public interface IPoetryContract {
interface IPoetryModel {
/**
* 得到诗歌
*
* @return 诗歌
*/
Observable<PoetryEntity> getPoetry();
} interface IPoetryPresenter {
void getPoetry();
} interface IPoetryView extends BaseView {
/**
* @param author 作者
*/
void searchSuccess(String author);
}
}

2.5 实体类 Entity



/**
* 诗歌的实体类
* created by xucanyou666
* on 2020/1/23 21:23
* email:913710642@qq.com
* API返回示例:
* {
* "content": "胡瓶落膊紫薄汗,碎叶城西秋月团。",
* "origin": "从军行七首",
* "author": "王昌龄",
* "category": "古诗文-天气-月亮"
* }
*/
public class PoetryEntity {
private String content; //诗歌内容
private String origin; //来源
private String author; //作者
private String category; //分类 public String getContent() {
return content;
} public void setContent(String content) {
this.content = content;
} public String getOrigin() {
return origin;
} public void setOrigin(String origin) {
this.origin = origin;
} public String getAuthor() {
return author;
} public void setAuthor(String author) {
this.author = author;
} public String getCategory() {
return category;
} public void setCategory(String category) {
this.category = category;
}
}

2.6 Retrofit接口 iApiService



/**
* retrofit接口
* created by xucanyou666
* on 2020/1/23 21:25
* email:913710642@qq.com
*/
public interface GetPoetryEntity {
/**
* 获取古诗词
*
* @return 古诗词
*/
@GET("all.json")
Observable<PoetryEntity> getPoetry();
}

2.7 视图层 View

这里为了减少代码量,方便读者们掌握核心操作,故View层都是用的同一个PresenterModel,仅作学习参考

2.7.1 MainActivity

需要注意的是,这里BaseMvpActivity<activity, presenter>Activity填入的是当前的ActivityPresenter填入的是对应的Presenter


/**
* Description : MainActivity
*
* @author XuCanyou666
* @date 2020/2/3
*/ public class MainActivity extends BaseMvpActivity<MainActivity, PoetryPresenter> implements IPoetryContract.IPoetryView { @BindView(R.id.btn_get_poetry)
Button btnGetPoetry;
@BindView(R.id.tv_poetry_author)
TextView tvPoetryAuthor;
@BindView(R.id.btn_goto_fragment)
Button btnGotoFragment;
@BindView(R.id.ll)
LinearLayout ll; @Override
protected void initViews() { } @Override
protected int getLayoutId() {
return R.layout.activity_main;
} @Override
protected PoetryPresenter createPresenter() {
return PoetryPresenter.getInstance();
} @Override
public void searchSuccess(String author) {
tvPoetryAuthor.setText(author);
} @Override
public void showProgressDialog() { } @Override
public void hideProgressDialog() { } @Override
public void onError(String result) {
Toast.makeText(MyApplication.getContext(), result, Toast.LENGTH_SHORT).show();
} @OnClick({R.id.btn_get_poetry, R.id.btn_goto_fragment})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.btn_get_poetry:
getPresenter().getPoetry();
break;
case R.id.btn_goto_fragment:
startFragment(R.id.ll, new MainFragment());
break;
default:
break;
}
}
}

2.7.2 MainFragment


/**
* Description : MainFragment
*
* @author XuCanyou666
* @date 2020/2/2
*/ public class MainFragment extends BaseFragment<PoetryPresenter> implements IPoetryContract.IPoetryView { @BindView(R.id.btn_get_poetry)
Button btnGetPoetry;
@BindView(R.id.tv_poetry_author)
TextView tvPoetryAuthor; @Override
public View initView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
} @Override
public PoetryPresenter initPresenter() {
return PoetryPresenter.getInstance();
} @Override
public void showProgressDialog() { } @Override
public void hideProgressDialog() { } @Override
public void onError(String result) {
Toast.makeText(MyApplication.getContext(), result, Toast.LENGTH_SHORT).show(); } @OnClick(R.id.btn_get_poetry)
public void onViewClicked() {
getPresenter().getPoetry();
} @Override
public void searchSuccess(String author) {
tvPoetryAuthor.setText(author);
}
}

2.8 Presenter



/**
* created by xucanyou666
* on 2020/1/16 17:09
* email:913710642@qq.com
*/
public class PoetryPresenter extends BasePresenter<IPoetryContract.IPoetryView> implements IPoetryContract.IPoetryPresenter { private static final String TAG = "PoetryPresenter"; private PoetryEntity mPoetryEntity;
private PoetryModel mPoetryModel; private PoetryPresenter() {
mPoetryModel = PoetryModel.getInstance();
} public static PoetryPresenter getInstance() {
return Inner.instance;
} private static class Inner {
private static final PoetryPresenter instance = new PoetryPresenter();
} /**
* 得到诗歌
*/
@Override
public void getPoetry() {
Observable observable = mPoetryModel.getPoetry().doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(Disposable disposable) throws Exception {
addDisposable(disposable);
}
});
observable = RxJavaUtil.toSubscribe(observable);
observable.subscribe(new Observer<PoetryEntity>() {
@Override
public void onSubscribe(Disposable d) {
} @Override
public void onNext(PoetryEntity poetryEntity) {
mPoetryEntity = poetryEntity;
} @Override
public void onError(Throwable e) {
getMvpView().onError(e.getMessage());
Log.d(TAG, "onError: " + e.getMessage());
} @Override
public void onComplete() {
if (mPoetryEntity != null) {
getMvpView().searchSuccess(mPoetryEntity.getAuthor());
}
}
}); }
}

2.9 Model



/**
* created by xucanyou666
* on 2020/1/16 17:06
* email:913710642@qq.com
*/
public class PoetryModel implements IPoetryContract.IPoetryModel { private PoetryModel() { } public static PoetryModel getInstance() {
return Inner.instance;
} private static class Inner {
private static final PoetryModel instance = new PoetryModel();
} /**
* 获取古诗词
*
* @return 古诗词
*/
@Override
public Observable<PoetryEntity> getPoetry() {
return RetrofitManager.getInstance().createRs(GetPoetryEntity.class).getPoetry();
}
}

2.10 app.build.gradle

apply plugin: 'com.android.application'

android {
compileSdkVersion 28
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId "com.users.xucanyou666.rxjava2_retrofit_mvp"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
} dependencies {
// RxJava
implementation 'io.reactivex.rxjava2:rxjava:2.1.12'
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
// Retrofit和jxjava关联
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
// Retrofit使用Gson转换
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
// RxAndroid
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
//引入ButterKnife
implementation "com.jakewharton:butterknife:10.2.0"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
annotationProcessor "com.jakewharton:butterknife-compiler:10.2.0" implementation "com.google.android.material:material:1.0.0"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

三.我在使用中遇到的问题

3.1 网络权限忘记授予

  • 解决措施:加上权限即可
<uses-permission android:name="android.permission.INTERNET" />

3.2 ButterKnife框架版本问题

使用ButterKnife框架的时候

当是androidX的时候,需要implementation 10.2.0版本的ButterKnife

//引入ButterKnife
implementation "com.jakewharton:butterknife:10.2.0"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
annotationProcessor "com.jakewharton:butterknife-compiler:10.2.0"

当是android 28等其他版本的时候,可以导入8.4.0版本的ButterKnife(导入10.2.0版本会出错)

implementation 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'

3.3 ButterKnife需要Java 1.8以上的支持

 compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

3.4 Fragment中点击事件失效的问题

  • 点击事件失效发生的场景:Fragment中初始化控件没有用ButterKnife框架

解决措施如下:

A:方法一:

  • 将控件的初始化放在onCreateView
  • 将控件的点击事件的代码放在onActivityCreated

B:方法二:

  • Fragment中使用ButterKnife框架

如果文章对您有一点帮助的话,希望您能点一下赞,您的点赞,是我前进的动力

本文参考链接:

带你封装自己的MVP+Retrofit+RxJava2框架(一)的更多相关文章

  1. 一步步搭建Retrofit+RxJava+MVP网络请求框架(二),个人认为这次封装比较强大了

    在前面已经初步封装了一个MVP的网络请求框架,那只是个雏形,还有很多功能不完善,现在进一步进行封装.添加了网络请求时的等待框,retrofit中添加了日志打印拦截器,添加了token拦截器,并且对Da ...

  2. android -------- Retrofit + RxJava2.0 + Kotlin + MVP 开发的 WanAndroid 项目

    简介 wanandroid项目基于 Retrofit + RxJava2.0 + Kotlin + MVP 用到的依赖 implementation 'io.reactivex.rxjava2:rxj ...

  3. Android开源实战:使用MVP+Retrofit开发一款文字阅读APP

    文字控 使用MVP+Retrofit开发的一款文艺APP,它是一个非常优美的文字阅读应用,界面基本上符合material design设计规范. 在该项目中,我采用的是MVP架构,该架构目前在Andr ...

  4. MVPArms MVP快速集成框架

    前言 今年的Android技术圈中MVP,Dagger2,Rxjava,Retrofit这些词汇非常火,随便打开一个技术论坛都充斥着大量的关于这些技术的文章,Github也充斥着各种以基于MVP+Re ...

  5. iOS回顾笔记(05) -- 手把手教你封装一个广告轮播图框架

    html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...

  6. 设计模式笔记之四:MVP+Retrofit+RxJava组合使用

    本博客转自郭霖公众号:http://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650236866&idx=1&sn=da66 ...

  7. 一步步搭建Retrofit+RxJava+MVP网络请求框架(一)

    首先,展示一下封装好之后的项目的层级结构. 1.先创建一个RetrofitApiService.java package com.xdw.retrofitrxmvpdemo.http; import ...

  8. Rxjava + retrofit + dagger2 + mvp搭建Android框架

    最近出去面试,总会被问到我们项目现在采用的什么开发框架,不过据我的经验网络框架(volley)+图片缓存(uIl)+数据库(orm)+mvp,不过现在这套框架比较好了,现在采用什么呢?Rxjava + ...

  9. [Android] 转-RxJava+MVP+Retrofit+Dagger2+Okhttp大杂烩

    原文url: http://blog.iliyun.net/2016/11/20/%E6%A1%86%E6%9E%B6%E5%B0%81%E8%A3%85/ 这几年来android的网络请求技术层出不 ...

随机推荐

  1. swift中的坑

    1.NSClassFromString //获取工程名称 let group = Bundle.main.infoDictionary let fileName = group?[kCFBundleE ...

  2. 十七、linux系统磁盘管理

     1.    磁盘是存放许多重要数据的地方,所有了解磁盘是非常重要的.  2.    我们现在一块高达1000GB(1T)磁盘,就是3个盘前(1个3.5寸盘)组成.磁盘在工作的时候,盘片是高速旋转,磁 ...

  3. 6)HTML中a链接跳转地址怎么写

    (1)看 thinkphp5的   附录--->助手函数  --->url 利用url进行书写地址跳转: 比如,你想跳转到cate控制器下的lst方法: <a href=" ...

  4. mui a链接的点击

    mui里面,使用click点击在有时候是无效的,或者点击的位置错位.在别处点击才有效. mui中对a的点击应该这样写: mui('body').on('tap', "#chart" ...

  5. linux中find,locate,whereis,which关系和用法

    主要有find,locate,whereis,which等 1. find是最常用也是最强大的查找命令,它可以查找任何类型的文件. find命令的一般格式为:find <指定目录>< ...

  6. Navicat for MySQL远程连接报10038的错误

    #################################################### """ 1.网络检测 1)ping主机可以: 2)telnet ...

  7. 关于Apache Commons的简介

    Apache Commons是对JDK的拓展,包含了很多开源的工具,用于解决平时编程经常会遇到的问题,减少重复劳动.官网网址:http://commons.apache.org Commons Bea ...

  8. SQL热备原理

  9. MOOC(15)- 接口异常处理

    首先通过fiddler拿到正确的传参 把value复制下来,存到json数据中,传参时通过键去json的值即可 但是运行请求的时候报错了 再运行,还是出错 查看fiddler结果 修改,从fiddle ...

  10. python后端面试第二部分:网络编程和并发编程--长期维护

    1. 简述 OSI 七层协议. 2. 什么是C/S和B/S架构? 3. 简述 三次握手.四次挥手的流程. 4. 什么是arp协议? 5. TCP和UDP的区别? 6. 什么是局域网和广域网? 7. 为 ...