Toutiao project source code reading

Project address
A third-party Toutiao client, MVP + RxJava + Retrofit

Architecture

Standard MVParchitecture

IBasePresenterFloor

Each P layer has a refresh interface and display network error requirements

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

Each V layer has an interface to display status pages, settings Presenter, and RxJavaunsubscribe to prevent memory leaks.

public interface IBaseView<T> {
    
    
    void onShowLoading();
    void onHideLoading();
    void onShowNetError();
    void setPresenter(T presenter); // P类型由V层泛型决定
    <X> AutoDisposeConverter<X> bindAutoDispose();
}
Analysis of news page structure

Create a new INewsArticleinterface, where the INewsArticle.Viewinterface is NewsArticleViewimplemented INewsArticle.Presenterby and NewsArticlePresenterimplemented by .

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();
    }
}
Analysis of news pages
  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);
        }
    }
}
MVPArchitecture summary
// 顶层接口定义一些共有的方法
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();
    }
}

The internet

The network framework uses Retorfit+OkHttp+RxJava

Cookiemanagement

A third-party library is used. The simple settings are as follows: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();
Cache management

OkHttpIt is recommended to use RxCache for the cache , which is really easy to use.

Dealing with RxJavamemory leaks

The project is not used RxLifecycle, are using the AutoDispose.
Examples:

 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);
         });

ViewHow the layer is implementedbindAutoDispose()

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间的订阅.
    }
}

Highlight effect

TapTargetViewThis library is MainActivity.showTapTarget()used, and it is used in the method in the project .

Dynamically set APP properties

SettingUtilTools in the project can set theme color, navigation bar color, video horizontal screen, font size, etc.

manifestMedium Activityplacement

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

When the Android device is running, the device configuration may change, such as horizontal and vertical screen switching, keyboard, etc., which will cause the Activity to restart. Before destroying, call onSaveInstanceState() to save some data in the application for use when the Activity is restored. If you want the phone If the configuration changes do not restart the Activity, you need to android:configChangesadd some properties to the current Activity , so that when the configuration changes, the Activity will not be restarted but the Activity onConfigurationChanged()method will be called .

  • navigation: The navigation bar has changed
  • fontScale: The font has changed
  • uiMode: The user mode has changed
  • orientation: The screen orientation has changed
  • screenSize: The screen size has changed
  • keyboardHidden: Keyboard usability has changed

Guess you like

Origin blog.csdn.net/MoLiao2046/article/details/106957752