仿B站Android客户端系列(一)项目环境搭建

2017-12-04  本文已影响0人  Misery_Dx

前言

挖了很久的坑来填。。(゚▽゚)/
上接👉👉👉仿B站Android客户端系列(启动篇)

项目架构

项目的基础框架是在学习的时候不断积累出来的,这个项目主要骨架也是MVP架构+Rxjava+Retrofit+Dagger,这也是技术论坛讨论最多的几个第三方库,下面就来具体说说。

RxJava2

RxJava这两年太火了,导致我没办法不用它。。。。。诶呀这么说太不好了,当然前提是它真的好用,能解决很多问题,优化很多代码,主要是用在处理异步操作。它的操作符十分强大,举个栗子:一个页面由多个请求的数据拼凑而成,如果不同请求相互依赖就需要写额外的逻辑来控制,而如果有RxJava的话一个zip操作符就解决了。随着代码逻辑越来越复杂,它体现的作用就越来越大,而且学习Rx的异步、链式调用思想也是很有帮助的。

Retrofit2+Gson+Okhttp3

这套网络请求组合拳可以说是十分实用了,相信大部分开发者都会选择它们,而且还完美契合上面的RxJava。

Dagger2+Butterknife

依赖注入的两把“利刃”,这俩也是优化代码的神器。Dagger2在刚入门的时候感觉非常难以理解,大多数文章都在说用法和注解的意义,然后一个demo甩脸,让人不明所以。但其实把dagger当做一个帮助我们去管理对象初始化的工具,再去学习用法会好懂一些。本项目里主要用Dagger2结合Retrofit来管理Api和结合泛型对Presenter的注入。

黄油刀没什么好说的,主要用到的功能就是绑定控件id,处理点击事件。用起来很舒服。

Fresco

当下强大的图片加载库无非就是Glide、Picasso、Fresco,这里三个库可以说是各有千秋,但是看过对比分析的文章后显而易见Fresco的B格最高,功能最为强大。再加上工作中Glide用的比较多,这里抱着多接触新技术的目的就选择了Fresco。

Fresco相对其他图片框来说没那么无脑,会复杂一点,从使用包括settings都是这样。如果使用默认设置会出现意料之外的问题。例如如果mBitmapMemoryCacheParamsSupplier使用默认不做设置的话,那么浏览图片列表时内存根本刹不住车。

其他

1.Fragmentation

非常实用的Fragment管理库,这个库把Fragment的大多数常见坑都填平了,用这个可以轻松实现并管理单Activity+多Fragment或是多模块Activity+多Fragment。作者还提供了页面组合的栗子,十分良心。

之前再用的时候觉得唯一的缺点就是Activity和Fragment必须继承库中提供的基类,这样大大降低了可扩展性(当有优先级更高的第三方基类需继承时,就会出现问题,目前良好的设计是通过接口+代理模式的方法实现),但是最近1.0版本中作者已经解决了这个问题。

推荐一下Fragmentation这个库,作者维护很勤,良心。

2.Multitype

Multitype这个也安利下,这是一个封装RecyclerView.Adapter用于实现多类型复杂列表的库,通过作者巧妙地设计可以轻松实现多类型列表。这里是使用的一个栗子:

        mAdapter = new MultiTypeAdapter();
        //注册Class Type和ViewBinder
        mAdapter.register(ImageBean.class, new ImageBinder());
        mAdapter.register(TextBean.class, new TextBinder());
        mAdapter.register(VideoBean.class, new VideoBinder());
        mRecyclerView.setAdapter(mAdapter);
        //设置数据
        Items<Object> items = new Items<>();
        items.add(new ImageBean(R.drawable.image1));
        items.add(new ImageBean(R.drawable.image2));
        items.add(new TextBean("text1"));
        items.add(new TextBean("text2"));
        items.add(new TextBean("text3"));
        items.add(new VideoBean(url));
        mAdapter.setItems(items);
        mAdapter.notifyDataSetChanged();

我们所需要做的工作是注册这个type的数据类型和其对应的binder,将具体的布局实现交由viewBinder,结合布局管理器LayoutManager和ItemDecoration可以很酷炫的实现多类型布局。其中binder里方法的名字与Recycler.Adapter别无二致,功能也大相径庭,如下所示:

public abstract class ItemViewBinder<T, VH extends ViewHolder> {

    ......

    @NonNull
    protected abstract VH onCreateViewHolder(
        @NonNull LayoutInflater inflater, @NonNull ViewGroup parent);

    protected abstract void onBindViewHolder(@NonNull VH holder, @NonNull T item);

    protected final int getPosition(@NonNull final ViewHolder holder) {
        return holder.getAdapterPosition();
    }

    @NonNull
    protected final MultiTypeAdapter getAdapter() {
        return adapter;
    }
}

可以说这样设计代码易读性非常高,而且用起来很灵活,可扩展性高。

3.ActivityLifecycleCallbacks

这不是一个类库,是一个偏结构型的设计。上面再讲Fragmentation时说道Activity和Fragment继承的缺点,优秀的类库设计一般会避免这种问题出现,比如通过接口的方式侧面解决问题。

我在看了这篇讲了ActivityLifecycleCallbacks的文章后也开始使用这种方式构建项目里面的基类。

public final class ActivityLifecycleManager implements Application.ActivityLifecycleCallbacks {
......
    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        boolean isIBaseActivity = activity instanceof IBaseActivity;
        boolean isIBaseMvpActivity = activity instanceof IBaseMvpActivity;

        //IBaseActivity 项目中的 Activity 都应实现 IBaseActivity
        if (isIBaseActivity) {
            IBaseActivity iActivity = (IBaseActivity) activity;
            //加载布局
            activity.setContentView(LayoutInflater.from(activity).inflate(iActivity.getLayoutId(), null));
            //创建变量保存实体
            ActivityBean bean = new ActivityBean();
            //绑定布局
            Unbinder unbinder = ButterKnife.bind(activity);
            bean.setUnbinder(unbinder);
            //保存变量
            activity.getIntent().putExtra(EXTRA_ACTIVITY_BEAN, bean);
            //Dagger注入
            iActivity.initInject();
            //IBaseMvpActivity 如果是 MVP Activity 则实现 IBaseMvpActivity
            if (isIBaseMvpActivity) {
                IBaseMvpActivity mvpActivity = (IBaseMvpActivity) activity;
                BaseView view = (BaseView) mvpActivity;
                if (mvpActivity.getPresenter() != null) {
                    mvpActivity.getPresenter().attachView(view);
                }
            }
            //TODO 如有其它特殊 Activity,定义新接口继承IBaseActivity,并在此处理

            //初始化
            iActivity.initViewAndEvent();
        }
        //TODO 第三方的 Activity 如需做公共处理,可在此处添加

    }

    @Override
    public void onActivityStarted(Activity activity) {
        if (activity instanceof IBaseMvpActivity) {
            IBaseMvpActivity mvpActivity = (IBaseMvpActivity) activity;
            mvpActivity.getPresenter().loadData();
        }
    }

    @Override
    public void onActivityResumed(Activity activity) {

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {
        if (activity instanceof IBaseMvpActivity) {
            IBaseMvpActivity mvpActivity = (IBaseMvpActivity) activity;
            mvpActivity.getPresenter().releaseData();
        }
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        if (activity instanceof IBaseActivity) {
            ActivityBean bean = activity.getIntent().getParcelableExtra(EXTRA_ACTIVITY_BEAN);
            bean.getUnbinder().unbind();
            if (activity instanceof IBaseMvpActivity) {
                IBaseMvpActivity iActivity = (IBaseMvpActivity) activity;
                if (iActivity.getPresenter() != null) {
                    iActivity.getPresenter().detachView();
                }
            }
        }
    }

}

在Application中设置就好

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        registerActivityLifecycleCallbacks(new ActivityLifecycleManager());
    }
    
}

通过ActivityLifecycleCallbacks我们可以完成基类中的功能并且适当解耦,很多公共的逻辑都可以写进这里面,与其他基类并不冲突。并且通过Intent还可以对对象进行存储,比如完成ButterKnife的绑定与解绑。

可以发现完全实现了基类的效果,当然用实现接口+代理的模式也可以解决基类冲突的问题,但这不失为一种优雅地解决方式。

项目地址:FakeBiliBili

还可以的话就赏个star吧!(≧▽≦)/

上一篇 下一篇

猜你喜欢

热点阅读