美文网首页安卓安卓资源收集JAVA
少年,老夫带你撸一把Android项目框架,你可想学?

少年,老夫带你撸一把Android项目框架,你可想学?

作者: 香脆的大鸡排 | 来源:发表于2017-02-26 22:29 被阅读7323次

时间从来没有等过我们,岁月这把捅猪刀.捅得你满脸都是沧桑.你一定是为工作操碎了心.不知道现在的身处何处,是否有挚爱的人照顾你.过得快乐或委屈?
哦忘了.你是个有故事的人,"你想ta过得比你要好,希望你永远不都会知道".


有时在过度劳累之后,腰腿酸痛精神不振,好像身体被掏空,是不是*透支了?

骚年莫慌,老夫带你撸个框架,进可重振雄风,退可养精蓄锐!

本篇适合什么样的人群看?

  • 做android有一段时间了,撸码就是一把梭。
  • 项目开工,但是目前不知道怎么去拆分业务和技术依赖分层。
  • 刚刚毕业出来,进了一家不大不小的公司一人我因酒醉,两眼是...啊,呸呸呸!一人单挑项目。

你能在本篇文章中收获什么?

  • 自己动手搭一个通用型依赖框架
  • 学习拆分业务层和技术层
  • and 吹吹水 装装哔

目录

  • 前言
  • 踩坑
  • 分析
  • 实现
  • 总结

前言

本篇是从线上的APP中基于一次次的采坑、总结、分享抽离出来的。但由于商业原因具有不可公开性。所以不会提及任何与业务有关联的场景用例。以及不会包含与任何商用项目相干的提及。所有技术仅仅用于技术学习交流。本篇不会是一个纯代码讲解的文章。文本中的故事也是一个虚构的。具体技术实现请您移步致GIthub,地址在篇尾。特此声明。

致谢

感谢 jeasinlee、LeoFangQ、Michael、zaneCC 几位前辈无私的分享才得已产出。

踩坑

什么?要求一个月后上线?老夫就是一把梭。复制粘贴东拼西凑就能出来。
上线一周后。小王啊。经过产品和老板讨论过后,我们下一步要把首页上的XX改一下。后面主流程不这样走了。你接下来...?
fuck!为毛你们之前不想好。这怎么改。代码是一大坨的。动一下可能就会导致到处都会崩溃。

项目中的代码是下面这样的:

你叫劳资怎么改?当初是你说要一个月上线。这个项目的代码以前只有我和神能看懂。现在只有神了。

分析

想想就气人,都是你们这帮孙子逼逼逼,连夜加班一个月赶出来的东西。上线不到一周就要改。这下我可头大了。之前没有考虑到后期可能会出现维护或者扩展。改?还不要了我命。但也没有更好的办法了。只好默默的理代码了。

第二天项目里进来了一个看起来像dalao的气息的人,dalao看完会心一笑,小王啊。这个时候我们宁可重构一把也不可在原有的基础上改,救得了今天救不过明天。这样,我向上级申请延长一些时间,我们一起来重新设计一把架构把。

之前的项目结构:

一把梭项目架构

dalao的项目结构:


dalao的架构

实现

等一下,dalao。你直接给我上个图,我看不懂呀。并且也没有解释清楚怎么实现分离的呢。

dalao咳咳两声喃喃道:首先,我们整体的思路是让现在开发的APP去依赖AndroidBasicLibs,我们尽量做到精简只需要在Gradle依赖它就行了。

 dependencies { compile 'xxx.xxx.xxx:AndroidBasicLibs' }

AndroidBasicLibs作为一个通用型的依赖库,它拥有三只麒麟臂,分别是basekit、common、uihelper。

AndroidBasicLibs

basekit

basekit是一个基于MVP+RXjava的的基础框架,它承载了MVP的设计风格,我们的上层APP只需要继承它的BaseActivity、BaseModel 、IBaseView 、BasePresenter 就可以了。上层要做的仅仅只是往对应的层填充独有的业务。如下图5是Base。 图6是APP中的业务单元。具体用法请参考GIthub,地址在本文末尾。

图5 图6

common

common是一个通用型的工具箱集,它可以对上层所依赖的第三方框架做解耦操作。如何理解这句话呢。假如我们要做图片加载,图片加载框架你肯定不会自己重复造轮子。在没有common层时。项目1.0的依赖的是ImageLoader,2.0的时候发现这个库有BUG需要将ImageLoader替换成主流的glide框架。但因为项目中多处都有使用到ImageLoader的API。无法做到加载图片处只改一行代码,就能让所有的业务都换了新的加载工具。这时common的威力就显而易见。,中间做解耦,上层调用common。common调用第三方依赖。当然这只是一个列子。还有很多地方都可以进行二次封装。比如网络请求、视图动画等我们把这些封装都放在common。下面是图片加载的二次封装实现代码。

//用接口统一约束Api
public interface ILoader {
    void init(Context context);

    void loadNet(ImageView target, String url, Options options);

    void loadResource(ImageView target, int resId, Options options);

    void loadAssets(ImageView target, String assetName, Options options);

    void loadFile(ImageView target, File file, Options options);

    void clearMemoryCache(Context context);

    void clearDiskCache(Context context);

    class Options {

        public static final int RES_NONE = -1;
        public int loadingResId = RES_NONE;//加载中的资源id
        public int loadErrorResId = RES_NONE;//加载失败的资源id

        public static Options defaultOptions() {
            return new Options(JConfig.IL_LOADING_RES, JConfig.IL_ERROR_RES);
        }

        public Options(int loadingResId, int loadErrorResId) {
            this.loadingResId = loadingResId;
            this.loadErrorResId = loadErrorResId;
        }
    }
}

//具体的实现
public class GlideLoader implements ILoader {
    @Override
    public void init(Context context) {

    }

    @Override
    public void loadNet(ImageView target, String url, Options options) {
        load(getRequestManager(target.getContext()).load(url), target, options);
    }

    @Override
    public void loadResource(ImageView target, int resId, Options options) {
        load(getRequestManager(target.getContext()).load(resId), target, options);
    }

    @Override
    public void loadAssets(ImageView target, String assetName, Options options) {
        load(getRequestManager(target.getContext()).load("file:///android_asset/" + assetName), target, options);
    }

    @Override
    public void loadFile(ImageView target, File file, Options options) {
        load(getRequestManager(target.getContext()).load(file), target, options);
    }

    @Override
    public void clearMemoryCache(Context context) {
        Glide.get(context).clearMemory();
    }

    @Override
    public void clearDiskCache(Context context) {
        Glide.get(context).clearDiskCache();
    }

    private RequestManager getRequestManager(Context context) {
        return Glide.with(context);
    }

    private void load(DrawableTypeRequest request, ImageView target, Options options) {
        if (options == null) options = Options.defaultOptions();

        if (options.loadingResId != Options.RES_NONE) {
            request.placeholder(options.loadingResId);
        }
        if (options.loadErrorResId != Options.RES_NONE) {
            request.error(options.loadErrorResId);
        }
        request.crossFade().into(target);
    }
}

//暴露给上层加载用的Factory
public class LoaderFactory {
    private static ILoader loader;

    public static ILoader getLoader() {
        if (loader == null) {
            synchronized (LoaderFactory.class) {
                if (loader == null) {
                    loader = new GlideLoader();
                }
            }
        }
        return loader;
    }
}

uihelper

uihelper是一个自定义View的依赖。所有不含业务型的View都应该放在这个module。

APP

APP就是上层的项目了,注意在androidStudio的module中除了可以传递依赖以外,还可以有自己独立的依赖,所以我们可以在APP中对业务类型的依赖添加在此处。如:后端SDK、友盟统计、微博分享等,只属于该项目中才会出现的就应当放在此处。

来我们再来看一眼构架图,是不是理清楚多了。

对比:

一把梭项目架构:

优点:

  • 快速开发
  • 无脑堆业务

缺点:

  • 维护成本高
  • 可阅读性差
  • 扩展性低

AndroidBasicLibs构架:

优点:

  • 可扩展
  • 可重用
  • 可维护
  • 更快的速度开发
  • 底层与业务抽离

缺点:

  • 前期搭建费时间
  • 依赖结构的层级略多

总结

项目的架构设计绝不是一成不变的套路,应该根据业务的类型去做模块拆解。并且实际上个人认为很多时候开发第一个版本的项目即便你考虑去搭建一套框架去写。但会因为进度和协同合作方面等因素导致理想状态不太一样。在线上稳定住后,就应该考虑去重构你的项目。见过一位同事的编码风格是边写业务边重构之前的代码。会导致与你协同开发的同事。翻阅过后就懵逼了。每次都要重读一遍。这不是一个好办法。最好与团队中成员达成一致的意见后开整。

本来这篇我并不想发表的,怕是自己学艺不精误导了新人。加上工作上的变动,近期略显仓促。希望各位看官多多给点评。觉得有收获就点个喜欢就是对我最大的鼓励。

最后本框架的地址是:https://github.com/wwah/AndroidBasicLibs


如何下次找到我?

相关文章

网友评论

  • 潇风寒月:dalao真是风趣幽默啊
  • 65a78f23ae39:进来只是看图
    香脆的大鸡排:@寻心_e8ae :joy: 要不要带走一套?不用给老婆打报告。
  • _大洲:老铁你这没毛病666
    香脆的大鸡排:@啊耀 :smiley: 加油,试试动态代理。文章中没讲。
    啊耀:非常感谢鸡排哥,项目准备重构啦
    香脆的大鸡排:@_大洲 :stuck_out_tongue_winking_eye:还不睡 等天亮吗
  • eb229fb197ff:收藏了
  • 饱醉豚我去年买了个表:不当段子手的程序猿不是好厨师!
    香脆的大鸡排:@_小马快跑_ 滴 滴滴 上车了
  • 麻油里:mark,项目准备重构,感谢鸡排君
  • 梦华芳秋:鸡排哥,你太流弊了!今天算是在简书中见识了什么叫无私的大神!
  • d73fe83636bb:彩笔鸡排,什么时候出书啊
  • ff66bb724d93:请问我集成的时候报错Error:(9, 1) A problem occurred evaluating root project 'tow'.> Could not find method compile() for arguments [com.github.wwah.AndroidBasicLibs:basekit:0.3.3] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.是怎么回事?定位在 compile 'com.github.wwah.AndroidBasicLibs:basekit:0.3.3' 错了。这个框架项目我已经单独下载下来了e
    香脆的大鸡排:应该放在app下的gradle中。而不是根目录
  • hfk:看完之后觉得
    你图挺多的嘛
    香脆的大鸡排:@hfk :joy: 老夫史藏1986典图。
  • Gaoer_Develop:老铁,你是不是被Android耽误了呀。 讲段子更靠谱啊
    香脆的大鸡排:@Gaoer_Develop 没毛病:smile_cat:
  • 容华谢后:相当不错
    香脆的大鸡排:@容华谢后 :smile:感谢点评
  • dcef74161308:集成出现:Error:Module 'com.github.wwah:AndroidBasicLibs:0.3.3' depends on one or more Android Libraries but is a jar
    香脆的大鸡排:@bin丶彬 非常抱歉 github上的dependencies写错了 正确的是这样的 compile 'com.github.wwah.AndroidBasicLibs:basekit:0.3.3' 已更正
  • 梦想编织者灬小楠:果然是老司机:clap: :clap: :clap:
    香脆的大鸡排:@梦想编织者灬小楠 :stuck_out_tongue_closed_eyes:
  • e031c3499981:我不关注框架,只关注段子,发现段子功力猛增啊:grin:
    香脆的大鸡排:@Jeasinlee 近朱者赤近墨者黑:joy:
  • 58492a66b402:收藏了
  • 爱言语论:还有点看不懂、、、、先点喜欢吧 估计过段时间就看懂了。。。
  • 尚酷:太棒了,加油,加油。
    香脆的大鸡排:@尚酷 :grin:加油加油 你最棒
  • 庞奇先生:本文风趣幽默,我看能打9分,少1分怕你骄傲:smile:
    香脆的大鸡排:@庞奇先生 感谢庞奇先生呀 哈哈 做你的小傲娇我也愿意。
  • Poseidon_Wang:楼主段子能力扎实啊 建议ui,base封装到common,模块化还是做第二层,最后才是业务模块,主要是考虑base的就那么几个文件,分包感觉浪费资源
    香脆的大鸡排:@Poseidon_Wang 不以文件大小个数来划分这个模块吧。这样划分的目的就是为了分层。不过你说的也没错,ui确实可以放到common但这个架构并非我一个人在维护,这个问题之前也有展开讨论过。我当时没能说服。感谢建议,我晚上再思考下:joy:
  • 在寻找雪见的景天:其实我一直没明白,为什么那么多人要用mvp,这个模式真的很好吗?因为普通的模式做一个页面就一个类,而mvp的话,就要三个类啊,多麻烦。
    香脆的大鸡排:@高富帅小小罗 当你的项目足够庞大的时候你就会发现它的优点
  • 随风青龙:不错~多谢分享~
  • im2016:段子手😌
  • 风舞尘起:小公司一个人做外包,有又压缩时间,套MVP什么的是不是太奢侈
    香脆的大鸡排:@风舞尘起 如果后期可能交给下一个人维护的话,还是尽量不给别人挖坑吧。毕竟今天留坑给别人,后天可能就踩别人给你挖的坑。多一行注释,让世界多一点关爱。:stuck_out_tongue_closed_eyes:
    风舞尘起:@香脆的大鸡排 捞一笔就走的项目
    香脆的大鸡排:@风舞尘起 看你要不要考虑后期维护,不考虑 一把梭可能更轻松 不过我觉得程序员还是尽量写可维护的项目更有助于提升自己
  • 梦里风吹过:刚搞Android没多久,基本全靠自己摸索,好像走了好多弯路啊,群已加
  • 小熊的璘居:看图来的,
  • de18268e1ec3:不是很看得懂,:joy:
  • 九号咖啡屋:我只关心吃瓜群众
    香脆的大鸡排:@_九叔 九叔好眼力,一定很厉害:+1:
  • 毹毹:感觉现在框架太多,这个很不错,感谢分享。
  • BigLong:dalao,做android多长时间了,总结很棒!
  • 闭上双眼你最牵挂n:dalao我不懂😂😂
    刚上大一,也不知道以后要干啥
    香脆的大鸡排:@闭上双眼你最牵挂n :joy: 大一加油 学校里最好了
  • Jboob:辛苦了,给我们讲了这么大的好笑话
    香脆的大鸡排:@SimpleIsBeauty :joy: 一起加油 进步
    SimpleIsBeauty:一看就是个段子高手,还是个优秀的IT gay。其实,我也正在筹划着搭建自己的框架,看了你的框架后受益匪浅。。
    香脆的大鸡排:@Jboob :relaxed: 您看得开心 有收获,就是对我的莫大支持。感谢
  • 三季人:思想很重要
    香脆的大鸡排:@三季人 你说的对 思路更重要
  • G米:感觉Android订阅更需要这篇文章。:heart_eyes:
  • 38f46aa3b520:不错,给力
    香脆的大鸡排:@38f46aa3b520 :kissing_heart:谢谢
  • 38ec9b2d871a:basekit里,mvp根据自身业务自己搭建么?
    香脆的大鸡排:@朱少1992 是的。你可以翻下源码看。不复杂。
  • 64c9559dcbc0:我也学android开发不就,谢谢啦
    香脆的大鸡排:@橙光小太阳 加油 小太阳
  • ChangQin:喜欢dalao的文采:smile:
    香脆的大鸡排:@ChangQin :flushed: 我也好喜欢da'lao 肿么回事
  • 7c27c8f7c129:虽然现在没搞安卓,可不知道以后了~
    感谢大佬分享(❁´ω`❁)
    香脆的大鸡排:@回眸落日 一起加油 一起成长!
    7c27c8f7c129: @香脆的大鸡排 我还是小白~只是目前做的东西杂~什么都在做,都不精~
    香脆的大鸡排:@回眸落日 虽然不做安卓了 我觉得在其他领域也能有借鉴之处!毕竟解耦的思想哪里都用得上呀 希望对你有帮助。:blush:
  • 滥情绝恋:不要太萌 哈哈哈哈哈哈
    香脆的大鸡排:@滥情绝恋 :joy: 感谢了 看官姥爷。一大早的上班去吗?
  • Poemrain:你是我关注的第一个人,不过我刚做安卓不久:joy:
    香脆的大鸡排:@MsYlalala 感谢关注,加油哦!:blush:
  • 汪简书:都是泪啊,不知几手的项目,2个UserInfo类,uid一个String、一个int。想重构,怕搂不住。看了文章,蛮有收货的 :)
    46b1089b2165:@lordkhan 同感
    lordkhan:能不能搂住不重要了。。。重要的是,改了无功;改了出了问题了,大罪。:joy:
    香脆的大鸡排:@然而喵星人早已看穿了一切 谢谢:grin:加油 少年

本文标题:少年,老夫带你撸一把Android项目框架,你可想学?

本文链接:https://www.haomeiwen.com/subject/bdjkwttx.html