我的 Android 开发实战经验总结

作者: ec95b5891948 | 来源:发表于2016-03-27 14:20 被阅读34796次

    以前一直想写一篇总结 Android 开发经验的文章,估计当时的我还达不到某种水平,所以思路跟不上,下笔又捉襟见肘。近日,思路较为明朗,于是重新操起键盘开始码字一番。先声明一下哈,本人不是大厂的程序猿。去年毕业前,就一直在当前创业小团队从事自己热爱的打码事业至今。下面总结是建立在我当前的技术水平和认知上写的,如有不同看法欢迎留下评论互相交流。

    1.理解抽象,封装变化

    目前 Android 平台上绝大部分开发都是用着 Java ,而跟 Java 这样一门面向对象的语言打交道,不免要触碰到 抽象封装 的概念。我身边接触过的一些开发者,有一部分还对这些概念停留在写一个抽象类、接口、或者一个方法(或抽象方法)。至于为什么,我不大清楚是他们表达不出来,还是不理解。下面我也不高谈阔论,直接举例子来解释我所理解的抽象。

    
    //Activity 间使用 Intent 传递数据的两种写法 下面均是伪代码形式,请忽略一些细节
    
    //写法一
    
    //SrcActivity 传递数据给 DestActivity
    Intent intent = new Intent(this,DestActivity.class);
    intent.putExtra("param", "clock");
    SrcActivity.startActivity(intent);
    
    //DestActivity 获取 SrcActivity 传递过来的数据
    String param = getIntent.getStringExtra("param");
    
    //写法二
    
    //SrcActivity 传递数据给 DestActivity
    Intent intent = new Intent(this,DestActivity.class);
    intent.putExtra(DestActivity.EXTRA_PARAM, "clock");
    SrcActivity.startActivity(intent);
    
    //DestActivity 获取 SrcActivity 传递过来的数据
    public final static String EXTRA_PARAM = "param";
    String param = getIntent.getStringExtra(EXTRA_PARAM);
    
    

    写法一,存在的问题是,如果 SrcActivity 和 DestActivity 哪个把 "param" 打错成 "para" 或者 "paran" ,传递的数据都无法成功接收到。而写法二则不会出现此类问题,因为两个 Activity 之间传递数据只需要知道 EXTRA_PARAM 变量即可,至于 EXTRA_PARAM 变量到底是 "param" 、 "para" 、"paran" 这一点并不需要关心,这就是一种对可能发生变化的地方进行抽象封装的体现,它所带来的好处就是降低手抖出错的概率,同时方便我们进行修改。

    基于抽象和封装,Java 本身很多 API 在设计上就有这样的体现,如 Collections 中的很多排序方法:

    Collections中的排序API

    这些方法都是基于 List 这个抽象的列表接口进行排序,至于这是一个用什么样的数据结构实现 List(ArrayList 还是 LinkedList),排序方法本身并不关心。看,是不是体现了 JDK 的设计人员的一种抽象编程的思维,因为 List 的具体实现可能有千万种,如果每一类 List 都要写一套排序方法,估计要哭瞎了。

    小结:把容易出现变化的部分进行抽象,就是对变化的一种封装。

    2.选好"车轮"

    一个项目的开发,我们不可能一切从0做起,如果真是这样,那同样要哭瞎。因此,善于借用已经做好的 "车轮" 非常重要,如:

    网络访问框架:okhttp、retrofit、android-async-http、volley
    图片加载框架:Android-Universal-Image-Loader、Glide、Fresco、Picasso
    缓存框架:DiskLruCache、 Robospice
    Json解析框架:Gson、Fastjson、Jackson
    事件总线:EventBus、Otto
    ORM框架:GreenDAO、Litepal
    还有其他各种各样开源的自定义控件、动画等。除了以上提到的开源框架,也包括一些不开源的SDK
    数据统计:友盟统计,百度统计...
    奔溃搜集:腾讯bugly、bugtags...
    云存储:七牛...
    即使通讯:环信、融云、阿里百川...
    推送:小米推送、腾讯推送、百度推送...
    安全加固:360加固宝、爱加密...

    一般情况下,我在选择是否引入一些开源框架主要基于以下几个因素:

    • 借助搜索引擎,如果网上有一大波资料,说明使用的人多,出了问题好找解决方案;当然,如果普遍出现差评,就可以直接Pass掉了
    • 看框架的作者或团队,如 JakeWharton大神Facebook团队等。大神和大公司出品的框架质量相对较高,可保证后续的维护和bug修复,不容易烂尾;
    • 关注开源项目的 commit密度,issue的提交、回复、关闭数量,watch数,start数,fork数等。像那种个基本不怎么提交代码、提issue又不怎么回复和修复的项目,最好就pass掉;

    针对不开源SDK的选择,也主要基于以下几点去考虑:

    • 借助搜索引擎,查明口碑;
    • 很多第三方SDK的官网首页都会告诉你,多少应用已经接入了此SDK,如果你看到有不少知名应用在上面,那这个SDK可以考虑尝试一下了。诸如,友盟官网:
    接入友盟的App
    • 查看SDK使用文档、它们的开发者社区、联系客服。好的SDK,使用文档肯定会详细指引你。出了问题,上开发者社区提问,他们的开发工程师也会社区上回答。实在不行只能联系客服,如果客服的态度都让你不爽,那就可以考虑换别家的SDK了。

    小结:选好 "车轮" ,事半功倍

    3.抽象依赖第三方框架

    为什么要抽象依赖于第三方框架呢?这里和第1点是互相照应的,就是降低我们对具体某个框架的依赖性,从而方便我们快速切换到不同的框架去。说到这里,你可能觉得很抽象,那我直接举一个加载图片的例子好了。

    假设你当前为项目引入一个加载图片的框架 —— Android-Universal-Image-Loader,最简单的做法就是加入相应的依赖包后,在任何需要加载图片的地方写上下面这样的代码段。

    
    ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance
    // Load image, decode it to Bitmap and display Bitmap in ImageView (or any other view 
    //  which implements ImageAware interface)
    imageLoader.displayImage(imageUri, imageView);
    // Load image, decode it to Bitmap and return Bitmap to callback
    imageLoader.loadImage(imageUri, new SimpleImageLoadingListener() {
        @Override
        public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
            // Do whatever you want with Bitmap
        }
    });
    
    

    这种做法最简单粗暴,但是带来的问题也最严重的。如果我有几十上百个地方都这么写,而在某一天,我听说Facebook出了个神器 Fresco,想要换掉 Android-Universal-Image-Loader ,你就会发现你需要丧心病狂的去改动几十上百个地方的代码,不仅工作量大,而且还容易出错。造成这样的原因,就在于项目和加载图片的框架之间形成了强耦合,而实际上,项目本身不应该知道我具体用了哪个加载图片的框架。

    正确的方式,应该是对框架做一个抽象的封装,以应对未来发生的变化,我直接举自己的开源项目 AndroidAlbum 中的一种封装做法好了。

    AndroidAlbum

    大致代码如下:

    //1、声明 ImageLoaderWrapper 接口,定义一些抽象的加载接口方法
    
    public interface ImageLoaderWrapper {
    
        /**
         * 显示 图片
         *
         * @param imageView 显示图片的ImageView
         * @param imageFile 图片文件
         * @param option    显示参数设置
         */
        public void displayImage(ImageView imageView, File imageFile, DisplayOption option);
    
        /**
         * 显示图片
         *
         * @param imageView 显示图片的ImageView
         * @param imageUrl  图片资源的URL
         * @param option    显示参数设置
         */
        public void displayImage(ImageView imageView, String imageUrl, DisplayOption option);
    
        /**
         * 图片加载参数
         */
        public static class DisplayOption {
            /**
             * 加载中的资源id
             */
            public int loadingResId;
            /**
             * 加载失败的资源id
             */
            public int loadErrorResId;
        }
    }
    
    // 2、将 UniversalAndroidImageLoader 封装成继承 ImageLoaderWrapper 接口的 UniversalAndroidImageLoader ,
    //这里代码有点长,感兴趣可以查看项目源码中的实现 https://github.com/D-clock/AndroidAlbum
    
    // 3、做一个ImageLoaderFactory
    
    public class ImageLoaderFactory {
    
        private static ImageLoaderWrapper sInstance;
    
        private ImageLoaderFactory() {
    
        }
    
        /**
         * 获取图片加载器
         *
         * @return
         */
        public static ImageLoaderWrapper getLoader() {
            if (sInstance == null) {
                synchronized (ImageLoaderFactory.class) {
                    if (sInstance == null) {
                        sInstance = new UniversalAndroidImageLoader();//<link>https://github.com/nostra13/Android-Universal-Image-Loader</link>
                    }
                }
            }
            return sInstance;
        }
    }
    
    //4、在所有需要加载图片的地方作如下的调用
    
    ImageLoaderWrapper loaderWrapper = ImageLoaderFactory.getLoader();
    ImageLoaderWrapper.DisplayOption displayOption = new ImageLoaderWrapper.DisplayOption();
    displayOption.loadingResId = R.mipmap.img_default;
    displayOption.loadErrorResId = R.mipmap.img_error;
    loaderWrapper.displayImage(imagview, url, displayOption);
    
    

    这样一来,切换框架所带来的代价就会变得很小,这就是不直接依赖于框架所带来的好处。当然,以上只是我比较简单的封装,你也可以进行更加细致的处理。

    小结:预留变更,不强耦合于第三方框架

    4.从 MVC 到 MVP

    说实话,在没接触 MVP 的架构之前,一直都是使用 MVC 的模式进行开发。而随着项目越来越大,Activity或者 Fragment里面代码越来越臃肿,看的时候想吐,改的时候想屎...这里撇开其他各种各样的架构不谈,只对比MVC 和 MVP 。

    MVC
    • View:布局的xml文件
    • Controller:Activity、Fragment、Dialog等
    • Model:相关的业务操作处理数据(如对数据库的操作、对网络等的操作都应该在Model层里)

    你会发现,如果 View 层只包含了xml文件,那我们 Android 项目中对 View 层可做操作的程度并不大,顶多就是用include复用一下布局。而 Activity 等简直就是一个奇葩,它虽然归属于 Controller 层,但实际上也干着 View 层的活(View 的初始化和相关操作都是在Activity中)。就是这种既是 View 又是 Controller 的结构,违背了单一责任原则,也使得 Activity 等出现了上述的臃肿问题。

    MVP
    • View:Activity、Fragment、Dialog、Adapter等,该层不包含任何业务逻辑
    • Presenter:中介,View 与 Model 不发生联系,都通过 Presenter 传递
    • Model:相关的业务操作处理数据(如对数据库的操作、对网络等的操作都应该在Model层里)

    相比 MVC,MVP在层次划分上更加清晰了,不会出现一人身兼二职的情况(有些单元测试的童鞋,会发现单元测试用例更好写了)。在此处你可以看到 View 和 Model 之间是互不知道对方存在的,这样应对变更的好处更大,很多时候都是 View 层的变化,而 Model 层发生的变化会相对较少,遵循 MVP 的结构开发后,改起来代码来也没那么蛋疼。
    这里也有地方需要注意,因为大量的交互操作集中在 Presenter 层中,所以需要把握好 Presenter 的粒度,一个 Activity 可以持有多个 View 和 Presenter,这样也就可以避开一个硕大的 View 和 Presenter 的问题了。

    推荐两个不错的 MVP 架构的项目给大家,还不明白的童鞋,可以自行体会一下其设计思想:

    https://github.com/pedrovgs/EffectiveAndroidUI
    https://github.com/antoniolg/androidmvp

    小结:去加以实践的理解 MVP 吧

    5.归档代码

    把一些常用的工具类或业务流程代码进行归类整理,加入自己的代码库(还没有自己个人代码仓库的童鞋可以考虑建一个了)。如加解密、拍照、裁剪图片、获取系统所有图片的路径、自定义的控件或动画以及其其他他一些常用的工具类等。归档有助于提高你的开发效率,在遇到新项目的时候随手即可引入使用。如果你想要更好的维护自己的代码库,不妨在不泄露公司机密的前提下,把这个私人代码库加上详细文档给开源出去。 这样能够吸引更多开发者来使用这些代码,也可以获得相应的bug反馈,以便于着手定位修复问题,增强这个仓库代码的稳定性。

    小结:合理归档代码,可以的话,加以开源维护

    6.性能优化

    关于性能优化的问题,大体都还是关注那几个方面:内存、CPU、耗电、卡顿、渲染、进程存活率等。对于这些地方的性能优化思路和分析方法,网络上已经有很多答案了,此处不做赘述。我只想说以下几点:

    • 不要过早的做性能优化,app先求能用再求好用。在需求都还没完成的时候把大量时间花在优化上是本末倒置的;
    • 优化要用实际数据说话,借助测试工具进行检测(如:网易的Emmagee、腾讯的GT和APT,科大讯飞的iTest,Google的Battery Historian)。毕竟老板问你比以前耗电降低多少,总不能回答降低了一些吧???
    • 任何不以减低性能损耗来做保活的手段,都是耍流氓。

    小结:合理优化,数据量化

    7.实践新技术

    Rxjava、React Native、Kotlin...开始兴起后,身边有很多开发者会跟风直上。学习新技术的精神是非常值得鼓励的,但没有经过一段时间实践观察,就擅自把新技术引入到商业项目中,则有失妥当。对于大公司的团队来说,会有专门团队或项目去研究这些新兴技术,以确定是否在自己的产品线开发中引入。但作为小公司,是不是就意味着没有实践尝试新技术的机会呢?并不是!个人有以下几点建议:

    • 借助搜索引擎。看此项技术坑多不多,口碑不错但是坑多的话,则说明当前技术不成熟,可以耐心等待更新;
    • 考虑学习成本。学习成本太大且不容易招到懂这方面的开发者的情况下,建议不要引入该技术;
    • 高仿一个项目并开源。如果你想引入 React Native 做商业开发,最好先高仿实现一个应用然后将其开源。这样一些对 RN 感兴趣的开发者会运行你的代码并反馈 bug 给你,有助于你知道一些新技术的坑,并寻找相应的解决方案,最终确定是否引入该技术;
    • 降低入门门槛。实践新技术的过程尽量加以详细的文档记录,这会有助于降低项目组其他同事对新技术的入门门槛,可以的话,也将学习文档开源,获得更多开发者对此份文档的反馈,也可纠正一些文档中的错误;
    • 结合实际业务。所有新技术的引入都要考虑是否符合当下的业务需求,我听过有些程序猿想引入新技术的原因是因为觉得这种技术很酷,网上说很好用,很啥啥啥...自己完全没弄过就人云亦云。有时候好无语,感觉在会用一些技术就像在炫技一样;

    小结:空谈误国,实干兴邦

    8.UML

    UML,驯服代码和了解项目结构的利器,本人也在学习和体验其好处的路途上。不管遇到大小项目,有了它,可以更好的理清一些脉络结构。对付旧的庞大项目代码,或者有志阅读某些开源项目代码的开发者,绝对是居家必备。

    小结:工欲善其事,必先利其器

    9.自造"车轮"

    前面 2 提到,项目不可能从0开始,是需要引入很多第三方框架的。这里并不与 2 互相违背,而是建议有想提高技术逼格的开发者,可以在空暇时间去编码实现一个框架。如果你对网络访问、图片加载方面很有研究见解,不妨把这些脑海里的思想落实成具体的代码。也许你会发现,你动手去实践的时候,考虑的东西会多得多,自己最终得到的也会更多。(特别建议那些看过很多开源代码,又至今未自己动手自撸一发的

    小结:不要停留在 api 调用的层面

    10.扩大技术圈

    有空又经济能力承受得起的时候,不妨去参加一些自己感兴趣的技术交流会。很多都有大牛上台演讲,听听人家的解决方案,拓宽一下自己看问题的思路,也可以多参加一些含金量高的线上活动。我有挺多开发者朋友,就是参加活动的时候认识的,有时候遇到一些技术问题,还会互相探讨交换一下解决思路。挺赞的!

    小结:拓宽技术视野

    11.写博客总结

    这个可能没什么好说的,大家看了标题就懂了。它最大的好处在于:

    • 系统化记录自己的解决方案;
    • 方便日后自己回顾;
    • 有问题也会有读者评论反馈,促进技术交流;
    • 增强自己书面表达能力;

    小结:认真总结,不断完善

    12.找个对象

    程序猿不要老是对着电脑,赶紧找个对象提升一下幸福感。据说幸福感高的程序猿,编码效率高,出bug几率小...

    总结:做个面向对象的程序员

    大概就想到这些了,以后要是再有想写的,另开新篇。絮絮叨叨写了这么多,最关键的还是自己要落实,千万不要听说过太多道理,却依然过不好这一生哈!!!!

    相关文章

      网友评论

      • 9a683f878b2f:我想知道不强耦与第三方框架的思路是不是写好一个接口 然后用一个类实现 把第三方框架的代码写进实现接口类里面,然后如果想换框架 或者框架不能用了 重新写实现接口的类 我这样理解对吗?有点不太懂
      • 0c45406e8da8:文章很棒。我们侠课岛正好在找远程录制课程视频或图文教程的朋友,我们会给到课程的需求大纲,每一节课程需要你来详细展开写一些代码举例和讲解清楚,对经验积累和创新能力有一定的要求。有兴趣联系我,微信:zhimadt
      • Todo2:组件化和插件化的开发里程总结
        https://www.jianshu.com/p/df2a6717009d
      • stefanJi:之前有这种写法:
        在 B Activity 写个静态的启动方法,数据传递和启动 Activity 都写在这里面,其他地方直接调这个静态方法传参数。
      • hw1212:我只想说,大神写的都是很好的,但是我们这种菜鸟实现起来都是很困难的!!!!!!
      • AWeiLoveAndroid:作为一个高级开发者 能少用三方的 就少用 即使要用也是有针对性的用 把不相干的去掉 然后结合项目加以改造再用:mask: 建议能自己实现的都自己写 :smile:
      • 牧头码尾:最后一个才是重点,然而我都没有做到,扎心啊
      • 6434a5e4326e:写的非常不错 ,很有道理
      • 吧主:A very good article, Keep up.
      • 让我来试试:关于热更新,可以参考这个
        http://www.jianshu.com/p/853dae4092d7
      • 小周爱吃瓜:最后条 臣妾办不到啊:joy:
      • 橙一升:最后一点才是关键
      • 9aef13d1a750:很实用
      • StandByMeSun:受启发了,谢谢
      • 9313a57c08ae:写的接地气
      • 5cc361bbf715:膜拜大神
      • 603788701970:\(≧▽≦)/
      • lynch0571:已收录《千赞专题》,欢迎关注:http://www.jianshu.com/collection/032a478c3dbf
      • 一梦付浮生:从最后一条做起正在奋斗中,我是一名即将毕业的android码农看到你的文章学到了不少东西,文章还通俗易懂赞一个,能加个QQ请教问题吗?😁😁
        一梦付浮生:@Flynn_0b6c 真刺激
        骑驴去看海:转行把,老兄。
      • 霁雪清虹:这id是不是群友啊
      • d104a19b33c0:写的很好,很有启发,谢谢!
      • SmartSean://1、首先在DestActivity里面
        public final static String EXTRA_PARAM = "param";
        String param = getIntent.getStringExtra(EXTRA_PARAM);

        public static void actionStart(Context context, String param){
        Intent intent=new Intent(context,DestActivity.class);
        intent.putExtra(EXTRA_PARAM,param);
        context.startActivity(intent);
        }

        //然后在SrcActivity中
        DestActivity.actionStart(context,param);

        个人觉得这种写法最标准,第一个活动需要考虑传递什么键,只要传值就行了
      • B_Crt:首先,也是最重要的,是你的粗现,让我看到了简书的魅力 有质量的腔调
        很认真的看了,沟通下我的见解
        1.抽象,封装
        这是java的基本特性,也是设计模式实现的最本质的手段
        那一部分人,看一下设计模式,我觉得会有豁然开朗之感的
        2.车轮
        这是我最欠缺的。从事rom开发,负责过日历,SystemUI,Keyguard,2017想写app,创造才更有意思。
        学习了
        3.MVP
        赞!写app必须掌握,这样写出来的代码才有质量,有层次。解耦了,代码也就更清晰了。维护代码的人,
        就不用天天追逻辑代码,一年两年还停留在逻辑的层次。
        给自己找个对象,幸福的程序员,写的最赞 :sunglasses:
        孤独的探索号: @ad38d5a7a478 我在也做rom,能加个qq或微信好友请教吗?😄
      • 性冷淡_:先Mark
      • 爱吃豆腐面:厉害厉害
      • 华清松:支持
      • 精致生活馆:写的非常到位,要是能写一篇完整的实战项目分析,那就牛逼了
      • fbca6cd85f85:牛🐮
      • 046348143e77:怒赞一发
      • 2e7e23281342:厉害👍
      • 我一定会学会:标记一下
      • 27c7534af0e9:看了一半,很实用,下班继续看
      • 浮夸初衷:写的听好,希望能看到更多你写的东西。受益匪浅呀!
      • chenyk:很不错,赞
      • BartonL:总结的很到位,赞
      • uncochen:以前不太理解面向接口开发的含义,维护的时候才深刻理解到了!有人跟我说,你的类怎么那么多,我反问他类多有什么不好吗
      • 淡月微云:真.面向对象😂
      • 合肥黑:干货
      • 笔绘丹心:第一点中方法二会不会性能不好,因为传完以后可能那个参数就再也用不到了,但是它用static修饰,就会一直存在内存中,而方法一参数使用完就回收了,至于手抖,可以不用手敲,直接到上一个界面把key复制 :grin:
        Jlanglang: @笔绘丹心 形参只是传值,又不是复制。你传接口,那么就相当于传个对象的地址值过去,如果这个对象回收了,对应的成员变量也就回收了。
        如果你说的定义string是在接口的方法里申明的话,那就是局部变量,走完就回收了
        今晚打老虎o: @Jlanglang 我在传参数的时候会写一个接口,然后在接口中定义一个比如string params=“abc”,这样会不会造成内存占用,在内存吃紧的时候是不是会先被回收掉?
        Jlanglang: @笔绘丹心 因为用的是常量,不会造成内存占用什么的,了解下常量池就知道了,如果你看了源码,会发现这种写法很常见
      • 小人物灌篮:做个面向对象的程序员,恩,很有道理,要努力了
      • 心田祭思:项目经理说全部不能用第三方的
        准备流浪的雪:喊你们项目经理自己做OCR,自己做地图啊
        张知卓见: @心田祭思 你说你上你不行
      • 张贤同学:深受启发。最近想看一些源码项目来学习学习,博主有空写一篇UML的博文啊~~
      • 014036a6c5f8:谢谢楼主分享自己的心得。看完后觉得很爽。
      • wo叫天然呆:你好LZ,读了你的这篇文章,受益匪浅,文章中提到一个UML,百度了下发现很好用,我正发愁如何展示代码结构,之前都是用word文档,已经残废,想请教下LZ用的是什么UML工具?是否可以导入java直接生成?
      • 5cf77dedd2b5:不错!!
      • ChienYi:謝謝 繼續加油
      • 小胖0_0:大饼哥写的真好!
      • 573a725e1add:棒棒棒棒棒
      • ad9814500d92:很棒,向你学习
      • 琼Kevin:赞赞赞
      • 向晚轻烟:谢谢分享
      • 244df12c906a:谢谢分享
      • 咖啡屋的蒲公英:谢谢分享,
      • baoerstudio:😜很好,一大早起来就读到好文章,赞👍一个!!
      • 09d8e043ac44:谢谢分享
      • 寒浪逐风:醍醐灌顶,谢谢!
      • Ken_mmm:最后一条是真的,也是最有效的,亲测有效 :clap:
      • thiagooo0:到头来,只做到了最后一点
        thiagooo0:@夕阳伴歌行 :joy: :joy:
        9aef13d1a750: @thiagooo0 看个代码也能被虐
        Jinbeen:@thiagooo0 不错,你成功了
      • 5620e5708a13:楼主好厉害,听君一席话,胜读十年书
      • thisfeng:非常棒!!潜行修炼
      • 囧_囧:面向对象……
      • 苏易川:不错,加油
      • 布鲁马:谁能告诉我简书中插入代码到底怎么查?
        1.```代码```
        2.两个Tab
        如上都不行啊,而且Tab必须敲4个空格吗?
      • HuDP:东西很不错:+1: 去年毕业 哈哈 又是一位工作时间不是很长 但水平出众的小伙伴 :+1:
      • 55a83370ca05:说实话,我就是一直停留在mvc模式下的人,而且以前没有归档代码库的习惯。。out了。。 :sob:
        ec95b5891948: @知秋一叶晨 哈哈,这个需要养成习惯,我自己也是这么过来的
      • 7231cc3e0f80:太棒了,我会一直持续关注博主这个话题
      • GYLEE:jiayou
      • 扑扑兔:写的不错,楼主记得一直更新这个话题
      • c83a487548eb:写得不错,干货
      • 张明云:好文章
      • fe52b911f5db:感谢分享,学习了
      • 2932ee5d780b:写得很好,纯干货。
      • System_out:先评论,你去年毕业?研究生毕业么?
        System_out:@D_clock 跪拜了……
        ec95b5891948: @System_out 😂本科毕业
      • _夜雨:抽象依赖三方框架,不进行强耦合,简直大赞。 :+1:
      • tmp_zhao:赞第12条!!!
      • f8575760b5e3:总结的很好,怒赞一发!
      • 191dcc8688dc:软件工程大三的学生,自己在学这一块,求大神给大腿啊
      • 她做了一个梦:总结的很好,学习了
      • SScience:做个面向对象的程序员~~ :joy:
      • Jasoncfpl:我怀疑第一条有问题,我记得好像如果引用一个activity的静态变量的话,当activity结束的时候,类好像不会被释放掉...
        Kevin_GTR:@_lynn 你就写个,常量工具类就好了,不是非得activity好吧
      • WilsonMing:干货,封装第三方库loadimage的方法中获取imageload对象的双锁检查单例有问题,对象需要加volatile
        uncochen:@D_clock爱吃葱花 如果严格说的话,DCL单例并不是真正意义的单例,可以通过反射和反序列化来创建新的对象
        ec95b5891948: @西门狂野 我去看看哈,有误立马修正
      • 淡漠IT:666666
      • LITTLEDREAM:刚刚才改完别人的一个项目的网络框架的问题。没有封装,直接暴露框架的接口,我也是跪了,这TM是做了几年Android开发的水准!!!!
        ec95b5891948: @LITTLEDREAM 哈哈,同样在这坑里出来的
      • wille_89:讲的很好,对我这种新人帮助很大

      本文标题:我的 Android 开发实战经验总结

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