Toutiao 项目源码阅读

项目地址
一款第三方今日头条客户端, MVP + RxJava + Retrofit

架构

标准的MVP架构

IBasePresenter

每个P层都有刷新界面以及显示网络错误需求

public interface IBasePresenter {
    void doRefresh();
    void doShowNetError();
}
IBaseView

每个V层都有展示状态页,设置Presenter,以及解除RxJava订阅防止内存泄漏的接口.

public interface IBaseView<T> {
    void onShowLoading();
    void onHideLoading();
    void onShowNetError();
    void setPresenter(T presenter); // P类型由V层泛型决定
    <X> AutoDisposeConverter<X> bindAutoDispose();
}
对新闻页架构分析

新建INewsArticle接口,其中INewsArticle.View接口由NewsArticleView实现,INewsArticle.PresenterNewsArticlePresenter实现.

public interface INewsArticle {
    // INewsArticle.View接口继承自IBaseListView接口,可以看作是对IBaseListView接口方法的补充.
    // INewsArticle.View接口中定义了新闻页的行为方法
    // IBaseListView<Presenter> 该泛型决定了P层类型,也就是该P层类型必须为INewsArticle.Presenter类型.
    interface View extends IBaseListView<Presenter> {
        void onLoadData();
        void onRefresh();
    }
    // INewsArticle.Presenter接口继承自IBasePresenter接口,可以看作是对IBaseListView接口方法的扩充
    // INewsArticle.Presenter接口中定义了新闻页P层的行为方法
    interface Presenter extends IBasePresenter {
        void doLoadData(String... category);
        void doLoadMoreData();
        void doSetAdapter(List<MultiNewsArticleDataBean> dataBeen);
        void doShowNoMore();
    }
}
对新闻页分析
  1. NewsArticlePresenter
public class NewsArticlePresenter implements INewsArticle.Presenter {
    private INewsArticle.View view;// NewsArticleView层引用, 用来更新UI.
    public NewsArticlePresenter(INewsArticle.View view) {
            this.view = view;
    }
}
  1. NewsArticleView
// <INewsArticle.Presenter>: 该泛型是用来确定创建得P层对象的类型.
public class NewsArticleView extends BaseListFragment<INewsArticle.Presenter> implements INewsArticle.View {
    // IBaseView接口中定义得setPresenter()方法,在这里实现.
    @Override
    public void setPresenter(INewsArticle.Presenter presenter) {
        if (null == presenter) {
            // this.presenter: 在BaseFragment中定义了一个变量presenter,专门用来接收P层对象..
            this.presenter = new NewsArticlePresenter(this);
        }
    }
}
MVP架构总结
// 顶层接口定义一些共有的方法
interface IBaseP {
}

interface IBaseV<T> {
    // 为V层创建P层对象
    // 泛型T确定了P层对象的类型
    T setPresenter(T t);
}

// 次级接口定义自己特有的方法
interface IMy {
    // <MyP>: 确定P层对象的类型为MyP
    interface MyV extends IBaseV<MyP> {
        void refreshPage();// 更新UI
    }

    interface MyP extends IBaseP {
        void getData();// 获取数据
    }
}
// P层的实现
// <T extends IMy.MyV>: 确定P层持有的V层类型
class MyPresenter<T extends IMy.MyV> implements IMy.MyP {
    T mView;

    public MyPresenter(T mView) {
        this.mView = mView;
    }

    @Override
    public void getData() {
        System.out.println("获取数据");
        mView.refreshPage();// 获取数据之后更新界面
    }
}
// 定义BaseActivity,<T>用来确定P层对象的类型.
abstract class BaseActivity<T> implements IBaseV<T> {
    T mPresenter;

    public BaseActivity() {
        // 调用子类实现的setPresenter()方法,创建P层对象,由父类mPresenter变量保存.
        mPresenter = setPresenter(mPresenter);
    }
}
// V层实现类
// <IMy.MyP>: 确定P层对象类型.
class MyActivity extends BaseActivity<IMy.MyP> implements IMy.MyV {
    public MyActivity() {
        mPresenter.getData();
    }

    @Override
    public void refreshPage() {
        System.out.println("更新界面");
    }
    // IBaseV.setPresenter()接口方法的具体实现.
    @Override
    public IMy.MyP setPresenter(IMy.MyP myP) {
        // <IMy.MyV>: 用来确认P层中V对象的类型.
        return new MyPresenter<IMy.MyV>(this);
    }
}
public class MVP {
    public static void main(String[] args) {
        // 模拟Activity启动之后获取数据然后刷新界面
        new MyActivity();
    }
}

网络

网络框架使用的是Retorfit+OkHttp+RxJava

Cookie管理

使用的是一个第三方库PersistentCookieJar
简单的设置如下:

ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(InitApp.AppContext));
OkHttpClient client = new OkHttpClient.Builder()
        .cookieJar(cookieJar)
        .cache(cache)
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(15, TimeUnit.SECONDS)
        .writeTimeout(15, TimeUnit.SECONDS)
        .retryOnConnectionFailure(true)
        .build();
缓存管理

OkHttp的缓存推荐使用RxCache,这个真的非常好用.

处理RxJava内存泄漏

项目中没有使用RxLifecycle,使用的是AutoDispose.
例子:

 RetrofitFactory.getRetrofit().create(IMobileNewsApi.class)
         .getNewsComment(groupId, offset)
         .subscribeOn(Schedulers.io())
         ...
         .observeOn(AndroidSchedulers.mainThread())
         .as(view.bindAutoDispose()) // bindAutoDispose该方法由View层实现.
         .subscribe(list -> {
             if (null != list && list.size() > 0) {
                 doSetAdapter(list);
             } else {
                 doShowNoMore();
             }
         }, throwable -> {
             doShowNetError();
             ErrorAction.print(throwable);
         });

View层如何实现的bindAutoDispose()

public abstract class BaseFragment<T extends IBasePresenter> extends Fragment implements IBaseView<T> {
    /**
     * 绑定生命周期
     */
    @Override
    public <X> AutoDisposeConverter<X> bindAutoDispose() {
        return AutoDispose.autoDisposable(AndroidLifecycleScopeProvider
                .from(this, Lifecycle.Event.ON_DESTROY)); // 当Activity被销毁时候,接触RxJava间的订阅.
    }
}

高亮提示效果

TapTargetView使用的是这个库,项目中是在MainActivity.showTapTarget()方法中用到.

动态设置APP属性

项目中SettingUtil工具类可以设置主题颜色,导航栏上色,视频横屏,字体大小等.

manifestActivity配置

<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize|uiMode"
    android:label="@string/app_name"
    android:theme="@style/AppTheme.NoActionBar"/>

Android 设备运行时候,设备的配置可能发生改变,如横竖屏切换,键盘等,这样会导致Activity重启,销毁前先调用onSaveInstanceState()保存应用中的一些数据,供Activity恢复时候使用.如果想让手机的配置改变不重启Activity的话需要为当前Activity的android:configChanges添加某些属性,这样当配置改变时不会重启Activity而是会调用Activity的onConfigurationChanged()方法.

  • navigation:导航栏发生了变化
  • fontScale: 字体发生了变化
  • uiMode:用户模式发生了变化
  • orientation:屏幕方向发生了变化
  • screenSize:屏幕大小发生了变化
  • keyboardHidden: 键盘可用性发生了变化
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章