前言
从今年2月份开始接触RxJava,看了不少博客和项目,在8月份的时候把公司的项目完成了改版。使用RxJava+Retrofit2+Dagger2 +MVP开发,关于这种模式的介绍的文章网上琳琅满目,这里就不再赘述了。附录于两个不错的项目,想学习MVP开发的童鞋可以看看。开发大型项目使用mvp再好不过了,可是很多时候对于小型项目,业务逻辑没有那么复杂的情况下,MVC显得更好用。下面进入正文。
Points
RxJava配合Retrofit2做网络请求
Rxlifecycle和CompositeSubscription做异步的生命周期管理
EventBus来做组件间的通信
Bufferkinfe做组件注入和点击事件回调
Okhttp3对网络返回内容设置头部信息,控制台日志,resonpse预处理等配置
Dagger2将M层注入V层
Glide做图片的处理和加载
Gson配合Retrofit2做数据解析
ultra-ptr做列表刷新
项目结构:
api:网络请求接口 网络请求客户端 自定义网络请求异常
beans:实体类(对应网络请求数据结构)
event:项目中的事件,结合eventbus 如登录 用户信息修改 充值 登录异常等
injector:注入相关 component(注射器)和module(全局实例仓库)
net:网络请求中的相关设置和处理类
ui:Activity Fragment 和adapter
utils:工具类
widget:第三方或者自定义控件
wxapi:微信分享
api介绍
ApiInterface中是使用Retrofit2的语法定义的网络请求接口如:
/** * 验证码 */
@FormUrlEncoded
@POST("public/sms")
Observable> get_auth_code(@Field("mobile") String mobile);
......
这里规定了
请求头
请求参数
请求方式
返回结果类型
看不懂的同学可以先看下retrofit官网的介绍。
关于返回类型Observable是RxJava中的被观察者,其中的泛型就是实体类。
ApiService 是请求服务类 ,相当于一个网络帮助类。
private ApiService() {
//HTTP log
HttpLoggingInterceptor httpLoggingInterceptor =newHttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//RequestInterceptor
RequestInterceptor requestInterceptor =newRequestInterceptor();
//ResponseInterceptor
ResponseInterceptor responseInterceptor =newResponseInterceptor();
//OkHttpClient
OkHttpClient mOkHttpClient =newOkHttpClient.Builder()
.connectTimeout(Constants.TIME_OUT,TimeUnit.SECONDS)
.addInterceptor(requestInterceptor)
.addInterceptor(responseInterceptor)
.addInterceptor(httpLoggingInterceptor)
.build();
//Retrofit
Retrofit mRetrofit =newRetrofit.Builder()
.client(mOkHttpClient)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(Constants.IP)
.build();
//ApiInterface
mApiInterface= mRetrofit.create(ApiInterface.class);
}
再其构造方法中初始化retrofit2客户端 并完成代理 mRetrofit.create(ApiInterface.class)进行这一部操作后,返回的ApiInterface就是被代理后的Interface,便有了网络请求的功能。构造方法私有的原因是为了写单例模式,一个项目中,我们只需要一个网络请求客户端。
单例模式(写法有很多种,看个人爱好随便选一种就好)
//Singleton
private static class SingletonHolder {
private static final ApiService INSTANCE = new ApiService();
}
//Instance
public static ApiService getApiService() {
return SingletonHolder.INSTANCE;
}
ApiService 应定义好请求的方法,这里举出两个例子
/**
* 验证码
*/
public Subscription get_auth_code(Subscriber subscriber, String mobile) {
return mApiInterface.get_auth_code(mobile)
.compose(new SchedulersTransformer<>())
.map(new HttpResultFunc<>())
.subscribe(subscriber);
}
......省略的其他api
接受一个subscriber 和请求的参数 使用RxJava完成设置线程和订阅,这种是第一种情况,这种情况,这个链式任务在这里就结束了,把结果发送给subscriber 。
/**
* 登录
*/
public Observable login(String mobile, String pwd, String device_token) {
Map map = new HashMap<>();
map.put("mobile", mobile);
map.put("pwd", pwd);
map.put("device_token", device_token);
return mApiInterface.login(map)
.compose(new SchedulersTransformer<>())
.map(new HttpResultFunc<>());
}
登录下载完成后拿到数据,我们还要存到本地,这部操作我们可以继续用map执行任务,存完以后再把结果发送给subscriber ,应该是上面的这种写法。然后我们在LoginActivity这样使用
mApi.login( phone, pwd, device_token)
.compose(this.bindToLifecycle())
.flatMap(loginBean -> {
String phoneNumber = vEtPhoneNumber.getText().toString();
SharePreferenceUtil.setLastLoginTime(LoginActivity.this);//最后登录时间
SharePreferenceUtil.savePhoneNumber(LoginActivity.this, phoneNumber);//保存最后登录账号
if (loginBean != null) {
App.saveSessionId(loginBean.session_id);//保存session_id
}
MobclickAgent.onEvent(LoginActivity.this, "login");// 登陆成功计数
//发送登陆成功广播
EventBus.getDefault().post(new LoginEvent());
//加入登录过的账号
Set phones = SharePreferenceUtil.getLoginPhones(LoginActivity.this);
phones.add(phoneNumber);
SharePreferenceUtil.saveLoginPhones(LoginActivity.this, phones);
return Observable.just(true);})
.subscribe(aBoolean -> {
T.showShort(getString(R.string.tip_success_login));
LoginActivity.this.finish();
}, Throwable::printStackTrace);
因为这里是LoginActivity,我们可以不用Subscription 去处理生命周期 ,结合rxlifecycle处理任务的生命周期即.compose(this.bindToLifecycle());
然后接来下我又进行了存用户数据的操作flatMap。最后才把发送Subscriber登录成功。
总结一下:如果需要继续操作下载后的数据在ApiService 求请方法就可以这样写,如果拿到数据不继续操作数据,只是为了展示的话,或者提示可以参考第一种写法。
线程统一处理
/**
* hepeng Created on 2016/11/21.
* 类描述:统一线程处理
*/
public class SchedulersTransformer implements Observable.Transformer {
@Override
public Observable call(Observable tObservable) {
return tObservable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
}
请求方法中的map操作是RxJava的转换,对结果进行处理
/**
* 用来统一处理Http的resultCode,并将HttpResult的Data部分剥离出来返回给subscriber
*
* @param Subscriber真正需要的数据类型,也就是Data部分的数据类型
*/
public class HttpResultFunc implements Func1, T> {
@Override
public T call(HttpResult httpResult) {
if (!httpResult.status.equals("1")) {
if (StringUtil.checkStr(httpResult.errcode)) {
if (httpResult.errcode.equals(Constants.SESSION_EXPIRE)) {
EventBus.getDefault().post(new SessionExpireEvent());
}
}
throw new ApiException(httpResult.errmsg);
}
return httpResult.dataresult;
}
}
这里要先讲一下实体类格式
HttpResult:
status为1表示成功,status为0表示失败,展示errmsg。成功的数据都在dataresult中。
HttpResultFunc就是这这里把错的结果过滤掉,判断了登录过期SESSION_EXPIRE如果过期会发送过期事件,然后把前台真正需要的数据返回即dataresult。
ApiException 就是一个自定义的异常,接受一个错误的message参数
Api到此就结束了。
实体类没什么好讲的搞java或者android应该都知道。
event 包下是一些全局的事件使用方法见EventBus
接下来讲一下injector
dagger2是一个DI类库 想学习的小伙伴可以参考牛晓伟的几篇文章
Android:dagger2让你爱不释手-基础依赖注入框架篇
Android:dagger2让你爱不释手-重点概念讲解、融合篇
Android:dagger2让你爱不释手-终结篇
估计看完你就对dagger2有一点理解了。
下面我相信你已经对dagger2有一定了解了,直接上代码。
AppComponent
/**
* Created by 何鹏 on 2016/8/4.
* 提供全局的实例对象
*/
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
//Exposed to sub-graphs.
Context getContext();
Gson getGson();
ApiService getApiService();
// inject to application
void inject(App app);
}
ActivityComponent
/**
* hepeng Created on 2016/10/12.
* 类名称:ActivityComponent Activity注入类
*/
@PerActivity
@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
//Exposed to sub-graphs.
Activity activity();
// inject Activity
void inject(MainActivity activity);
void inject(PersonInfoActivity activity);
.....
}
FragmentComponent
/**
* 类描述:
* 作者:何鹏 on 2016/4/19 15:23
* 邮箱:[email protected]
*/
@PerFragment
@Component(modules = FragmentModule.class, dependencies = AppComponent.class)
public interface FragmentComponent {
//Exposed to sub-graphs.
Activity getActivity();
Fragment getFragment();
// inject Fragment
void inject(HomeFragment fragment);
void inject(AccountFragment fragment);
......
以上三个类都属于component,ActivityComponent,FragmentComponent都依赖于AppComponent,用来提供实例 并申明接受注入的类。
下面看module
/**
* Created by CAI on 2016/8/4.
* 全局实例仓库
*/
@Module
public class AppModule {
private final Context context;
public AppModule(Context context) {
this.context = context;
}
/**
* @return Context(application)
*/
@Singleton
@Provides
public Context ProvideContext() {
return context;
}
/***
* Gson客户端
*/
@Provides
@Singleton
public Gson provideGson() {
return new Gson();
}
/***
* ApiService
*/
@Provides
@Singleton
public ApiService provideApiService() {
return ApiService.getApiService();
}
......
}
这里提供了 Context Gson 和 ApiService 其他类想用直接注入就好了,下面需要用的时候,我会介绍怎么用。
net
HttpSubscriber 是对RxJava中Subscriber的封装
/**
* hepeng Created on 2016/9/30.
* 类名称: 网络请求订阅者
*/
public abstract class HttpSubscriber extends Subscriber {
@Override
public void onStart() {
super.onStart();
if (!NetUtils.isConnected()) {
if (!isUnsubscribed()) {
unsubscribe();
}
com.shoucainu.shoucainu.utils.T.showShort("请检查网络连接后重试!");
finish();
}
}
@Override
public void onError(Throwable e) {
finish();
if (e instanceof HttpException ||e instanceof ConnectException || e instanceof SocketTimeoutException || e instanceof TimeoutException) {
onNetworkException(e);
} else if (e instanceof ApiException) {
onApiException(e);
} else {
onUnknownException(e);
}
}
@Override
public void onCompleted() {
finish();
}
public abstract void finish();
public void onNetworkException(Throwable e) {
e.printStackTrace();
com.shoucainu.shoucainu.utils.T.showShort("获取数据失败,请检查网络状态");
}
public void onApiException(Throwable e) {
e.printStackTrace();
com.shoucainu.shoucainu.utils.T.showShort(e.getMessage());
}
public void onUnknownException(Throwable e) {
e.printStackTrace();
}
}
在任务开始时对网络进行检查 对错误进行区分处理 并且onError 和 onCompleted或者 onStart 结束后都会回调给子类finish()方法。在回调中处理请求结束的改变,若成功还会回调onNext()方法,会把请求下的数据回调给子类。
RequestInterceptor
public class RequestInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
//请求定制:添加请求头
Request.Builder requestBuilder = original
.newBuilder()
.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
//set Cookie
String sessionId = App.getSessionId();
if (StringUtil.checkStr(sessionId)) {
requestBuilder.addHeader("Cookie", "PHPSESSID=" + sessionId);
}
//请求体定制:统一添加参数
if (original.body() instanceof FormBody) {
FormBody.Builder newFormBody = new FormBody.Builder();
FormBody oidFormBody = (FormBody) original.body();
for (int i = 0; i < oidFormBody.size(); i++) {
newFormBody.addEncoded(oidFormBody.encodedName(i), oidFormBody.encodedValue(i));
}
String client = Constants.CONFIG_CLIENT;
String key = Constants.CONFIG_KEY;
String sign = Md5Util.getMD5Str(client + "|" + key);
newFormBody.add("_deviceid_", App.getDeviceId());
newFormBody.add("_client_", client);
newFormBody.add("_sign_", sign);
newFormBody.add("version", Constants.CONFIG_VERSION);
newFormBody.add("channel", App.getUmengChannel());
requestBuilder.method(original.method(), newFormBody.build());
}
return chain.proceed(requestBuilder.build());
}
RequestInterceptor 是统一设置请求头和一些common的请求参数
ResponseInterceptor
/**
* hepeng Created on 2016/10/8.
* 类名称:ResponseInterceptor
*/
public class ResponseInterceptor implements Interceptor {
String emptyString = ":\"\"";
String emptyObject = ":{}";
String emptyArray = ":[]";
String newChars = ":null";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
ResponseBody responseBody = response.body();
if (responseBody != null) {
String json = responseBody.string();
MediaType contentType = responseBody.contentType();
if (!json.contains(emptyString)) {
ResponseBody body = ResponseBody.create(contentType, json);
return response.newBuilder().body(body).build();
}else {
String replace = json.replace(emptyString, newChars);
String replace1 = replace.replace(emptyObject, newChars);
String replace2 = replace1.replace(emptyArray, newChars);
ResponseBody body = ResponseBody.create(contentType, replace2);
return response.newBuilder().body(body).build();
}
}
return response;
}
}
ResponseInterceptor是对后台返回的结果预处理
接下来介绍重要的部分了
ui
个人认为些项目之前先写好BaseActivity 和BaseFragment很重要 这个写得好 接下来你在写的过程中会感觉无比的轻松。
BaseActivity
/**
* hepeng Created on 2016/10/8.
* 类描述: activity的超类
*/
public abstract class BaseActivity extends RxAppCompatActivity implements View.OnClickListener {
protected String TAG = this.getClass().getSimpleName();
protected ActivityComponent mActivityComponent;
protected SVProgressHUD loading;
private CompositeSubscription mCompositeSubscription;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppManager.getAppManager().addActivity(this);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); // 屏幕竖屏
setContentView(initContentView());
loading = new SVProgressHUD(this);
ButterKnife.bind(this);
initAppComponent();
initInjector();
initUiAndListener();
initData();
}
/**
* 设置view
*/
public abstract int initContentView();
/**
* init UI && Listener
*/
public abstract void initUiAndListener();
/**
* init Data
*/
public abstract void initData();
/**
* init ActivityModule
*/
protected ActivityModule getActivityModule() {
return new ActivityModule(this);
}
/**
* init AppComponent
*/
private void initAppComponent() {
mActivityComponent =DaggerActivityComponent.builder().activityModule(getActivityModule()).appComponent(getAppComponent()).build();
}
/**
* initInjector的默认实现 子类需要时重写即可
*/
protected void initInjector() {
}
/**
* get AppComponent
*/
protected AppComponent getAppComponent() {
return ((App) getApplication()).getAppComponent();
}
/**
* http请求的订阅设置安全的CompositeSubscription
*/
protected synchronized void addSubscription(Subscription subscription) {
if (mCompositeSubscription == null) {
mCompositeSubscription = new CompositeSubscription();
}
mCompositeSubscription.add(subscription);
}
/**
* 跳转一个界面不传递数据
*
* @param clazz 要启动的Activity
*/
protected void startActivity(Class extends BaseActivity> clazz) {
Intent intent = new Intent();
intent.setClass(this, clazz);
startActivity(intent);
}
@Override
protected void onDestroy() {
if (mCompositeSubscription != null) {
mCompositeSubscription.unsubscribe();
}
AppManager.getAppManager().finishActivity(this);
super.onDestroy();
}
/**
* 判断触摸时间派发间隔
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
if (FastDoubleClickUtils.isFastDoubleClick()) {
return true;
}
}
return super.dispatchTouchEvent(ev);
}
}
在onCreate方法中初始化了ContentView Dialog(即SVProgressHUD ) ButterKinfe的注册等
重写dispatchTouchEvent方法是为了防止用户连续点击。
startActivity方法是对intent跳转页面的封装。
看具体的activity 拿NickNameActivity来举例这个Activity的功能是展示并修改用户的昵称
NickNameActivity
/**
* hepeng Created on 2016/10/20.
* 类名称:
*/
public class NickNameActivity extends BaseActivity {
@BindView(R.id.et_nick_name)
ClearEditText vEtNickName;
@Inject
ApiService mApi;
private String oldNickName;
public static void startActivity(Context mContext, String nickName) {
Intent intent = new Intent(mContext, NickNameActivity.class);
intent.putExtra("nickName", nickName);
mContext.startActivity(intent);
}
@Override
public int initContentView() {
return R.layout.activity_nick_name;
}
@Override
public void initUiAndListener() {
setTitle("昵称");
}
@Override
protected void initInjector() {
super.initInjector();
mActivityComponent.inject(this);
}
@Override
public void initData() {
oldNickName = getIntent().getStringExtra("nickName");
if (StringUtil.checkStr(oldNickName)) {
vEtNickName.setText(oldNickName);
vEtNickName.setSelection(vEtNickName.getText().toString().length());//设置光标位置在文本框末尾
}
requestGetNickName();
}
private void requestGetNickName() {
HttpSubscriber subscriber = new HttpSubscriber() {
@Override
public void onNext(NickNameBean nickNameBean) {
if (nickNameBean != null) {
oldNickName = nickNameBean.nickname;
}
}
@Override
public void finish() {
}
};
Subscription subscription = mApi.queryMemberNickName(subscriber);
addSubscription(subscription);
}
@OnClick(R.id.btn_save)
public void onClick() {
if (checkInput()) {
requestChangeNickName();
}
}
private void requestChangeNickName() {
loading.showWithStatus();
HttpSubscriber subscriber = new HttpSubscriber() {
@Override
public void onNext(SuccessBean changeNickNameBean) {
EventBus.getDefault().post(new UserModifyEvent());
if (changeNickNameBean != null) {
if (StringUtil.checkStr(changeNickNameBean.sucmsg))
T.showShort(changeNickNameBean.sucmsg);
}
}
@Override
public void finish() {
loading.dismiss();
NickNameActivity.this.finish();
}
};
Subscription subscription = mApi.modifyNickName(subscriber, vEtNickName.getText().toString().trim());
addSubscription(subscription);
}
private boolean checkInput() {
if (vEtNickName.getText().toString().trim().equals("")) {
T.showShort(getString(R.string.tip_null_nick_name));
vEtNickName.setText("");
vEtNickName.requestFocus();
return false;
}
if (vEtNickName.getText().toString().trim().equals(oldNickName)) {
T.showShort("您还没有修改过您的昵称");
vEtNickName.requestFocus();
return false;
}
return true;
}
}
先是控件和ApiService的注入
@BindView(R.id.et_nick_name) ClearEditText vEtNickName;
@Inject ApiService mApi;
intent传值方式,实现了页面之间的解耦
public static void startActivity(Context mContext, String nickName) {
Intent intent = new Intent(mContext, NickNameActivity.class);
intent.putExtra("nickName", nickName);
mContext.startActivity(intent);
}
其他页面如过想启动这个Activity只需要在自身的点击事件里加上就可以了
NickNameActivity.startActivity(this, nickName);
如果是不需要传递参数的启动用BaseActivity里封装的,ps:这里只是举个例子哈,我这个页面当然是需要一个参数的。
startActivity(NickNameActivity.class);
接下来的几个方法分别是
initContentView
initUiAndListener
initInjector
initData
见名知意,我想就不用介绍了。
requestGetNickName和requestChangeNickName方法分别完成了一次网络请求。
一个页面就这么愉快的结束了,并且线程很安全(addSubscription方法不要忘记调用,线程安全的关键)。
BaseFragment和Activity很相似,我也不介绍了。
Adapter 我用的是ComomAdapter 和CommonViewHolder (不知是哪位大神首创,写的不错,拿来就用了)大家可以参考。
CommonViewHolder
public class CommonViewHolder {
private SparseArray mViews;
private int mPosition;
private View mConvertView;
public CommonViewHolder( Context context, int layoutId, int position) {
this.mPosition = position;
this.mViews = new SparseArray<>();
this.mConvertView = LayoutInflater.from(context).inflate(layoutId,null);
mConvertView.setTag(this);
}
public static CommonViewHolder get(Context context,View convertView, ViewGroup parent, int layoutId, int postion) {
if (convertView == null) {
return new CommonViewHolder(context, layoutId, postion);
} else {
CommonViewHolder holder = (CommonViewHolder) convertView.getTag();
holder.mPosition = postion;
return holder;
}
}
/**
* 通过viewId获取控件
*/
public T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
/**
* 返回下标
*/
public int getPosition() {
return mPosition;
}
public View getConvertView() {
return mConvertView;
}
public CommonViewHolder setText(int viewId, String text) {
TextView tv = getView(viewId);
tv.setText(checkStr(text) ? text : "");
return this;
}
/**
* 设置显示隐藏的方法
*/
public CommonViewHolder setVisible(int viewId, boolean isVisible) {
View view = getView(viewId);
if (isVisible){
view.setVisibility(View.VISIBLE);
}else {
view.setVisibility(View.GONE);
}
return this;
}
// 判断字符串的合法性
public boolean checkStr(String str) {
if (null == str) {
return false;
}
if ("".equals(str.trim())) {
return false;
}
return true;
}
}
CommonAdapter
public abstract class CommonAdapter extends BaseAdapter {
protected Context mContext;
protected List mDatas;
protected int layoutId;
public CommonAdapter(int layoutId, Context mContext) {
this.layoutId = layoutId;
this.mContext = mContext;
}
public void bindData(List data) {
mDatas = data;
notifyDataSetChanged();
}
public void addAll(List datas) {
if (mDatas != null) {
mDatas.addAll(datas);
notifyDataSetChanged();
}
}
public void clear() {
if (mDatas != null) {
mDatas.clear();
notifyDataSetChanged();
}
}
public void remove(T data) {
if (mDatas != null && mDatas.contains(data)) {
mDatas.remove(data);
notifyDataSetChanged();
}
}
@Override
public int getCount() {
if (mDatas != null) {
return mDatas.size() > 0 ? mDatas.size() : 0;
}
return 0;
}
@Override
public T getItem(int position) {
if (mDatas != null) {
return mDatas.get(position);
}
return null;
}
@Override
public long getItemId(int position) {
return position;
}
public List getData() {
return mDatas;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
CommonViewHolder holder = CommonViewHolder.get(mContext, convertView, parent, layoutId, position);
convert(holder, mDatas.get(position));
return holder.getConvertView();
}
public abstract void convert(CommonViewHolder holder, T t);
}
用法
class StudyAdapter extends CommonAdapter {
public StudyAdapter(Context mContext) {
super(R.layout.item_study, mContext);
}
@Override
public void convert(CommonViewHolder holder, StudyBean bean) {
if (bean != null) {
holder.setText(R.id.tv_study_title, bean.title);
holder.setText(R.id.tv_study_spell, bean.title_py);
GlideLoader.load(bean.logo_url, StudyActivity.this, holder.getView(R.id.iv_study_icon));
holder.getConvertView().setOnClickListener(
v -> WebViewActivity.startActivity(
StudyActivity.this, bean.title,
bean.url)
);
}
}
}
Utils包
Glide作为图片加载也是好用的不行,之前用过ImageLoader和Picasso。Glide的介绍可以看Glide网站
这里我封装了一个工具类,大家喜欢可以拿去用
/**
* hepeng Created on 2016/10/12.
* 类名称:
*/
public class GlideLoader {
/**
* @param url url
* @param object must be Activity Fragment or Context
* @param imageView target
*/
public static void load(String url, Object object, ImageView imageView) {
if (object == null) {
return;
}
if (!StringUtil.checkStr(url)) {
return;
}
if (!url.contains("http")) {
url = Constants.M_IP + url;
}
if (object instanceof Fragment) {
Glide.with(((Fragment) object))
.load(url).into(imageView);
} else if (object instanceof Activity) {
Glide.with(((Activity) object))
.load(url).into(imageView);
} else if (object instanceof Context) {
Glide.with(((Context) object))
.load(url).into(imageView);
} else {
throw new RuntimeException("parameter exception");
}
}
}
举一个在fragment中使用的例子
GlideLoader.load(bannersBean.image, HomeFragment.this, imageView);
关于utils好用的一大堆了,下面列出个人的utils,想用哪个网上收类名就好啦
总结
好吧,差不多已经大致的介绍了一遍了。由于介绍用的是公司的项目不方便放出源码,还请见谅。
以后有什么经验,我会继续分享的。
作者:流风夜雪
邮箱:[email protected]
发布日期:2016-11-16
更新日期:2016-11-21
版权声明:禁止转载,欢迎交流。
附录
MVP项目地址1:GeekNews
MVP项目地址2:TLint for 虎扑体育 基于Dagger2+RxJava+Retrofit开发