Repository: ccj659/clean-project-architecture Branch: master Commit: 3b367d5ca83d Files: 182 Total size: 234.2 KB Directory structure: gitextract_uy9zytuq/ ├── .gitignore ├── .idea/ │ ├── gradle.xml │ ├── markdown-navigator/ │ │ └── profiles_settings.xml │ ├── markdown-navigator.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── README.md ├── README_MODULE.md ├── README_MVP.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── libs/ │ │ └── xUtils-2.6.14.jar │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── efly/ │ │ └── flyhelper/ │ │ └── ApplicationTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── efly/ │ │ │ └── flyhelper/ │ │ │ ├── AppApplication.java │ │ │ ├── MainActivity.java │ │ │ └── adapter/ │ │ │ └── FragmentAdapter.java │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── ic_dashboard_black_24dp.xml │ │ │ ├── ic_home_black_24dp.xml │ │ │ └── ic_notifications_black_24dp.xml │ │ ├── layout/ │ │ │ └── activity_bottom_navigation.xml │ │ ├── menu/ │ │ │ └── menu_bottom_navigation.xml │ │ └── values/ │ │ └── strings.xml │ └── test/ │ └── java/ │ └── com/ │ └── efly/ │ └── flyhelper/ │ └── ExampleUnitTest.java ├── base/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── ccj/ │ │ └── base/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── ccj/ │ │ │ └── base/ │ │ │ ├── AppManager.java │ │ │ ├── Constants.java │ │ │ ├── RouterConstants.java │ │ │ ├── adapter/ │ │ │ │ ├── CommonRcvAdapter.java │ │ │ │ ├── bean/ │ │ │ │ │ ├── AdapterBean.java │ │ │ │ │ └── AdapterGroupBean.java │ │ │ │ ├── item/ │ │ │ │ │ └── AdapterItem.java │ │ │ │ └── util/ │ │ │ │ ├── IAdapter.java │ │ │ │ └── ItemTypeUtil.java │ │ │ ├── api/ │ │ │ │ ├── APIService.java │ │ │ │ ├── RetrofitRequest.java │ │ │ │ └── VolleyUtils.java │ │ │ ├── base/ │ │ │ │ ├── BaseActivity.java │ │ │ │ ├── BaseApplication.java │ │ │ │ ├── BaseBean.java │ │ │ │ ├── BaseFragment.java │ │ │ │ ├── BaseModel.java │ │ │ │ ├── BasePresenter.java │ │ │ │ ├── BaseView.java │ │ │ │ └── Constants.java │ │ │ ├── bean/ │ │ │ │ ├── User.java │ │ │ │ └── UserDetail.java │ │ │ ├── utils/ │ │ │ │ ├── BitmapUtil.java │ │ │ │ ├── LruBitmapCache.java │ │ │ │ ├── SerializableUtil.java │ │ │ │ ├── SharedPreferenceUtil.java │ │ │ │ ├── TDeviceUtils.java │ │ │ │ ├── TLog.java │ │ │ │ ├── ToastUtil.java │ │ │ │ ├── eventbus/ │ │ │ │ │ └── EventUtils.java │ │ │ │ └── router/ │ │ │ │ ├── LoginModuleService.java │ │ │ │ ├── RounterInterceptor.java │ │ │ │ ├── RounterSerialization.java │ │ │ │ ├── RouterService.java │ │ │ │ └── RouterUtils.java │ │ │ └── view/ │ │ │ ├── SuperRecyclerView.java │ │ │ └── list/ │ │ │ ├── OnAppBarSkipListener.java │ │ │ └── OnLoadNextListener.java │ │ └── res/ │ │ ├── anim/ │ │ │ ├── anim_bottom_in.xml │ │ │ ├── anim_bottom_out.xml │ │ │ ├── dialog_enter.xml │ │ │ ├── dialog_exit.xml │ │ │ ├── footer_menu_slide_in.xml │ │ │ ├── footer_menu_slide_out.xml │ │ │ ├── in_from_bottom.xml │ │ │ ├── in_from_top.xml │ │ │ ├── out_to_bottom.xml │ │ │ └── out_to_top.xml │ │ ├── drawable/ │ │ │ ├── bg_toolbar.xml │ │ │ └── btn_ripple.xml │ │ ├── layout/ │ │ │ └── base_layout_tool_bar.xml │ │ └── values/ │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── com/ │ └── ccj/ │ └── base/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── module_home/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── ccj/ │ │ └── home/ │ │ ├── HomeFragment.java │ │ └── MainActivity.java │ ├── release/ │ │ └── AndroidManifest.xml │ └── res/ │ ├── drawable/ │ │ └── ic_launcher_background.xml │ ├── drawable-v24/ │ │ └── ic_launcher_foreground.xml │ ├── layout/ │ │ ├── activity_main.xml │ │ └── home_fragment_haojia_home.xml │ ├── mipmap-anydpi-v26/ │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ └── values/ │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── module_meizi/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── ccj/ │ │ └── meizi/ │ │ ├── adapter/ │ │ │ └── MeiziRcvAdapter.java │ │ ├── api/ │ │ │ ├── MeiziAPIServiceImp.java │ │ │ └── MeiziRetrofitImp.java │ │ ├── bean/ │ │ │ └── Meizhi.java │ │ ├── debug/ │ │ │ ├── ContainActivity.java │ │ │ └── MainActivity.java │ │ ├── holder/ │ │ │ └── MeiziItemHolder.java │ │ ├── ui/ │ │ │ ├── detail/ │ │ │ │ └── MeiziDetailActivity.java │ │ │ └── main/ │ │ │ ├── MeiZhiContract.java │ │ │ ├── MeiZhiFragment.java │ │ │ └── MeiZhiPresenter.java │ │ └── utils/ │ │ └── DateStringUtils.java │ ├── release/ │ │ └── AndroidManifest.xml │ └── res/ │ ├── drawable/ │ │ └── ic_launcher_background.xml │ ├── drawable-v24/ │ │ └── ic_launcher_foreground.xml │ ├── layout/ │ │ ├── activity_contain.xml │ │ ├── activity_main.xml │ │ ├── activity_meizi_detail.xml │ │ ├── fragment_meizi.xml │ │ └── item_meizi_item.xml │ ├── mipmap-anydpi-v26/ │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ └── values/ │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── module_user/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── ccj/ │ │ └── login/ │ │ ├── api/ │ │ │ ├── LoginAPIServiceImp.java │ │ │ └── LoginRetrofitImp.java │ │ ├── debug/ │ │ │ ├── LoginApplication.java │ │ │ └── MainActivity.java │ │ ├── service/ │ │ │ └── CheckLoginService.java │ │ └── ui/ │ │ ├── login/ │ │ │ ├── LoginActivity.java │ │ │ ├── LoginContract.java │ │ │ ├── LoginModel.java │ │ │ └── LoginPresenter.java │ │ ├── register/ │ │ │ └── RegisterActivity.java │ │ └── user/ │ │ └── UserFragment.java │ ├── release/ │ │ └── AndroidManifest.xml │ └── res/ │ ├── layout/ │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── login_fragment_register.xml │ │ └── user_fragment_user_home.xml │ └── values/ │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── module_video/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── ccj/ │ │ └── video/ │ │ ├── VideoFragment.java │ │ ├── debug/ │ │ │ ├── MainActivity.java │ │ │ └── VideoApplication.java │ │ ├── service/ │ │ │ └── VideoServiceImpl.java │ │ └── ui/ │ │ ├── TakePhotoActivity.java │ │ ├── TakePhotoContract.java │ │ ├── TakePhotoModel.java │ │ └── TakePhotoPresenter.java │ ├── release/ │ │ └── AndroidManifest.xml │ └── res/ │ ├── layout/ │ │ ├── activity_main.xml │ │ ├── activity_take_photo.xml │ │ └── video_fragment_video_home.xml │ └── values/ │ ├── colors.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/markdown-navigator/profiles_settings.xml ================================================ ================================================ FILE: .idea/markdown-navigator.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: README.md ================================================ # clean-project-architecture 更新 2018/2/1 组件化重构,修改方式为 主容器app+业务module模式. ---- 组件化项目框架,以及主流的项目架构(MVP+RxJava+Retrofit),以后会不断完善. - [MVP传送门](https://github.com/ccj659/clean-project-architecture/blob/master/README_MVP.md) - [组件化传送门](https://github.com/ccj659/clean-project-architecture/blob/master/README_MODULE.md) 参考界面: ![](http://i.imgur.com/EnBxczU.gif) ================================================ FILE: README_MODULE.md ================================================ # 客户端组件化探究 **项目地址** [https://github.com/ccj659/clean-project-architecture](https://github.com/ccj659/clean-project-architecture) ## 1.组件化的产生背景 随着业务的增多,迭代版本的增加, 模块化开发, 业务解耦, 业务独立进行测试,编译,运行,想想都惊喜~ 如果不想忍受超长的编译时间,不想忍受类之间的强耦合,受够了满屏的不相干的文件,那么..... 为了你的"代码洁癖",还有项目的未来, 组件化, 势在必行..... ## 2.普通开发模式的弊端 1、实际业务变化非常快,但是单一工程的业务模块耦合度太高,牵一发而动全身,每次改一个地方都很小心. 2、对工程所做的任何修改都必须要编译整个工程; 3、团队协同开发存在较多的冲突.不得不花费更多的时间去沟通和协调,并且在开发过程中,任何一位成员没办法专注于自己的功能点,影响开发效率; 4、不能灵活的对业务模块进行配置和组装; ## 3.什么是组件化 组件化是 编程思想"高内聚,低耦合"的一种体现, 是 业务独立化, 粒度最小化,可移植的功能模块. 1.页面上的每个 独立的 可视/可交互区域视为一个组件; 2.每个组件对应一个工程目录,组件所需的各种资源都在这个目录下就近维护; 3.每个组件相对独立,页面只不过是组件的容器,组件自由组合形成功能完整的界面; 4.当不需要某个组件,或者想要替换组件时,可以整个目录删除/替换。 ## 4.组件化会解决 目前项目中哪些问题? 1.急需解决 项目编译时间过长问题! 2.急需团队开发,解决提交代码 牵一发动全身的问题! 3.解决开发效率低下问题. 4.解决项目越来越臃肿,分层不明确,,难以维护问题. 5.减少 学习成本. ## 5.项目如何进行组件化? **在框架中的 项目组件化是这样的~** ![未命名文件 (6).png](http://upload-images.jianshu.io/upload_images/1848340-4011e008d6533cc2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) **反映在代码目录结构上,就是如下所示~** ![image.png](http://upload-images.jianshu.io/upload_images/1848340-1681c9f78b3f90c9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) **根据上述结构图,我对目前的项目做了如下调整.** 1.将项目的业务模块(主页,好价,好文,好物,个人中心,关注),作为独立的application module. 每一位业务的开发人员,只需要管理自己的module即可,避免直接或者间接修改他人代码导致的问题.而且,开发期间,自住选择必要中间件的依赖,自住开发风格(MVP,MVC,etc). 2.将中间组件(通用list_item组件,分享组件,推送组件,内置浏览器组件,通用view组件,以及通用跳转等等),作为library module进行选择性依赖. 3.将底层库(网络请求库,db库,File库,图片库等等),作为一个底层library库,独立进行版本控制,用@aar-v1.0.1 来引用,这样可以防止,他人的随意改动,提高版本切换之间的稳定性. ## 6.问题五连 #### Q1.如何将工程拆分成有机的整体,组件单独运行? 利用Android studio 自带的gradle 来管理. 我们可以使用grovxy脚本,来对 是否是组件状态进行切换. ![image.png](http://upload-images.jianshu.io/upload_images/1848340-46ab2ef7c01244f3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) #### Q2.如何分别执行独立运行,合并运行的代码? 当项目是APPlication时候,需要有category为LAUNCHER的入口activity. 当项目是lib的时候,不能存在入口activity.所以要分别建立两套配置. 还要注意,如果想要保持主题样式通用, 主app下,’theme’'ico','label'等等,在module中都不能存在. ![image.png](http://upload-images.jianshu.io/upload_images/1848340-a531f85dd9d77563.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) #### Q3.代码都分离了,如何进行带参跳转,相互调用? 首先,考虑到解耦, 可以把ARouter的所有跳转都进行封装.以后待项目成熟,我会用自己的Router. 另外,module内,可以用传统方式传递和用路由器传递都行. ![image.png](http://upload-images.jianshu.io/upload_images/1848340-8bf31d3c74218ad9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) #### Q4.是啊,分业务是好,但是代码合并后,如果避免存在资源名冲突? 1.第三方依赖的问题,依赖尽量放在base中.这样,gradle会自动去重. 2.资源名重复,在编码的时候 添加resourcePrefix "video"+"_",强制人员添加前缀(但是对drawable不支持,需用户自己增加前缀). 3.将资源统一放在一个module(比如Base中),但是编译会增加时间.(不太复合资源解耦 原则) #### Q5.代码那么多,把之前的都解耦了,如何避免后期继续耦合? 针对这个问题, 组件之间必须针对接口编程,杜绝面向实现编程. 另外, 我们在设计一个组件的时候, 要尽量用继承封装多态去解决问题. ## 7.如何实现 **1.在`gradle.properties` 增加一个变量** ``` # true代表模块开发,false代表合并到主app. #模式切换开关 isModule=false ``` **2.在每个业务module的`build.gradle`里面添加** ``` //根据isModule值进行切换 是否为lib或者app if (isModule.toBoolean()) { apply plugin: 'com.android.application' } else { apply plugin: 'com.android.library' } ``` **3.建立两个`AndroidManifest.xml`,进行切换** 大家都知道,当项目是APPlication时候,需要有category为`LAUNCHER`的入口activity. 而当项目是lib的时候,不能存在入口activity.所以要分别建立两套`AndroidManifest.xml `,还要注意,如果想要保持主题样式通用, 主app项目下的`theme`,'ico','label'等等,在module中都不能存在. 进行如下配置. ![image.png](http://upload-images.jianshu.io/upload_images/1848340-92b4a7213a9ad8d1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) **4.建立两个`AndroidManifest.xml`,进行切换** ``` sourceSets { main { if (isModule.toBoolean()) { manifest.srcFile 'src/main/AndroidManifest.xml' } else { manifest.srcFile 'src/main/release/AndroidManifest.xml' java { //release 时 debug 目录下文件不需要合并到主工程 exclude '**/debug/**' } } } } ``` **5.业务组件不需要混淆代码.** 一旦业务组件的代码被混淆,而这时候代码中又出现了bug,将很难根据日志找出导致bug的原因; **6.当ismodule开关为true时,每个module可独立运行.** ![image.png](http://upload-images.jianshu.io/upload_images/1848340-ddb00f60544243a3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 应用效果 ![compone1.gif](http://upload-images.jianshu.io/upload_images/1848340-a3af30d73a24fdaa.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ![compone2.gif](http://upload-images.jianshu.io/upload_images/1848340-06e31929d2927460.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) #总结 ## 优点 1.业务module独立,降低业务的学习成本. 2.模块解耦, 代码架构更加清晰,降低项目的维护难度. 3.随着项目的代码增加,单独编译,单独运行,明显减少代码编译时间. 4.适合于团队开发. ## 不足 1.switch中 case必须是 静态常亮,所以在引用 library中 的 资源(R.id.button) 时, 需要改为if else. 这个还需要找到解决方案... 2.模块间 数据交互,比较繁琐 3.路由器模块还不太成熟 4.实际开展过程,抽丝剥茧,过程会很痛苦. 5.还存在潜在问题. ## 后期计划 1.提高编译速度, 将 依赖 由 整个项目进行编译, 变为依赖 aar包, 减少编译时间. 2.搭建一个私有的maven仓库,将我们开发好的组件上传到这个私有的maven仓库上,然后内部开发人员就可以像引用三方库那样轻而易举的将组件引入到项目中 3. 完善复合项目的事件路由Router, ## 参考 [aar的使用](http://blog.csdn.net/zxw136511485/article/details/52777286) [http://blog.csdn.net/guiying712/article/details/55213884](http://blog.csdn.net/guiying712/article/details/55213884) [Android彻底组件化demo发布](https://www.jianshu.com/p/59822a7b2fad) ================================================ FILE: README_MVP.md ================================================ 2018/2/1 更新 1.完善组件化,宿主构建. 所以,mvp写法请参照`lib_base`和`module_meizi`. ps妹子图又更新了一波,😄 2016/11/1 更新 用gank.io的妹子数据 1.添加get请求. 2.完成妹子Meterial Design 风格的list界面. 参考界面: ![](http://i.imgur.com/EnBxczU.gif) ------- 2016/7/23 更新 热修复相关 csdn: http://blog.csdn.net/ccj659/article/details/52004522 简书: http://www.jianshu.com/p/2301d40dbb33 [热修复]--源码级分析以及项目实践 谢谢大家指正~ ---------- android架构篇 =================== mvp+rxjava+retrofit+eventBus =================== ---------- 高层不应该知道低层的细节,应该是面向抽象的编程。业务的实现交给实现的接口的类。高层只负责调用。 ---------- 首先,要介绍一下一个项目中好架构的好处:好的软件设计必须能够帮助开发者发展和扩充解决方案,保持代码清晰健壮,并且可扩展,易于维护,而不必每件事都重写代码。面对软件存在的问题,必须遵守SOLID原则(面向对象五大原则),不要过度工程化,尽可能降低框架中模块的依赖性。 ---------- 之前的一段时间,学习了一些新的技术,并把自己关注的技术整合了一下,是的,相似的技术有很多,自己择优选择,将它们的思想和技术应用到了自己的搭建的项目框架中. 限于自己能力水平有限,自己搭建的项目可能还有些不足,欢迎大家指正批评,让自己的想法和设计思想走向正轨.O(∩_∩)O谢谢~ 在框架中 ------------- *1.**项目整体框架: 利用google-clean-architecture的思想 来负责项目的整体MVP架构.*** - MVP是模型(Model)、视图(View)、主持人(Presenter)的缩写,分别代表项目中3个不同的模块。**我以登录为例子,进行说明.** ![这里写图片描述](http://img.blog.csdn.net/20160712153716629) 这里每个业务首先要有一个管理接口Contract,在这里面有三个接口来面向接口编程, (Model),(View),(Presenter). 将三个接口放在一起便于管理. ![这里写图片描述](http://img.blog.csdn.net/20160712153741743) ```java /** * 登录关联接口类 * * Created by ccj on 2016/7/7. */ public interface LoginContract { interface View extends BaseView { void showProgress(); void hideProgress(); void showError(String error); void navigateToMain(); void navigateToRegister(); } interface Presenter extends BasePresenter { void login(String username, String password); void onDestroy(); } interface Model{ void saveUserInfo(User user); void saveLoginState(Boolean isLogin); void saveRememberPass(User user); } } ``` - **模型(Model):实现 implements LoginContract.Model** 负责处理数据的加载或者存储,比如从网络或本地数据库获取数据等;**这里的login 涉及到的业务逻辑比较少请求网络 采用了rxjava +retroft+gsons 相当于 model层. 如果处理的出具多,就采用此model ,就像图片保存显示等等.** - **视图(View):采用接口的方式,让activity实现该接口,接口中有关于视图的方法,例如”initVIew()”,”showDialog()”,”hideDialog()”等等, 负责界面数据的展示,与用户进行交互;** ```java public class LoginActivity extends BaseActivity implements LoginContract.View { //省略bufferknife 注解 private LoginPresenter presenter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); ButterKnife.bind(this); presenter=new LoginPresenter(this); presenter.start();//初始化控制层 } //实现于view的方法 @Override public void navigateToMain() { Intent intent =new Intent(getBaseContext(),MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); } ``` - **主持人(Presenter):持有 view和model的对象,操作两者的方法.相当于协调者,是模型与视图之间的桥梁,将模型与视图分离开来,对view 和model 进行调度操作。** ```java /** * login的presenter层 进行对view 和 model 的控制, * Created by ccj on 2016/7/7. */ public class LoginPresenter implements LoginContract.Presenter { private LoginContract.View loginView; public LoginPresenter(LoginContract.View loginView) { this.loginView = loginView; } @Override public void login(String username, String password) { loginView.showProgress(); Observable userObservable = APIService.userLogin(username, password); userObservable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber() { @Override public void onCompleted() { loginView.hideProgress(); } @Override public void onError(Throwable e) { TLog.log(e.getMessage().toString()); loginView.hideProgress(); loginView.showError(e.getMessage().toString()); } @Override public void onNext(User getIpInfoResponse) { TLog.log(getIpInfoResponse.toString()); loginView.navigateToMain(); } }); } @Override public void start() { } ``` ***2.网络访问: 采用rxjava+retrofit+gson进行网络访问,并轻松的将json转为对象,结构清晰,使用方便.*** - **在APIService中初始化retrofit** ```java /** * 调用后台的接口,架构网络层采用Retroft+Rxjava+gson * Created by ccj on 2016/7/1. * */ public class APIService { private static final String TAG = "APIService"; public static final String URL_HOST ="http://123.234.82.23" ;//服务器端口 /** * 基础地址 * 初始化 retroft */ private static final Retrofit sRetrofit = new Retrofit.Builder() .baseUrl(URL_HOST) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 使用RxJava作为回调适配器 .build(); private static final RetrofitRequest apiManager = sRetrofit.create(RetrofitRequest.class); /** * 登录,返回,我这边用的是json格式的post,大家可以进行选择 * @param city * @return */ public static Observable userLogin(String format, String city) { HashMap hashMap =new HashMap<>(); hashMap.put("UserPhone", format); hashMap.put("UserPassWord", city); TLog.log(hashMap.toString()); Observable ss = apiManager.userLogin(hashMap); return ss; } /**********************仿照上面的方法,进行请求数据****************************/ ``` - **用retrofit访问 返回observable的对象** ```java public interface RetrofitRequest { boolean isTest=true; //是否在测试环境下 //发布之前更改 String BASE_URL_TEST = "/flyapptest/";//测试服务器 String BASE_URL_OFFICAL = "/flyapp/";//正式服务器 String BASE_URL = isTest?BASE_URL_TEST:BASE_URL_OFFICAL;//发布服务器 /** * 登录返回(json post) * @param body * @return */ @Headers( "Content-Type: application/json" ) @POST(BASE_URL+"Login.ashx/") Observable userLogin(@Body HashMap body); ``` ***3.异步处理: 采用rxjava响应式框架进行优雅的异步处理,简化代码逻辑,并且很好的解决内存泄漏 问题.(相关模块在TakePhoto业务中)*** ```java /** * rxjava 进行异步操作 eventBus进行时间传递 * @param data */ @Override public void savePhoto(final Intent data) { TLog.log("savePhoto", "data-->" + data.getData().toString()); Log.e("Tlog-->", "data-->" + data.getData().toString()); saveObservable = Observable.fromCallable(new Callable() { @Override public String call() throws Exception {//通知调用 并返回string return savePic(data);//此方法在io线程中调用 并返回 } }); saveSubscription = saveObservable .subscribeOn(Schedulers.io())//observable在调度中的IO线程中进行调度进行 .observeOn(AndroidSchedulers.mainThread())//在主线程中进行观察 .subscribe(new Observer() {//订阅观察者 @Override public void onCompleted() { Log.e("Tlog-->", "onCompleted-->"); } @Override public void onError(Throwable e) { Log.e("Tlog-->", "Throwable-->" + e.getMessage().toString()); EventBus.getDefault().post(new EventUtils.ObjectEvent(e.getMessage().toString())); } @Override public void onNext(String s) {//带参数的下一步,在此就是当 Log.e("Tlog-->", "s-->" + s); EventBus.getDefault().post(new EventUtils.ObjectEvent(bitmap)); } }); } ``` ***4.事件订阅: 采用EventBus作为事件总线,进行线程间,组件之间的通信.*** ```java /** * 事件总线 用于组件或线程通信,可替代回调,广播等 * Created by ccj on 2016/4/14. */ public class EventUtils { /** * object类型(即传统的所有类型,都可以强转进行传递事件) */ public static class ObjectEvent{ private Object object; public ObjectEvent(Object object) { // TODO Auto-generated constructor stub this.object = object; } public Object getMsg(){ return object; } } } ``` ***5.代码分包: 根据业务区分进行分包,便于对代码进行管理 .*** ![这里写图片描述](http://img.blog.csdn.net/20160712153822145) ***6. 工具类: TDeviceUtils设备状态的工具类,,SeriliazebleUtils 序列化工具类,SharepreferenceUtils保存工具类,*** 相关请参考代码 ***7.app栈管理: 基于baseActivity,很好的释放内存,管理内存.*** 相关请参考代码 --- #### 待后期完成 ***异常捕获(待完善)*** ***测试框架Espresso/JUnit/Mockito/Robolectric (待完善)*** --- ##总结 1.层次分明,各层级之间都不管对方如何实现,只关注结果; 2.在视图层(Presentation Layer)使用MVP架构,使原本臃肿的Activity(或Fragment)变得简单,其处理方法都交给了Presenter。 3.易于做测试,只要基于每个模块单独做好单元测试就能确保整体的稳定性。 4.易于快速迭代,基于代码的低耦合,只需在业务逻辑上增加接口,然后在相应的层级分别实现即可,丝毫不影响其他功能。 ##Blog-link [csdn博客,欢迎大家指正,评阅~谢谢O(∩_∩)O谢谢](http://blog.csdn.net/ccj659/article/details/51889713) ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' repositories { jcenter() } android { compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion defaultConfig { applicationId "com.efly.flyhelper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode rootProject.ext.versionCode versionName rootProject.ext.versionName multiDexEnabled false //arouter javaCompileOptions { annotationProcessorOptions { arguments = [moduleName: project.getName()] } } } buildTypes { release { //更改AndroidManifest.xml中预先定义好占位符信息 //manifestPlaceholders = [app_icon: "@drawable/icon"] // 不显示Log buildConfigField "boolean", "LEO_DEBUG", "false" //是否zip对齐 zipAlignEnabled true // 缩减resource文件 shrinkResources true //Proguard minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { //给applicationId添加后缀“.debug” applicationIdSuffix ".debug" //manifestPlaceholders = [app_icon: "@drawable/launch_beta"] buildConfigField "boolean", "LOG_DEBUG", "true" zipAlignEnabled false shrinkResources false minifyEnabled false debuggable true } } } dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') //arouter compile rootProject.ext.arouterApi annotationProcessor rootProject.ext.arouterCompiler //butterknife annotationProcessor rootProject.ext.butterknifeCompiler if (isModule.toBoolean()) { //当isDebug true,app得依赖基础库,保证app不报错 compile project(':base') } else { compile project(':module_home') compile project(':module_user') compile project(':module_video') compile project(':module_meizi') } } ================================================ FILE: app/proguard-rules.pro ================================================ -ignorewarnings # 忽略警告,避免打包时某些警告出现 -dontwarn -dontskipnonpubliclibraryclasses -dontskipnonpubliclibraryclassmembers -dontpreverify -verbose # 混淆时是否记录日志 #-printmapping topden.map -keepclassmembers class fqcn.of.javascript.interface.for.webview { public *; } -keep class * extends java.lang.annotation.Annotation { *; } -keep class * extends android.app.Application { *; } -keep public class * implements java.io.Serializable {*;} -keep public class * implements android.os.Parcelable {*;} -keepattributes Exceptions,InnerClasses,Signature -keepattributes SourceFile,LineNumberTable,EnclosingMethod # keep 泛型 -keepattributes *Annotation* -keepclassmembers class * { public (org.json.JSONObject); } -keepclassmembers class **.R$* { public static ; } -keep public class * extends android.app.Fragment -keep public class * extends android.app.Activity -keep public class * extends android.app.Application -keep public class * extends android.app.Service -keep public class * extends android.content.BroadcastReceiver -keep public class * extends android.content.ContentProvider -keep public class * extends android.app.backup.BackupAgentHelper -keep public class * extends android.preference.Preference -keepattributes *Annotation* -keep class android.support.v4.** { *; } -keep interface android.support.v4.app.** { *; } -keep public class * extends android.support.v4.** -keep public class * extends android.support.v13.** -keep class android.support.v13.** { *; } -keep class android.support.v7.** { *; } -dontwarn javax.annotation.** -keep class javax.annotation.** { *;} #########-------------------第三方 ----------------------------------############# #recoo -keep class com.dodola.** {*;} -keep class com.lody.legend.** {*;} #-dontwarn com.dodola.rocoo.** { *; } #-keep class com.dodola.rocoo.** { *; } # ButterKnife -keep class butterknife.** { *; } -dontwarn butterknife.internal.** -keep class **$$ViewBinder { *; } -keepclasseswithmembernames class * { @butterknife.* ; } -keepclasseswithmembernames class * { @butterknife.* ; } # EventBus -keepattributes *Annotation* -keepclassmembers class ** { @org.greenrobot.eventbus.Subscribe ; } -keep enum org.greenrobot.eventbus.ThreadMode { *; } # RxJava RxAndroid -dontwarn sun.misc.** -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { long producerIndex; long consumerIndex; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { rx.internal.util.atomic.LinkedQueueNode producerNode; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { rx.internal.util.atomic.LinkedQueueNode consumerNode; } # Retrofit -dontwarn retrofit2.** -keep class retrofit2.** { *; } -keepattributes Signature -keepattributes Exceptions # OkHttp3 -dontwarn com.squareup.okhttp3.** -keep class com.squareup.okhttp3.** { *;} -dontwarn okio.** # Gson #-keepattributes Signature-keepattributes *Annotation* -keep class sun.misc.Unsafe { *; } -keep class com.google.gson.stream.** { *; } # 使用Gson时需要配置Gson的解析对象及变量都不混淆。不然Gson会找不到变量。 # 将下面替换成自己的实体类 -keep class com.efly.flyhelper.bean.** { *; } # EventBus -keepattributes *Annotation* -keepclassmembers class ** { @org.greenrobot.eventbus.Subscribe ; } -keep enum org.greenrobot.eventbus.ThreadMode { *; } ================================================ FILE: app/src/androidTest/java/com/efly/flyhelper/ApplicationTest.java ================================================ package com.efly.flyhelper; import android.app.Application; import android.test.ApplicationTestCase; /** * Testing Fundamentals */ public class ApplicationTest extends ApplicationTestCase { public ApplicationTest() { super(Application.class); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/efly/flyhelper/AppApplication.java ================================================ package com.efly.flyhelper; import com.ccj.base.base.BaseApplication; /** * ARouter 这里必须在 这里初始化, 在 base里面初始化 无效,奇怪 * Created by chenchangjun on 17/8/10. */ public class AppApplication extends BaseApplication { @Override public void onCreate() { super.onCreate(); } } ================================================ FILE: app/src/main/java/com/efly/flyhelper/MainActivity.java ================================================ package com.efly.flyhelper; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.BottomNavigationView; import android.support.v4.app.Fragment; import android.support.v4.view.ViewPager; import android.view.MenuItem; import com.alibaba.android.arouter.launcher.ARouter; import com.ccj.base.base.BaseActivity; import com.ccj.base.RouterConstants; import com.efly.flyhelper.adapter.FragmentAdapter; import java.util.LinkedList; import java.util.List; public class MainActivity extends BaseActivity { private ViewPager mPager; private List mFragments = new LinkedList<>(); private FragmentAdapter mAdapter; private BottomNavigationView navigation; private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { int i = item.getItemId(); if (i == R.id.navigation_home) { mPager.setCurrentItem(0); return true; } else if (i == R.id.navigation_dashboard) { mPager.setCurrentItem(1); return true; } else if (i == R.id.navigation_notifications) { mPager.setCurrentItem(2); return true; } return false; } }; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_bottom_navigation); mPager = (ViewPager) findViewById(R.id.container_pager); navigation = (BottomNavigationView) findViewById(R.id.navigation); initViewPager(); navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener); } private void initViewPager() { Fragment home = (Fragment) ARouter.getInstance().build(RouterConstants.HOME_MUDULE_FRAGMENT_HOME_HOME).navigation(); Fragment meizi = (Fragment) ARouter.getInstance().build(RouterConstants.MEIZI_MUDULE_FRAGMENT_HOME_MEIZI).navigation(); Fragment user = (Fragment) ARouter.getInstance().build(RouterConstants.USER_MUDULE_FRAGMENT_HOME_USER).navigation(); mFragments.add(home); mFragments.add(meizi); mFragments.add(user); mAdapter = new FragmentAdapter(getSupportFragmentManager(), mFragments); mPager.setAdapter(mAdapter); } } ================================================ FILE: app/src/main/java/com/efly/flyhelper/adapter/FragmentAdapter.java ================================================ package com.efly.flyhelper.adapter; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import java.util.List; public class FragmentAdapter extends FragmentStatePagerAdapter { private List mFragments; public FragmentAdapter(FragmentManager fm, List mFragments) { super(fm); this.mFragments = mFragments; } @Override public Fragment getItem(int position) { return mFragments.get(position); } @Override public int getCount() { return mFragments != null ? mFragments.size() : 0; } @Override public int getItemPosition(Object object) { return android.support.v4.view.PagerAdapter.POSITION_NONE; } } ================================================ FILE: app/src/main/res/drawable/ic_dashboard_black_24dp.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_home_black_24dp.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_notifications_black_24dp.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_bottom_navigation.xml ================================================ ================================================ FILE: app/src/main/res/menu/menu_bottom_navigation.xml ================================================ ================================================ FILE: app/src/main/res/values/strings.xml ================================================ MVP和组件化 我的 妹子 首页 ================================================ FILE: app/src/test/java/com/efly/flyhelper/ExampleUnitTest.java ================================================ package com.efly.flyhelper; import org.junit.Test; import static org.junit.Assert.*; /** * To work on unit tests, switch the Test Artifact in the Build Variants view. */ public class ExampleUnitTest { @Test public void addition_isCorrect() throws Exception { assertEquals(4, 2 + 2); } } ================================================ FILE: base/.gitignore ================================================ /build ================================================ FILE: base/build.gradle ================================================ apply plugin: 'com.android.library' android { compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion defaultConfig { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode rootProject.ext.versionCode versionName rootProject.ext.versionName } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } repositories { flatDir { dirs 'aars' } } ext{ libSupportVersion = '25.3.1' } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile rootProject.ext.appcompatV7 compile rootProject.ext.constraintLayout //butterknife compile rootProject.ext.butterknife compile rootProject.ext.arouterApi compile rootProject.ext.eventbus /*view相关*/ compile "com.android.support:design:${libSupportVersion}" compile "com.android.support:recyclerview-v7:${libSupportVersion}" compile "com.android.support:cardview-v7:${libSupportVersion}" compile "com.android.support:support-v4:${libSupportVersion}" /*请求网络框架*/ //retrofit compile 'com.squareup.retrofit2:retrofit:2.0.2' compile 'com.squareup.okhttp3:logging-interceptor:3.1.2' compile 'com.squareup.retrofit2:converter-gson:2.0.2' compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2' //rxjava compile 'io.reactivex:rxandroid:1.1.0' compile 'io.reactivex:rxjava:1.1.0' compile 'com.google.code.gson:gson:2.4' compile 'com.mcxiaoke.volley:library:1.0.19' //图片请求 compile 'com.squareup.picasso:picasso:2.5.2' } ================================================ FILE: base/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/chenchangjun/Library/Android/sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile -keep public class com.alibaba.android.arouter.routes.**{*;} -keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;} # 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口 -keep interface * implements com.alibaba.android.arouter.facade.template.IProvider # 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现 -keep class * implements com.alibaba.android.arouter.facade.template.IProvider ================================================ FILE: base/src/androidTest/java/com/ccj/base/ExampleInstrumentedTest.java ================================================ package com.ccj.base; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.ccj.base.test", appContext.getPackageName()); } } ================================================ FILE: base/src/main/AndroidManifest.xml ================================================ ================================================ FILE: base/src/main/java/com/ccj/base/AppManager.java ================================================ package com.ccj.base; import android.app.Activity; import android.app.ActivityManager; import android.content.Context; import java.util.Stack; /** * activity堆栈式管理 * */ public class AppManager { private static Stack activityStack; private static AppManager instance; private AppManager() { } /** * 单一实例 */ public static AppManager getAppManager() { if (instance == null) { instance = new AppManager(); } if (activityStack == null) { activityStack = new Stack(); } return instance; } /** * 获取指定的Activity * * @author kymjs */ public static Activity getActivity(Class cls) { if (activityStack != null) for (Activity activity : activityStack) { if (activity.getClass().equals(cls)) { return activity; } } return null; } /** * 添加Activity到堆栈 */ public void addActivity(Activity activity) { activityStack.add(activity); } /** * 获取当前Activity(堆栈中最后一个压入的) */ public Activity currentActivity() { Activity activity = activityStack.lastElement(); return activity; } /** * 结束当前Activity(堆栈中最后一个压入的) */ public void finishActivity() { Activity activity = activityStack.lastElement(); finishActivity(activity); } /** * 结束指定的Activity */ public void finishActivity(Activity activity) { if (activity != null && activityStack.contains(activity)) { activityStack.remove(activity); activity.finish(); } } /** * 结束指定的Activity */ public void removeActivity(Activity activity) { if (activity != null && activityStack.contains(activity)) { activityStack.remove(activity); } } /** * 结束指定类名的Activity */ public void finishActivity(Class cls) { for (Activity activity : activityStack) { if (activity.getClass().equals(cls)) { finishActivity(activity); break; } } } /** * 结束所有Activity */ public void finishAllActivity() { for (int i = 0, size = activityStack.size(); i < size; i++) { if (null != activityStack.get(i)) { finishActivity(activityStack.get(i)); } } activityStack.clear(); } /** * 退出应用程序 */ public void AppExit(Context context) { try { finishAllActivity(); // System.exit(0); } catch (Exception e) { } // 获取packagemanager的实例 try { ActivityManager activityMgr = (ActivityManager) context .getSystemService(Context.ACTIVITY_SERVICE); activityMgr.killBackgroundProcesses(context.getPackageName()); activityStack = null; instance=null; System.exit(0); } catch (Exception e) { System.exit(0); } } } ================================================ FILE: base/src/main/java/com/ccj/base/Constants.java ================================================ package com.ccj.base; /** * Created by chenchangjun on 17/8/10. */ public class Constants { public static final String START_LOGIN_WITH_PARAMS = "START_LOGIN_WITH_PARAMS"; public static final int REQUEST_START_LOGIN = 0x00110; public static final int RESULT_FROM_LOGIN = 0x00011; public static final String PARAMS_RESULT_FROM_LOGIN = "PARAMS_RESULT_FROM_LOGIN"; public static final String PARAMS_REQUEST_FOR_DETAIL = "PARAMS_REQUEST_FOR_DETAIL"; } ================================================ FILE: base/src/main/java/com/ccj/base/RouterConstants.java ================================================ package com.ccj.base; /** * Created by chenchangjun on 17/8/9. */ public final class RouterConstants { /** * home module */ public static final String HOME_MODULE_NAME="/homemodule/"; public static final String HOME_MUDULE_FRAGMENT_HOME_HOME = HOME_MODULE_NAME+"FRAGMENT_HOME_HOME"; /** * user module */ public static final String USER_MODULE_NAME ="/loginmodule/"; public static final String USER_MUDULE_FRAGMENT_HOME_USER = USER_MODULE_NAME+"FRAGMENT_HOME_USER"; public static final String USER_MOUDLE_ACTIVITY = USER_MODULE_NAME +"LoginActivity"; public static final String USER_SERVICE_IMPL = USER_MODULE_NAME +"LoginServiceImpl"; public static final String USER_REGISTER_FRAGMENT = USER_MODULE_NAME +"/RegisterFragment"; /** * video module */ public static final String VIDEO_MODULE_NAME="/videomodule/"; public static final String VIDEO_MUDULE_FRAGMENT_HOME_VIDEO = VIDEO_MODULE_NAME+"FRAGMENT_HOME_VIDEO"; public static final String VIDEO_MUDULE_ACTIVITY = VIDEO_MODULE_NAME+"TakePhotoActivity"; public static final String VIDEO_SERVICE_IMPL = VIDEO_MODULE_NAME+"VideoServiceImpl"; /** * meizi module */ public static final String MEIZI_MODULE_NAME="/meizimodule/"; public static final String MEIZI_MUDULE_FRAGMENT_HOME_MEIZI = MEIZI_MODULE_NAME+"FRAGMENT_HOME_MEIZI"; public static final String MEIZI_MUDULE_ACTIVITY_MEIZI_DETAIL = MEIZI_MODULE_NAME+"ACTIVITY_MEIZI_DETAIL"; } ================================================ FILE: base/src/main/java/com/ccj/base/adapter/CommonRcvAdapter.java ================================================ package com.ccj.base.adapter; import android.app.Activity; import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.ViewGroup; import com.ccj.base.adapter.item.AdapterItem; import com.ccj.base.adapter.util.IAdapter; import com.ccj.base.adapter.util.ItemTypeUtil; import java.util.ArrayList; import java.util.List; /** * @author ccj * @date 2017/3/23 */ public abstract class CommonRcvAdapter extends RecyclerView.Adapter implements IAdapter { private List mDataList; private Object mType; private ItemTypeUtil mUtil; private int currentPos; public Activity mActivity; public CommonRcvAdapter(@Nullable List data, Activity mActivity) { if (data == null) { data = new ArrayList<>(); } mDataList = data; mUtil = new ItemTypeUtil(); this.mActivity = mActivity; } public T getItem(int position) { if (position < 0 || position > getItemCount() - 1) { return null; } return mDataList.get(position); } @Override public int getItemCount() { return mDataList == null ? 0 : mDataList.size(); } @Override public void setData(@NonNull List data) { mDataList = data; } @Override public List getData() { return mDataList; } @Override public long getItemId(int position) { return position; } /** * instead by{@link #getItemType(Object)} *

* 通过数据得到obj的类型的type * 然后,通过{@link ItemTypeUtil}来转换位int类型的type */ @Deprecated @Override public int getItemViewType(int position) { this.currentPos = position; mType = getItemType(mDataList.get(position)); return mUtil.getIntType(mType); } @Override public Object getItemType(T t) { return -1; // default } @Override public RcvAdapterItem onCreateViewHolder(ViewGroup parent, int viewType) { return new RcvAdapterItem(parent.getContext(), parent, createItem(mType)); } @Override public void onBindViewHolder(RcvAdapterItem holder, int position) { holder.item.handleData(getConvertedData(mDataList.get(position), mType), position); } @NonNull @Override public Object getConvertedData(T data, Object type) { return data; } @Override public int getCurrentPosition() { return currentPos; } /////////////////////////////////////////////////////////////////////////// // 内部用到的viewHold /////////////////////////////////////////////////////////////////////////// public static class RcvAdapterItem extends RecyclerView.ViewHolder { protected AdapterItem item; boolean isNew = true; // debug中才用到 RcvAdapterItem(Context context, ViewGroup parent, AdapterItem item) { super(LayoutInflater.from(context).inflate(item.getLayoutResId(), parent, false)); this.item = item; this.item.bindViews(itemView); this.item.setViews(); } } /////////////////////////////////////////////////////////////////////////// // For debug /////////////////////////////////////////////////////////////////////////// private void debug(RcvAdapterItem holder) { boolean debug = false; if (debug) { holder.itemView.setBackgroundColor(holder.isNew ? 0xffff0000 : 0xff00ff00); holder.isNew = false; } } } ================================================ FILE: base/src/main/java/com/ccj/base/adapter/bean/AdapterBean.java ================================================ package com.ccj.base.adapter.bean; import java.io.Serializable; /** * 数据bean * 为了不同viewItem 分别对应不同的bean,
* 避免不同item的字段都杂糅的同一个类中的情况,比如{ mobile中的 CommonRowsBean}
* 根据Holder建立一个新的bean, 自定义的bean要继承AdapterBean,在bean中添置所需字段,供holder调用,比如(mobile中 BrandRcvAdapter中的holder)
*

* Created by chenchangjun on 17/12/28. */ public abstract class AdapterBean implements Serializable { //唯一标示! public abstract void setCell_type(int cell_type); public abstract int getCell_type(); } ================================================ FILE: base/src/main/java/com/ccj/base/adapter/bean/AdapterGroupBean.java ================================================ package com.ccj.base.adapter.bean; import java.io.Serializable; import java.util.List; /** * 类似于 view和 viewgroup 的概念 这里是 viewgroup * Created by chenchangjun on 18/1/4. */ public abstract class AdapterGroupBean extends AdapterBean implements Serializable { //唯一标示! public abstract List getList(); public abstract String getTitle(); public abstract T getRedirect_data(); } ================================================ FILE: base/src/main/java/com/ccj/base/adapter/item/AdapterItem.java ================================================ package com.ccj.base.adapter.item; import android.support.annotation.LayoutRes; import android.view.View; /** * @author ccj * @date 2017/3/21 */ public interface AdapterItem { /** * @return item布局文件的layoutId */ @LayoutRes int getLayoutResId(); /** * 初始化views */ void bindViews(final View root); /** * 设置view的参数 * 设置监听等,只执行一次 */ void setViews(); /** * 根据数据来设置item的内部views * * @param t 数据list内部的model * @param position 当前adapter调用item的位置 */ void handleData(T t, int position); } ================================================ FILE: base/src/main/java/com/ccj/base/adapter/util/IAdapter.java ================================================ package com.ccj.base.adapter.util; import android.support.annotation.Keep; import android.support.annotation.NonNull; import com.ccj.base.adapter.item.AdapterItem; import java.util.List; /** * @author ccj * @date 2017/3/22 * 通用的adapter必须实现的接口,作为方法名统一的一个规范 */ public interface IAdapter { /** * @param data 设置数据源 */ void setData(@NonNull List data); List getData(); /** * @param t list中的一条数据 * @return 强烈建议返回string, int, bool类似的基础对象做type,不要返回data中的某个对象 */ Object getItemType(T t); /** * 当缓存中无法得到所需item时才会调用 * * @param type 通过{@link #getItemType(Object)}得到的type * @return 任意类型的 AdapterItem */ @Keep @NonNull AdapterItem createItem(Object type); /** * 如果放入item的最终数据和list中的每一条数据类型是不同的,可以通过此方法进行转换 * * @param data 从原始的list中取得得数据 * @param type item的类型 * @return 放入adapterItem的最终数据 */ @Keep @NonNull Object getConvertedData(T data, Object type); /** * 通知adapter更新当前页面的所有数据 */ void notifyDataSetChanged(); /** * 得到当前要渲染的最后一个item的position */ int getCurrentPosition(); } ================================================ FILE: base/src/main/java/com/ccj/base/adapter/util/ItemTypeUtil.java ================================================ package com.ccj.base.adapter.util; import android.support.annotation.VisibleForTesting; import java.util.HashMap; /** * @author ccj * @date 2017/3/22 */ @VisibleForTesting /*package*/ public class ItemTypeUtil { private HashMap typePool; public void setTypePool(HashMap typePool) { this.typePool = typePool; } /** * @param type item的类型 * @return 通过object类型的type来得到int类型的type */ public int getIntType(Object type) { if (typePool == null) { typePool = new HashMap<>(); } if (!typePool.containsKey(type)) { typePool.put(type, typePool.size()); } return typePool.get(type); } } ================================================ FILE: base/src/main/java/com/ccj/base/api/APIService.java ================================================ package com.ccj.base.api; import retrofit2.Retrofit; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; /** * 调用后台的接口,架构网络层采用Retroft+Rxjava+gson * Created by ccj on 2016/7/1. * */ public class APIService { private static final String TAG = "APIService"; //get请求 public static final String URL_GANK_IO = "http://gank.io";//gank.io 中的妹子API /** * 基础地址 * 初始化 retroft */ protected static final Retrofit sRetrofit = new Retrofit.Builder() .baseUrl( URL_GANK_IO ) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 使用RxJava作为回调适配器 .build(); // protected static final RetrofitRequest apiManager = sRetrofit.create(RetrofitRequest.class); /**********************仿照上面的方法,进行请求数据****************************/ } ================================================ FILE: base/src/main/java/com/ccj/base/api/RetrofitRequest.java ================================================ package com.ccj.base.api; /** * * Created by ccj on 2016/7/6. */ public interface RetrofitRequest { boolean isTest=false; //是否在测试环境下 //发布之前更改 String BASE_URL_TEST = "/flyapptest/";//测试服务器 String BASE_URL_OFFICAL = "/flyapp/";//正式服务器 String BASE_URL = isTest?BASE_URL_TEST:BASE_URL_OFFICAL;//发布服务器 } ================================================ FILE: base/src/main/java/com/ccj/base/api/VolleyUtils.java ================================================ package com.ccj.base.api; import android.content.Context; import android.text.TextUtils; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.Volley; import com.ccj.base.base.BaseApplication; import com.ccj.base.utils.LruBitmapCache; /** * volley留做备用 * Created by ccj on 2016/12/4. */ public class VolleyUtils { public static final String TAG = VolleyUtils.class.getSimpleName(); public static final Context context = BaseApplication.getInstance(); public static VolleyUtils volleyUtils; private RequestQueue requestQueue; private ImageLoader imageLoader; // The best way to maintain volley core objects and request queue is, making them global by creating a singleton class. public static VolleyUtils getInstance() { if (volleyUtils == null) { volleyUtils = new VolleyUtils(); } return volleyUtils; } //Default constructor private VolleyUtils() { getRequestQueue(); getImageLoader(); } public RequestQueue getRequestQueue() { if (requestQueue == null) { requestQueue = Volley.newRequestQueue(context); } return requestQueue; } public ImageLoader getImageLoader() { getRequestQueue(); if (imageLoader == null) { imageLoader = new ImageLoader(requestQueue, new LruBitmapCache()); } return imageLoader; } public void addToRequestQueue(Request req, String tag) { //Set default tag if empty req.setTag(TextUtils.isEmpty(tag) ? TAG : tag); getRequestQueue().add(req); } public void addToRequestQueue(Request req) { req.setTag(TAG); getRequestQueue().add(req); } public void cancelPendingRequests(Object tag) { if (requestQueue != null) { requestQueue.cancelAll(tag); } } } ================================================ FILE: base/src/main/java/com/ccj/base/base/BaseActivity.java ================================================ package com.ccj.base.base; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.View; import com.ccj.base.AppManager; import com.ccj.base.R; import java.util.List; /** * base 来进行 toolbar dialog 初始化,activity栈的添加,删除等 * Created by ccj on 2016/7/5. */ public class BaseActivity extends AppCompatActivity implements BaseView { private static final String TAG = "BaseActivity"; public T mPresenter; protected ProgressDialog progressDialog; protected Toolbar toolbar; protected Context mContext; private int fragmentIndex = 0; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); AppManager.getAppManager().addActivity(this); mContext = this; initDialog(); } public void initToolBar() { toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); toolbar.setBackgroundColor(getResources().getColor(R.color.tool_bar_white)); toolbar.setNavigationIcon(R.mipmap.back); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); } public Toolbar getToolbar() { return toolbar; } /** * 资源释放 */ @Override protected void onDestroy() { super.onDestroy(); if (mPresenter != null) mPresenter.onDestroy(); AppManager.getAppManager().finishActivity(this); } private void initDialog() { progressDialog = new ProgressDialog(this); progressDialog.setMessage(getResources().getString(R.string.show_loading_msg)); } public void showLoading() { progressDialog.show(); } public void dismissLoading() { progressDialog.dismiss(); } @Override public void initView() { } /** * 解决fragment onActivityResult不调用 * * @param requestCode * @param resultCode * @param data */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { FragmentManager fm = getSupportFragmentManager(); //if (index != 0) { if (fm.getFragments() == null) { Log.w(TAG, "Activity result fragment fragmentIndex out of range: 0x" + Integer.toHexString(requestCode)); return; } for (int i = 0; i frags = frag.getChildFragmentManager().getFragments(); if (frags != null) { for (Fragment f : frags) { if (f != null) handleResult(f, requestCode, resultCode, data); } } } } ================================================ FILE: base/src/main/java/com/ccj/base/base/BaseApplication.java ================================================ package com.ccj.base.base; import android.app.Application; import android.content.Context; import android.content.res.Resources; import android.view.Gravity; import android.widget.Toast; import com.alibaba.android.arouter.launcher.ARouter; import com.ccj.base.utils.SharedPreferenceUtil; /** * 获取上下文,Toast,以及各种初始化 * Created by Administrator on 2016/7/5. */ public class BaseApplication extends Application { private static String lastToast = ""; private static long lastToastTime; private static Context context; private static Resources resource; private static BaseApplication baseApplication; public static synchronized BaseApplication getInstance() { return baseApplication; } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); } @Override public void onCreate() { super.onCreate(); initARouter(); context = getBaseContext(); baseApplication = this; SharedPreferenceUtil.initSharedPreference(getApplicationContext()); } /** * ARouter 在每个模式下都需要,此时, * 由于每个module的application只有在module模式下才启用,所以可以这样设置--> * 可以将各module都继承BaseApplication */ private void initARouter() { //if (BuildConfig.DEBUG) { // 这两行必须写在init之前,否则这些配置在init过程中将无效 ARouter.openLog(); // 打印日志 ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) ARouter.printStackTrace(); // 打印日志的时候打印线程堆栈 // } ARouter.init(this); // 尽可能早,推荐在Application中初始化 } /** * 防抖动 弹窗 * * @param message * @param duration * @param icon * @param gravity */ public static void showToast(String message, int duration, int icon, int gravity) { Toast.makeText(getContext(),message,duration).show(); } public static Context getContext() { return context; } public static void setContext(Context context) { BaseApplication.context = context; } public static Resources getResource() { return resource; } public static void setResource(Resources resource) { BaseApplication.resource = resource; } public static void showShortToast(String message) { showToast(message, Toast.LENGTH_SHORT, 0, Gravity.BOTTOM); } public static void showLongToast(String message) { showToast(message, Toast.LENGTH_LONG, 0, Gravity.BOTTOM); } public static void showToast(String message) { showToast(message, Toast.LENGTH_LONG, 0, Gravity.BOTTOM); } } ================================================ FILE: base/src/main/java/com/ccj/base/base/BaseBean.java ================================================ package com.ccj.base.base; import java.io.Serializable; /** * Created by Administrator on 2016/7/6. */ public class BaseBean implements Serializable { } ================================================ FILE: base/src/main/java/com/ccj/base/base/BaseFragment.java ================================================ package com.ccj.base.base; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.util.DisplayMetrics; import android.view.LayoutInflater; /** * Created by Administrator on 2016/7/5. */ public abstract class BaseFragment extends Fragment implements BaseView { private static final String TAG="BaseFragment"; private LayoutInflater mInflater; private Dialog dialog; public T mPresenter; protected float mDensity; protected int mDensityDpi; protected int mWidth; protected int mAvatarSize; private Context mContext; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getScreen(); } public void showLoading() { if (dialog == null) { dialog = new ProgressDialog(getActivity()); } dialog.setTitle("正在加载..."); dialog.show(); } public void dismissLoading() { if (dialog != null) { dialog.dismiss(); } } private void getScreen() { DisplayMetrics dm = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm); mDensity = dm.density; mDensityDpi = dm.densityDpi; mWidth = dm.widthPixels; mAvatarSize = (int) (50 * mDensity); } /** * 解绑的时候 清除 */ @Override public void onDetach() { if (mPresenter != null) { mPresenter.onDestroy(); } super.onDetach(); } } ================================================ FILE: base/src/main/java/com/ccj/base/base/BaseModel.java ================================================ package com.ccj.base.base; /** * Created by Administrator on 2016/7/14. */ public interface BaseModel { /** * 默认的开始,在activity中初始化 */ void start(); /** * 在activity中的ondestoy 调用 在此方法中将资源至null, * 此处略嫌麻烦,但是如果把presenter层搞成抽象类,在里面添加成员变量和方法体, * 就有点失去了味道,所以还是选择了这种方式代替下列注释的部分. * */ void onDestroy(); } ================================================ FILE: base/src/main/java/com/ccj/base/base/BasePresenter.java ================================================ package com.ccj.base.base; /** * Created by Administrator on 2016/7/7. */ public interface BasePresenter { /** * 默认的开始,在activity中初始化 */ void start(); /** * 在activity中的ondestoy 调用 在此方法中将资源至null, * 此处略嫌麻烦,但是如果把presenter层搞成抽象类,在里面添加成员变量和方法体, * 就有点失去了味道,所以还是选择了这种方式代替下列注释的部分. * */ void onDestroy(); } /* public abstract class BasePresenter { public Context context; public E mModel; public T mView; public Source mSource = new Source(); public void setVM(T v, E m) { this.mView = v; this.mModel = m; this.onStart(); } public abstract void onStart(); public void onDestroy() { mSource.clear(); } }*/ ================================================ FILE: base/src/main/java/com/ccj/base/base/BaseView.java ================================================ package com.ccj.base.base; /** * Created by Administrator on 2016/7/6. */ public interface BaseView { void initView(); } ================================================ FILE: base/src/main/java/com/ccj/base/base/Constants.java ================================================ package com.ccj.base.base; /** * 静态常量 */ public class Constants { public static final int REQUST_FOR_LOGIN = 0x0001; public static final int RESULT_FOR_LOGIN = 0x0002; public static final int IMAGES_ACTIVITY_REQUEST_CODE = 300; } ================================================ FILE: base/src/main/java/com/ccj/base/bean/User.java ================================================ package com.ccj.base.bean; import com.ccj.base.base.BaseBean; import com.google.gson.Gson; import java.io.Serializable; public class User extends BaseBean implements Serializable { public static final long serialVersionUID = 2233933716943685981L; /** * code : 200 * msg : 登录成功 * result : {"ID":1,"Users_Organization":null,"Users_CellPhoneNum":"13800138000","Users_PassWord":"123456","Users_CorpName":"安监站","Users_IDCard":null,"Users_Kind":1,"Users_RegisterDate":"2016-06-14T11:20:02","Users_IsDel":false,"Users_Guid":null,"Users_PersonName":"刘某某","Users_CorpKind":null,"Users_AppID":109,"Users_AreaCode":"02","Users_TableName":null,"Users_PKCode":null,"Users_Alias":"10014AAC2F4FAD043E6BF71E30C34DEC","Users_JobName":"主管","Users_CorpKindName":"施工企业","Users_AreaName":"市南区","Users_Photo":null,"Users_OrderBy":null} */ public String code; public String msg; /** * ID : 1 * Users_Organization : null * Users_CellPhoneNum : 13800138000 * Users_PassWord : 123456 * Users_CorpName : 安监站 * Users_IDCard : null * Users_Kind : 1 * Users_RegisterDate : 2016-06-14T11:20:02 * Users_IsDel : false * Users_Guid : null * Users_PersonName : 刘某某 * Users_CorpKind : null * Users_AppID : 109 * Users_AreaCode : 02 * Users_TableName : null * Users_PKCode : null * Users_Alias : 10014AAC2F4FAD043E6BF71E30C34DEC * Users_JobName : 主管 * Users_CorpKindName : 施工企业 * Users_AreaName : 市南区 * Users_Photo : null * Users_OrderBy : null */ public ResultBean result; public static User objectFromData(String str) { return new Gson().fromJson(str, User.class); } public static class ResultBean { public int ID; public Object Users_Organization; public String Users_CellPhoneNum; public String Users_PassWord; public String Users_CorpName; public Object Users_IDCard; public int Users_Kind; public String Users_RegisterDate; public boolean Users_IsDel; public Object Users_Guid; public String Users_PersonName; public Object Users_CorpKind; public int Users_AppID; public String Users_AreaCode; public Object Users_TableName; public Object Users_PKCode; public String Users_Alias; public String Users_JobName; public String Users_CorpKindName; public String Users_AreaName; public Object Users_Photo; public Object Users_OrderBy; @Override public String toString() { return "ResultBean{" + "ID=" + ID + ", Users_Organization=" + Users_Organization + ", Users_CellPhoneNum='" + Users_CellPhoneNum + '\'' + ", Users_PassWord='" + Users_PassWord + '\'' + ", Users_CorpName='" + Users_CorpName + '\'' + ", Users_IDCard=" + Users_IDCard + ", Users_Kind=" + Users_Kind + ", Users_RegisterDate='" + Users_RegisterDate + '\'' + ", Users_IsDel=" + Users_IsDel + ", Users_Guid=" + Users_Guid + ", Users_PersonName='" + Users_PersonName + '\'' + ", Users_CorpKind=" + Users_CorpKind + ", Users_AppID=" + Users_AppID + ", Users_AreaCode='" + Users_AreaCode + '\'' + ", Users_TableName=" + Users_TableName + ", Users_PKCode=" + Users_PKCode + ", Users_Alias='" + Users_Alias + '\'' + ", Users_JobName='" + Users_JobName + '\'' + ", Users_CorpKindName='" + Users_CorpKindName + '\'' + ", Users_AreaName='" + Users_AreaName + '\'' + ", Users_Photo=" + Users_Photo + ", Users_OrderBy=" + Users_OrderBy + '}'; } public static ResultBean objectFromData(String str) { return new Gson().fromJson(str, ResultBean.class); } } @Override public String toString() { return "User{" + "code='" + code + '\'' + ", msg='" + msg + '\'' + ", result=" + result + '}'; } } ================================================ FILE: base/src/main/java/com/ccj/base/bean/UserDetail.java ================================================ package com.ccj.base.bean; /** * Created by Administrator on 2016/8/10. */ public class UserDetail { public String Users_PersonName; public String Users_CellPhoneNum; } ================================================ FILE: base/src/main/java/com/ccj/base/utils/BitmapUtil.java ================================================ package com.ccj.base.utils; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Environment; import android.util.Log; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; /** * Created by Administrator on 2016/3/3. */ public class BitmapUtil { public static final String LOG_TAG = "HelloCamera"; public static final int MEDIA_TYPE_IMAGE = 1; public static final int MEDIA_TYPE_VIDEO = 2; private static final String TAG = "BitmapUtil"; private static boolean isRun = true; private static Context context; private int i=0; private static BitmapUtil bitmapUtil; private static File tempFile; private boolean getAllPicIsRun = true; private int pic2=0,pic1=0; public BitmapUtil(Context context){ BitmapUtil.context =context; } /** * Create a file Uri for saving an image or video */ public static Uri getOutputMediaFileUri(int type, String name) { return Uri.fromFile(getOutputMediaFile(type, name)); } /** * Create a File for saving an image or video */ public static File getOutputMediaFile(int type, String name) { // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. File mediaStorageDir = null; try { // This location works best if you want the created images to be // shared // between applications and persist after your app has been // uninstalled. mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCamera"); } catch (Exception e) { e.printStackTrace(); Log.e(LOG_TAG, "Error in Creating mediaStorageDir: " + mediaStorageDir); } // Create the storage directory if it does not exist if (!mediaStorageDir.exists()) { if (!mediaStorageDir.mkdirs()) { // Log.e(LOG_TAG, "failed to create directory, check if you have the WRITE_EXTERNAL_STORAGE permission"); return null; } } // Create a media file name String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); File mediaFile = null; if (type == MEDIA_TYPE_IMAGE) { mediaFile = new File(mediaStorageDir.getPath() + File.separator + timeStamp + ".jpg"); Log.d(LOG_TAG, "sucessfully create mediafile"); } return mediaFile; } public static String getPhotoURL(String name) { File mediaStorageDir; mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCamera"); String fileName = getFile(mediaStorageDir.getPath(), name);//判断文件是否包含文件名 String file = mediaStorageDir.getPath() + File.separator + fileName; File file1 = new File(file); if (file1.isDirectory()) { return null; } return file; } public static String getFile(String dir, String contain) { if (contain==null){ return ""; } File file = new File(dir); File[] array = file.listFiles(); Log.e("listFiles-->", array.length + "!"+contain); for (int i = 0; i < array.length; i++) { if (array[i].isFile()) { // only take file name Log.e("getFile-->", array[i].getName()); if (array[i].getName().contains(contain)) { return array[i].getName(); } else if (array[i].isDirectory()) { getFile(array[i].getPath(), contain); } } } return ""; } public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; } public static File transMsgPicFile(File file, Bitmap bitmap) { tempFile=BitmapUtil.getOutputMediaFile(MEDIA_TYPE_IMAGE, "temp"); try { BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file)); bitmap.compress(Bitmap.CompressFormat.JPEG, 60, bos); bos.flush(); bos.close(); } catch (IOException e) { e.printStackTrace(); } return tempFile; } //纯粹的压缩图片 public static Bitmap comp(Bitmap image) { if (image==null){ return null; } ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos); if (baos.toByteArray().length / 1024 > 1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出 baos.reset();//重置baos即清空baos image.compress(Bitmap.CompressFormat.JPEG, 50, baos);//这里压缩50%,把压缩后的数据存放到baos中 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray()); BitmapFactory.Options newOpts = new BitmapFactory.Options(); //开始读入图片,此时把options.inJustDecodeBounds 设回true了 newOpts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts); newOpts.inJustDecodeBounds = false; int w = newOpts.outWidth; int h = newOpts.outHeight; //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为 float hh = 800f;//这里设置高度为800f float ww = 480f;//这里设置宽度为480f //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可 int be = 1;//be=1表示不缩放 if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放 be = (int) (newOpts.outWidth / ww); } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放 be = (int) (newOpts.outHeight / hh); } if (be <= 0) be = 1; newOpts.inSampleSize = be;//设置缩放比例 //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了 isBm = new ByteArrayInputStream(baos.toByteArray()); bitmap = BitmapFactory.decodeStream(isBm, null, newOpts); return compressImage(bitmap);//压缩好比例大小后再进行质量压缩 } //由path 得到压缩后的bitmap private Bitmap getimage(String srcPath) { BitmapFactory.Options newOpts = new BitmapFactory.Options(); //开始读入图片,此时把options.inJustDecodeBounds 设回true了 newOpts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);//此时返回bm为空 newOpts.inJustDecodeBounds = false; int w = newOpts.outWidth; int h = newOpts.outHeight; //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为 float hh = 800f;//这里设置高度为800f float ww = 480f;//这里设置宽度为480f //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可 int be = 1;//be=1表示不缩放 if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放 be = (int) (newOpts.outWidth / ww); } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放 be = (int) (newOpts.outHeight / hh); } if (be <= 0) be = 1; newOpts.inSampleSize = be;//设置缩放比例 //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了 bitmap = BitmapFactory.decodeFile(srcPath, newOpts); return compressImage(bitmap);//压缩好比例大小后再进行质量压缩 } //质量压缩图片 private static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 int options = 100; while (baos.toByteArray().length / 1024 > 100 && options>10) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩 baos.reset();//重置baos即清空baos image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中 options -= 10;//每次都减少10 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中 Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片 return bitmap; } } ================================================ FILE: base/src/main/java/com/ccj/base/utils/LruBitmapCache.java ================================================ package com.ccj.base.utils; import android.graphics.Bitmap; import android.support.v4.util.LruCache; import com.android.volley.toolbox.ImageLoader; /** * 图片缓存池 * Created by ccj on 2016/1/6. */ public class LruBitmapCache implements ImageLoader.ImageCache { private LruCache mCache; public LruBitmapCache() { int maxSize = 10 * 1024 * 1024; mCache = new LruCache(maxSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight(); } }; } @Override public Bitmap getBitmap(String url) { return mCache.get(url); } @Override public void putBitmap(String url, Bitmap bitmap) { mCache.put(url, bitmap); } } ================================================ FILE: base/src/main/java/com/ccj/base/utils/SerializableUtil.java ================================================ package com.ccj.base.utils; import android.util.Base64; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.List; /** * Created by ccj on 2015/12/7. * 将 对象转为 字符串,易于保存 上传等 * */ public class SerializableUtil { public static String objToStr(Object obj) throws IOException { if (obj == null) { return ""; } //实例化一个ByteArrayOutputStream对象,用来装载压缩后的字节文件 ByteArrayOutputStream baos = new ByteArrayOutputStream(); //然后将得到的字符数据装载到ObjectOutputStream ObjectOutputStream oos = new ObjectOutputStream(baos); //writeObject 方法负责写入特定类的对象的状态,以便相应的readObject可以还原它 oos.writeObject(obj); //最后,用Base64.encode将字节文件转换成Base64编码,并以String形式保存 String listString = new String(Base64.encode(baos.toByteArray(), Base64.DEFAULT)); //关闭oos oos.close(); return listString; } //将序列化的数据还原成Object public static Object strToObj(String str) throws IOException { byte[] mByte = Base64.decode(str.getBytes(), Base64.DEFAULT); ByteArrayInputStream bais = new ByteArrayInputStream(mByte); ObjectInputStream ois = new ObjectInputStream(bais); try { return ois.readObject(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } public static String listToString(List list) throws IOException { //实例化一个ByteArrayOutputStream对象,用来装载压缩后的字节文件 ByteArrayOutputStream baos = new ByteArrayOutputStream(); //然后将得到的字符数据装载到ObjectOutputStream ObjectOutputStream oos = new ObjectOutputStream(baos); //writeObject 方法负责写入特定类的对象的状态,以便相应的readObject可以还原它 oos.writeObject(list); //最后,用Base64.encode将字节文件转换成Base64编码,并以String形式保存 String listString = new String(Base64.encode(baos.toByteArray(), Base64.DEFAULT)); //关闭oos oos.close(); return listString; } public static List stringToList(String str) throws IOException { byte[] mByte = Base64.decode(str.getBytes(), Base64.DEFAULT); ByteArrayInputStream bais = new ByteArrayInputStream(mByte); ObjectInputStream ois = new ObjectInputStream(bais); List stringList = null; try { stringList = (List) ois.readObject(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return stringList; } } ================================================ FILE: base/src/main/java/com/ccj/base/utils/SharedPreferenceUtil.java ================================================ package com.ccj.base.utils; import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils; import android.util.Log; import com.ccj.base.base.BaseApplication; import com.ccj.base.bean.User; import java.io.IOException; import java.io.StreamCorruptedException; public class SharedPreferenceUtil { // 用户名key public final static String KEY_NAME = "KEY_NAME"; public final static String KEY_AUTO = "KEY_AUTO"; public final static String KEY_LOGIN = "KEY_LOGIN"; public final static String KEY_LEVEL = "KEY_LEVEL"; public final static String KEY_DELIVERY = "KEY_DELIVERY"; private static SharedPreferenceUtil spUtils; private static User spUser = null; private SharedPreferences sp; // /** * * 初始化,一般在应用启动之后就要初始化 * * @param context 此处的context要用application的全局上下文, * 避免static强类型一直持有activity的引用,造成内存泄露 */ public static synchronized void initSharedPreference(Context context) { if (spUtils == null) { spUtils = new SharedPreferenceUtil(context); } } /** * 获取唯一的instance * * @return */ public static synchronized SharedPreferenceUtil getInstance() { if (spUtils == null) { spUtils = new SharedPreferenceUtil(BaseApplication.getInstance()); } return spUtils; } public SharedPreferenceUtil(Context context) { sp = context.getSharedPreferences("SharedPreferenceUtil", Context.MODE_PRIVATE); } public SharedPreferences getSharedPref() { return sp; } public synchronized void putAutoLogin(Boolean AutoLogin) { SharedPreferences.Editor editor = sp.edit(); editor.putBoolean(KEY_AUTO, AutoLogin); editor.commit(); } public synchronized Boolean getAutoLogin() { Boolean flag = sp.getBoolean(KEY_AUTO, false); Log.i("flag", flag + "flag"); return flag; } public synchronized void setIsLogin(Boolean AutoLogin) { SharedPreferences.Editor editor = sp.edit(); editor.putBoolean(KEY_LOGIN, AutoLogin); editor.commit(); } public synchronized Boolean getIsLogin() { Boolean flag = sp.getBoolean(KEY_LOGIN, false); return flag; } public synchronized void putUser(User user) { SharedPreferences.Editor editor = sp.edit(); String str = ""; try { str = SerializableUtil.objToStr(user); } catch (IOException e) { e.printStackTrace(); } editor.putString(KEY_NAME, str); editor.commit(); spUser = user; } //记录用户名 public void setUsername(String username){ SharedPreferences.Editor editor = sp.edit(); editor.putString("pre_username",username); editor.apply(); } //读取用户名 public String getUsername(){ return sp.getString("pre_username",""); } public synchronized User getUser() { String str = sp.getString(SharedPreferenceUtil.KEY_NAME, ""); if (TextUtils.isEmpty(str)) { return null; } if (spUser == null) { spUser = new User(); //获取序列化的数据 try { Object obj = SerializableUtil.strToObj(str); if (obj != null) { spUser = (User) obj; Log.e("USER", "getuser" + spUser.toString()); } } catch (StreamCorruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return spUser; } public synchronized void DeleteUser() { SharedPreferences.Editor editor = sp.edit(); editor.putString(KEY_NAME, ""); editor.commit(); spUser = null; } // // public synchronized DeliveryMessage getDeliveryMessage() { // DeliveryMessage deliveryMessage = new DeliveryMessage(); // //获取序列化的数据 // String str = sp.getString(SharedPreferenceUtil.KEY_DELIVERY, ""); // if (TextUtils.isEmpty(str)) { // return null; // } // try { // Object obj = SerializableUtil.strToObj(str); // if (obj != null) { // deliveryMessage = (DeliveryMessage) obj; // } // } catch (StreamCorruptedException e) { // e.printStackTrace(); // } catch (IOException e) { // e.printStackTrace(); // } // // return deliveryMessage; // // } // // public synchronized void putDeliveryMessage(DeliveryMessage deliveryMessage) { // SharedPreferences.Editor editor = sp.edit(); // String str = ""; // try { // str = SerializableUtil.objToStr(deliveryMessage); // } catch (IOException e) { // e.printStackTrace(); // } // editor.putString(KEY_DELIVERY, str); // editor.commit(); // } } ================================================ FILE: base/src/main/java/com/ccj/base/utils/TDeviceUtils.java ================================================ package com.ccj.base.utils; import android.annotation.TargetApi; import android.app.Activity; import android.app.Dialog; import android.content.ActivityNotFoundException; import android.content.ClipboardManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.graphics.Point; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.PowerManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.Display; import android.view.View; import android.view.ViewConfiguration; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import com.ccj.base.R; import com.ccj.base.base.BaseApplication; import java.io.File; import java.lang.reflect.Field; import java.text.NumberFormat; import java.util.List; @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public class TDeviceUtils { // 手机网络类型 public static final int NETTYPE_WIFI = 0x01; public static final int NETTYPE_CMWAP = 0x02; public static final int NETTYPE_CMNET = 0x03; public static boolean GTE_HC; public static boolean GTE_ICS; public static boolean PRE_HC; private static Boolean _hasBigScreen = null; private static Boolean _hasCamera = null; private static Boolean _isTablet = null; private static Integer _loadFactor = null; private static int _pageSize = -1; public static float displayDensity = 0.0F; static { GTE_ICS = Build.VERSION.SDK_INT >= 14; GTE_HC = Build.VERSION.SDK_INT >= 11; PRE_HC = Build.VERSION.SDK_INT < 11; } public TDeviceUtils() { } public static float dpToPixel(float dp) { return dp * (getDisplayMetrics().densityDpi / 160F); } public static int getDefaultLoadFactor() { if (_loadFactor == null) { Integer integer = Integer.valueOf(0xf & BaseApplication.getContext() .getResources().getConfiguration().screenLayout); _loadFactor = integer; _loadFactor = Integer.valueOf(Math.max(integer.intValue(), 1)); } return _loadFactor.intValue(); } public static float getDensity() { if (displayDensity == 0.0) displayDensity = getDisplayMetrics().density; return displayDensity; } public static DisplayMetrics getDisplayMetrics() { DisplayMetrics displaymetrics = new DisplayMetrics(); ((WindowManager) BaseApplication.getContext().getSystemService( Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics( displaymetrics); return displaymetrics; } public static float getScreenHeight() { return getDisplayMetrics().heightPixels; } public static float getScreenWidth() { return getDisplayMetrics().widthPixels; } public static int[] getRealScreenSize(Activity activity) { int[] size = new int[2]; int screenWidth = 0, screenHeight = 0; WindowManager w = activity.getWindowManager(); Display d = w.getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); d.getMetrics(metrics); // since SDK_INT = 1; screenWidth = metrics.widthPixels; screenHeight = metrics.heightPixels; // includes window decorations (statusbar bar/menu bar) if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 17) try { screenWidth = (Integer) Display.class.getMethod("getRawWidth") .invoke(d); screenHeight = (Integer) Display.class .getMethod("getRawHeight").invoke(d); } catch (Exception ignored) { } // includes window decorations (statusbar bar/menu bar) if (Build.VERSION.SDK_INT >= 17) try { Point realSize = new Point(); Display.class.getMethod("getRealSize", Point.class).invoke(d, realSize); screenWidth = realSize.x; screenHeight = realSize.y; } catch (Exception ignored) { } size[0] = screenWidth; size[1] = screenHeight; return size; } public static int getStatusBarHeight() { Class c = null; Object obj = null; Field field = null; int x = 0; try { c = Class.forName("com.android.internal.R$dimen"); obj = c.newInstance(); field = c.getField("status_bar_height"); x = Integer.parseInt(field.get(obj).toString()); return BaseApplication.getContext().getResources() .getDimensionPixelSize(x); } catch (Exception e) { e.printStackTrace(); } return 0; } public static boolean hasBigScreen() { boolean flag = true; if (_hasBigScreen == null) { boolean flag1; if ((0xf & BaseApplication.getContext().getResources() .getConfiguration().screenLayout) >= 3) flag1 = flag; else flag1 = false; Boolean boolean1 = Boolean.valueOf(flag1); _hasBigScreen = boolean1; if (!boolean1.booleanValue()) { if (getDensity() <= 1.5F) flag = false; _hasBigScreen = Boolean.valueOf(flag); } } return _hasBigScreen.booleanValue(); } public static final boolean hasCamera() { if (_hasCamera == null) { PackageManager pckMgr = BaseApplication.getContext() .getPackageManager(); boolean flag = pckMgr .hasSystemFeature("android.hardware.camera.front"); boolean flag1 = pckMgr.hasSystemFeature("android.hardware.camera"); boolean flag2; flag2 = flag || flag1; _hasCamera = Boolean.valueOf(flag2); } return _hasCamera.booleanValue(); } public static boolean hasHardwareMenuKey(Context getContext) { boolean flag = false; if (PRE_HC) flag = true; else if (GTE_ICS) { flag = ViewConfiguration.get(getContext).hasPermanentMenuKey(); } else flag = false; return flag; } public static boolean hasInternet() { boolean flag; ConnectivityManager manager = (ConnectivityManager) BaseApplication.getContext() .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = manager.getActiveNetworkInfo(); flag = activeNetworkInfo != null; return flag; } public static boolean gotoGoogleMarket(Activity activity, String pck) { try { Intent intent = new Intent(); intent.setPackage("com.android.vending"); intent.setAction(Intent.ACTION_VIEW); intent.setData(Uri.parse("market://details?id=" + pck)); activity.startActivity(intent); return true; } catch (Exception e) { e.printStackTrace(); return false; } } public static boolean isPackageExist(String pckName) { try { PackageInfo pckInfo = BaseApplication.getContext().getPackageManager() .getPackageInfo(pckName, 0); if (pckInfo != null) return true; } catch (NameNotFoundException e) { TLog.error(e.getMessage()); } return false; } public static void hideAnimatedView(View view) { if (PRE_HC && view != null) view.setPadding(view.getWidth(), 0, 0, 0); } public static void hideSoftKeyboard(View view) { if (view == null) return; ((InputMethodManager) BaseApplication.getContext().getSystemService( Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow( view.getWindowToken(), 0); } public static boolean isLandscape() { boolean flag; flag = BaseApplication.getContext().getResources().getConfiguration().orientation == 2; return flag; } public static boolean isPortrait() { boolean flag = true; if (BaseApplication.getContext().getResources().getConfiguration().orientation != 1) flag = false; return flag; } public static boolean isTablet() { if (_isTablet == null) { boolean flag; flag = (0xf & BaseApplication.getContext().getResources() .getConfiguration().screenLayout) >= 3; _isTablet = Boolean.valueOf(flag); } return _isTablet.booleanValue(); } public static float pixelsToDp(float f) { return f / (getDisplayMetrics().densityDpi / 160F); } public static void showAnimatedView(View view) { if (PRE_HC && view != null) view.setPadding(0, 0, 0, 0); } public static void showSoftKeyboard(Dialog dialog) { dialog.getWindow().setSoftInputMode(4); } public static void showSoftKeyboard(View view) { ((InputMethodManager) BaseApplication.getContext().getSystemService( Context.INPUT_METHOD_SERVICE)).showSoftInput(view, InputMethodManager.SHOW_FORCED); } public static void toogleSoftKeyboard(View view) { ((InputMethodManager) BaseApplication.getContext().getSystemService( Context.INPUT_METHOD_SERVICE)).toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); } public static boolean isSdcardReady() { return Environment.MEDIA_MOUNTED.equals(Environment .getExternalStorageState()); } public static String getCurCountryLan() { return BaseApplication.getContext().getResources().getConfiguration().locale .getLanguage() + "-" + BaseApplication.getContext().getResources().getConfiguration().locale .getCountry(); } public static boolean isZhCN() { String lang = BaseApplication.getContext().getResources() .getConfiguration().locale.getCountry(); return lang.equalsIgnoreCase("CN"); } public static String percent(double p1, double p2) { String str; double p3 = p1 / p2; NumberFormat nf = NumberFormat.getPercentInstance(); nf.setMinimumFractionDigits(2); str = nf.format(p3); return str; } public static String percent2(double p1, double p2) { String str; double p3 = p1 / p2; NumberFormat nf = NumberFormat.getPercentInstance(); nf.setMinimumFractionDigits(0); str = nf.format(p3); return str; } public static void gotoMarket(Context getContext, String pck) { if (!isHaveMarket(getContext)) { BaseApplication.showToast("你手机中没有安装应用市场!"); return; } Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setData(Uri.parse("market://details?id=" + pck)); if (intent.resolveActivity(getContext.getPackageManager()) != null) { getContext.startActivity(intent); } } public static boolean isHaveMarket(Context getContext) { Intent intent = new Intent(); intent.setAction("android.intent.action.MAIN"); intent.addCategory("android.intent.category.APP_MARKET"); PackageManager pm = getContext.getPackageManager(); List infos = pm.queryIntentActivities(intent, 0); return infos.size() > 0; } public static void openAppInMarket(Context getContext) { if (getContext != null) { String pckName = getContext.getPackageName(); try { gotoMarket(getContext, pckName); } catch (Exception ex) { try { String otherMarketUri = "http://market.android.com/details?id=" + pckName; Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(otherMarketUri)); getContext.startActivity(intent); } catch (Exception e) { } } } } public static void setFullScreen(Activity activity) { WindowManager.LayoutParams params = activity.getWindow() .getAttributes(); params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; activity.getWindow().setAttributes(params); activity.getWindow().addFlags( WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); } public static void cancelFullScreen(Activity activity) { WindowManager.LayoutParams params = activity.getWindow() .getAttributes(); params.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN); activity.getWindow().setAttributes(params); activity.getWindow().clearFlags( WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); } public static PackageInfo getPackageInfo(String pckName) { try { return BaseApplication.getContext().getPackageManager() .getPackageInfo(pckName, 0); } catch (NameNotFoundException e) { TLog.error(e.getMessage()); } return null; } public static int getVersionCode() { int versionCode = 0; try { versionCode = BaseApplication .getContext() .getPackageManager() .getPackageInfo(BaseApplication.getContext().getPackageName(), 0).versionCode; } catch (NameNotFoundException ex) { versionCode = 0; } return versionCode; } public static int getVersionCode(String packageName) { int versionCode = 0; try { versionCode = BaseApplication.getContext().getPackageManager() .getPackageInfo(packageName, 0).versionCode; } catch (NameNotFoundException ex) { versionCode = 0; } return versionCode; } public static String getVersionName() { String name = ""; try { name = BaseApplication .getContext() .getPackageManager() .getPackageInfo(BaseApplication.getContext().getPackageName(), 0).versionName; } catch (NameNotFoundException ex) { name = ""; } return name; } public static boolean isScreenOn() { PowerManager pm = (PowerManager) BaseApplication.getContext() .getSystemService(Context.POWER_SERVICE); return pm.isScreenOn(); } public static void installAPK(Context getContext, File file) { if (file == null || !file.exists()) return; Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); getContext.startActivity(intent); } public static Intent getInstallApkIntent(File file) { Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); return intent; } public static void openDial(Context getContext, String number) { Uri uri = Uri.parse("tel:" + number); Intent it = new Intent(Intent.ACTION_DIAL, uri); getContext.startActivity(it); } public static void openSMS(Context getContext, String smsBody, String tel) { Uri uri = Uri.parse("smsto:" + tel); Intent it = new Intent(Intent.ACTION_SENDTO, uri); it.putExtra("sms_body", smsBody); getContext.startActivity(it); } public static void openDail(Context getContext) { Intent intent = new Intent(Intent.ACTION_DIAL); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getContext.startActivity(intent); } public static void openSendMsg(Context getContext) { Uri uri = Uri.parse("smsto:"); Intent intent = new Intent(Intent.ACTION_SENDTO, uri); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getContext.startActivity(intent); } public static void openCamera(Context getContext) { Intent intent = new Intent(); // 调用照相机 intent.setAction("android.media.action.STILL_IMAGE_CAMERA"); intent.setFlags(0x34c40000); getContext.startActivity(intent); } public static String getIMEI() { TelephonyManager tel = (TelephonyManager) BaseApplication.getContext() .getSystemService(Context.TELEPHONY_SERVICE); return tel.getDeviceId(); } public static String getPhoneType() { return Build.MODEL; } public static void openApp(Context getContext, String packageName) { Intent mainIntent = BaseApplication.getContext().getPackageManager() .getLaunchIntentForPackage(packageName); if (mainIntent == null) { mainIntent = new Intent(packageName); } else { TLog.log("Action:" + mainIntent.getAction()); } getContext.startActivity(mainIntent); } public static boolean openAppActivity(Context getContext, String packageName, String activityName) { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); ComponentName cn = new ComponentName(packageName, activityName); intent.setComponent(cn); try { getContext.startActivity(intent); return true; } catch (Exception e) { return false; } } public static boolean isWifiOpen() { boolean isWifiConnect = false; ConnectivityManager cm = (ConnectivityManager) BaseApplication .getContext().getSystemService(Context.CONNECTIVITY_SERVICE); // check the networkInfos numbers NetworkInfo[] networkInfos = cm.getAllNetworkInfo(); for (int i = 0; i < networkInfos.length; i++) { if (networkInfos[i].getState() == NetworkInfo.State.CONNECTED) { if (networkInfos[i].getType() == ConnectivityManager.TYPE_MOBILE) { isWifiConnect = false; } if (networkInfos[i].getType() == ConnectivityManager.TYPE_WIFI) { isWifiConnect = true; } } } return isWifiConnect; } public static void uninstallApk(Context getContext, String packageName) { if (isPackageExist(packageName)) { Uri packageURI = Uri.parse("package:" + packageName); Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI); getContext.startActivity(uninstallIntent); } } @SuppressWarnings("deprecation") public static void copyTextToBoard(String string) { if (TextUtils.isEmpty(string)) return; ClipboardManager clip = (ClipboardManager) BaseApplication.getContext() .getSystemService(Context.CLIPBOARD_SERVICE); clip.setText(string); BaseApplication.showToast("复制成功"); } /** * 发送邮件 * * @param getContext * @param subject 主题 * @param content 内容 * @param emails 邮件地址 */ public static void sendEmail(Context getContext, String subject, String content, String... emails) { try { Intent intent = new Intent(Intent.ACTION_SEND); // 模拟器 // intent.setType("text/plain"); intent.setType("message/rfc822"); // 真机 intent.putExtra(Intent.EXTRA_EMAIL, emails); intent.putExtra(Intent.EXTRA_SUBJECT, subject); intent.putExtra(Intent.EXTRA_TEXT, content); getContext.startActivity(intent); } catch (ActivityNotFoundException e) { e.printStackTrace(); } } public static int getStatuBarHeight() { Class c = null; Object obj = null; Field field = null; int x = 0, sbar = 38;// 默认为38,貌似大部分是这样的 try { c = Class.forName("com.android.internal.R$dimen"); obj = c.newInstance(); field = c.getField("status_bar_height"); x = Integer.parseInt(field.get(obj).toString()); sbar = BaseApplication.getContext().getResources() .getDimensionPixelSize(x); } catch (Exception e1) { e1.printStackTrace(); } return sbar; } public static int getActionBarHeight(Context getContext) { int actionBarHeight = 0; TypedValue tv = new TypedValue(); if (getContext.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getContext.getResources().getDisplayMetrics()); if (actionBarHeight == 0 && getContext.getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) { actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getContext.getResources().getDisplayMetrics()); } return actionBarHeight; } public static boolean hasStatusBar(Activity activity) { WindowManager.LayoutParams attrs = activity.getWindow().getAttributes(); return (attrs.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != WindowManager.LayoutParams.FLAG_FULLSCREEN; } /** * 调用系统安装了的应用分享 * * @param getContext * @param title * @param url */ public static void showSystemShareOption(Activity getContext, final String title, final String url) { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_SUBJECT, "分享:" + title); intent.putExtra(Intent.EXTRA_TEXT, title + " " + url); getContext.startActivity(Intent.createChooser(intent, "选择分享")); } /** * 获取当前网络类型 * * @return 0:没有网络 1:WIFI网络 2:WAP网络 3:NET网络 */ public static int getNetworkType() { int netType = 0; ConnectivityManager connectivityManager = (ConnectivityManager) BaseApplication .getInstance().getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); if (networkInfo == null) { return netType; } int nType = networkInfo.getType(); if (nType == ConnectivityManager.TYPE_MOBILE) { String extraInfo = networkInfo.getExtraInfo(); if (!TextUtils.isEmpty(extraInfo)) { if (extraInfo.toLowerCase().equals("cmnet")) { netType = NETTYPE_CMNET; } else { netType = NETTYPE_CMWAP; } } } else if (nType == ConnectivityManager.TYPE_WIFI) { netType = NETTYPE_WIFI; } return netType; } } ================================================ FILE: base/src/main/java/com/ccj/base/utils/TLog.java ================================================ package com.ccj.base.utils; import android.util.Log; public class TLog { public static final String LOG_TAG = "TLog-->"; public static boolean DEBUG = true;//是否处在debug public TLog() { } public static final void analytics(String log) { if (DEBUG) Log.d(LOG_TAG, log); } public static final void error(String log) { if (DEBUG) Log.e(LOG_TAG, "" + log); } public static final void log(String log) { if (DEBUG) Log.e(LOG_TAG, log); } public static final void log(String tag, String log) { if (DEBUG) Log.e(tag, log); } public static final void logI(String log) { if (DEBUG) Log.i(LOG_TAG, log); } public static final void warn(String log) { if (DEBUG) Log.w(LOG_TAG, log); } } ================================================ FILE: base/src/main/java/com/ccj/base/utils/ToastUtil.java ================================================ package com.ccj.base.utils; import com.ccj.base.base.BaseApplication; public class ToastUtil { public static void show( String text) { android.widget.Toast.makeText(BaseApplication.getContext(), text, android.widget.Toast.LENGTH_SHORT).show(); } } ================================================ FILE: base/src/main/java/com/ccj/base/utils/eventbus/EventUtils.java ================================================ package com.ccj.base.utils.eventbus; /** * 事件总线 用于组件或线程通信,可替代回调,广播等 * Created by ccj on 2016/4/14. */ public class EventUtils { /** * 传递String类型的event * */ public static class StringEvent{ private String mMsg; public StringEvent(String msg) { // TODO Auto-generated constructor stub this.mMsg = msg; } public String getMsg(){ return mMsg; } } public static class intEvent{ private int mMsg; public intEvent(int msg) { // TODO Auto-generated constructor stub this.mMsg = msg; } public int getMsg(){ return mMsg; } } /** * object类型(即传统的所有类型,都可以强转进行传递事件) * */ public static class ObjectEvent{ private Object object; public ObjectEvent(Object object) { // TODO Auto-generated constructor stub this.object = object; } public Object getMsg(){ return object; } } } ================================================ FILE: base/src/main/java/com/ccj/base/utils/router/LoginModuleService.java ================================================ package com.ccj.base.utils.router; import com.alibaba.android.arouter.facade.template.IProvider; /** * 示例:子模块间调用方法 * Created by chenchangjun on 17/8/14. */ public interface LoginModuleService extends IProvider { boolean checkLoginState(); } ================================================ FILE: base/src/main/java/com/ccj/base/utils/router/RounterInterceptor.java ================================================ package com.ccj.base.utils.router; import android.content.Context; import com.alibaba.android.arouter.facade.Postcard; import com.alibaba.android.arouter.facade.annotation.Interceptor; import com.alibaba.android.arouter.facade.callback.InterceptorCallback; import com.alibaba.android.arouter.facade.template.IInterceptor; /** * // 比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查 // 拦截器会在跳转之间执行,多个拦截器会按优先级顺序依次执行 * Created by chenchangjun on 17/8/9. */ @Interceptor(priority = 8, name = "测试用拦截器") public class RounterInterceptor implements IInterceptor { @Override public void process(Postcard postcard, InterceptorCallback callback) { callback.onContinue(postcard); // 处理完成,交还控制权 // callback.onInterrupt(new RuntimeException("我觉得有点异常")); // 觉得有问题,中断路由流程 // 以上两种至少需要调用其中一种,否则不会继续路由 } @Override public void init(Context context) { // 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次 } } ================================================ FILE: base/src/main/java/com/ccj/base/utils/router/RounterSerialization.java ================================================ package com.ccj.base.utils.router; import android.content.Context; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.facade.service.SerializationService; import java.lang.reflect.Type; /** * Created by chenchangjun on 17/8/9. */ @Route(path = "/router/RounterSerialization") public class RounterSerialization implements SerializationService { @Override public T json2Object(String json, Class clazz) { return null; } @Override public String object2Json(Object instance) { return null; } /** * Parse json to object * * @param input json string * @param clazz object type * @return instance of object */ @Override public T parseObject(String input, Type clazz) { return null; } @Override public void init(Context context) { } } ================================================ FILE: base/src/main/java/com/ccj/base/utils/router/RouterService.java ================================================ package com.ccj.base.utils.router; import com.alibaba.android.arouter.facade.template.IProvider; /** * Created by chenchangjun on 17/8/9. */ public interface RouterService extends IProvider { String start(String name); } ================================================ FILE: base/src/main/java/com/ccj/base/utils/router/RouterUtils.java ================================================ package com.ccj.base.utils.router; import android.app.Activity; import com.alibaba.android.arouter.launcher.ARouter; //https://github.com/alibaba/ARouter public class RouterUtils { public static void inject(Object obj){ ARouter.getInstance().inject(obj); } /** * 跳转 * 得到 跳转对象 * @param path * @return */ public static Object navigation(String path) { // 构建标准的路由请求 return ARouter.getInstance().build(path).navigation(); } public void startActivityForResult(Activity activity,String path,int requestCode,String argKey,Object argValue) { ARouter.getInstance().build(path). withObject(argKey, argValue). navigation(activity, requestCode); } public void startActivityForCallback() { // 使用两个参数的navigation方法,可以获取单次跳转的结果 //ARouter.getInstance().build("/test/1").navigation(this, new NavigationCallback() ); } } ================================================ FILE: base/src/main/java/com/ccj/base/view/SuperRecyclerView.java ================================================ package com.ccj.base.view; import android.content.Context; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import com.ccj.base.R; import com.ccj.base.utils.ToastUtil; import com.ccj.base.view.list.OnAppBarSkipListener; import com.ccj.base.view.list.OnLoadNextListener; public class SuperRecyclerView extends RecyclerView { private OnLoadNextListener mLoadNextListener; private OnAppBarSkipListener mAppBarSkipListener; //是否正在加载 private boolean isLoading = false; //是否加载到最后 private boolean isEnd = false; //没有更多提示是否已显示过 private boolean toastHasShown = false; private int mActionBarAutoHideSensivity = 0; private int mActionBarAutoHideMinY = 0; private int mActionBarAutoHideSignal = 0; public SuperRecyclerView(Context context) { super(context); init(); } public SuperRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public SuperRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public void setOnAppBarSkipListener(OnAppBarSkipListener listener) { mAppBarSkipListener = listener; } private void init() { addOnScrollListener(new OnScrollListener() { final static int ITEMS_THRESHOLD = 1; int lastFvi = 0; @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); LinearLayoutManager mLayoutManager = (LinearLayoutManager) getLayoutManager(); int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition(); if (lastFvi != firstVisibleItem) { if (lastFvi > 0) { onMainContentScrolled(firstVisibleItem <= ITEMS_THRESHOLD ? 0 : Integer.MAX_VALUE, lastFvi - firstVisibleItem > 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE ); } lastFvi = firstVisibleItem; } int totalItemCount = mLayoutManager.getItemCount(); if (isEnd) { if (!toastHasShown) { //加载到最后,判断是否拉到底部 if (!canScrollVertically(1)) { if (canScrollVertically(-1)) {//只有内容超过一屏时才触发提示 ToastUtil.show( getResources().getString(R.string.no_more)); toastHasShown = true; } } } } else if (!isLoading && mLoadNextListener != null) { int lastVisibleItem = mLayoutManager.findLastVisibleItemPosition(); //lastVisibleItem >= totalItemCount - 3 表示剩下2个item自动加载(因为包含Banner) // dy>0 表示向下滑动 if (totalItemCount > 5 && lastVisibleItem >= totalItemCount - 3 && dy > 0) { isLoading = true; mLoadNextListener.onLoadNext(); } } //用于防止首页好价的toolbar 因快速的显示隐藏而抖动 if (null != mAppBarSkipListener) { if (firstVisibleItem > ITEMS_THRESHOLD) { if (Math.abs(dy) < 15) { mAppBarSkipListener.isSkip(true); } else { mAppBarSkipListener.isSkip(false); } } else { mAppBarSkipListener.isSkip(false); } } } }); initActionBarAutoHide(); } public void setLoadNextListener(OnLoadNextListener listener) { mLoadNextListener = listener; } public void setLoadingState(boolean isLoading) { this.isLoading = isLoading; } public boolean getLoadingState() { return isLoading; } /** * 设置是否加载到最后 * * @param isEnd true:不再触发翻页 false:正常翻页 */ public void setLoadToEnd(boolean isEnd) { if (!isEnd) { //列表已刷新,重置toastHasShown状态 toastHasShown = false; } this.isEnd = isEnd; } /** * Initializes the Action Bar auto-hide (aka Quick Recall) effect. */ protected void initActionBarAutoHide() { mActionBarAutoHideMinY = getResources().getDimensionPixelSize( R.dimen.action_bar_auto_hide_min_y); mActionBarAutoHideSensivity = getResources().getDimensionPixelSize( R.dimen.action_bar_auto_hide_sensivity); } /** * Copied from google io 2014 by Aidi * Indicates that the main content has scrolled (for the purposes of showing/hiding * the action bar for the "action bar auto hide" effect). currentY and deltaY may be exact * (if the underlying view supports it) or may be approximate indications: * deltaY may be INT_MAX to mean "scrolled forward indeterminately" and INT_MIN to mean * "scrolled backward indeterminately". currentY may be 0 to mean "somewhere close to the * start of the list" and INT_MAX to mean "we don't know, but not at the start of the list" */ public void onMainContentScrolled(int currentY, int deltaY) { if (mLoadNextListener != null) { if (deltaY > mActionBarAutoHideSensivity) { deltaY = mActionBarAutoHideSensivity; } else if (deltaY < -mActionBarAutoHideSensivity) { deltaY = -mActionBarAutoHideSensivity; } if (Math.signum(deltaY) * Math.signum(mActionBarAutoHideSignal) < 0) { // deltaY is a motion opposite to the accumulated signal, so reset signal mActionBarAutoHideSignal = deltaY; } else { // add to accumulated signal mActionBarAutoHideSignal += deltaY; } boolean shouldShow = currentY < mActionBarAutoHideMinY || (mActionBarAutoHideSignal <= -mActionBarAutoHideSensivity); mLoadNextListener.autoShowOrHideToolbar(shouldShow); } } } ================================================ FILE: base/src/main/java/com/ccj/base/view/list/OnAppBarSkipListener.java ================================================ package com.ccj.base.view.list; /** * 控制监听好价 toolbar显示隐藏抖动 */ public interface OnAppBarSkipListener { void isSkip(boolean iskip); } ================================================ FILE: base/src/main/java/com/ccj/base/view/list/OnLoadNextListener.java ================================================ package com.ccj.base.view.list; /** */ public interface OnLoadNextListener { public void onLoadNext(); public void autoShowOrHideToolbar(boolean show); } ================================================ FILE: base/src/main/res/anim/anim_bottom_in.xml ================================================ ================================================ FILE: base/src/main/res/anim/anim_bottom_out.xml ================================================ ================================================ FILE: base/src/main/res/anim/dialog_enter.xml ================================================ ================================================ FILE: base/src/main/res/anim/dialog_exit.xml ================================================ ================================================ FILE: base/src/main/res/anim/footer_menu_slide_in.xml ================================================ ================================================ FILE: base/src/main/res/anim/footer_menu_slide_out.xml ================================================ ================================================ FILE: base/src/main/res/anim/in_from_bottom.xml ================================================ ================================================ FILE: base/src/main/res/anim/in_from_top.xml ================================================ ================================================ FILE: base/src/main/res/anim/out_to_bottom.xml ================================================ ================================================ FILE: base/src/main/res/anim/out_to_top.xml ================================================ ================================================ FILE: base/src/main/res/drawable/bg_toolbar.xml ================================================ ================================================ FILE: base/src/main/res/drawable/btn_ripple.xml ================================================ ================================================ FILE: base/src/main/res/layout/base_layout_tool_bar.xml ================================================ ================================================ FILE: base/src/main/res/values/colors.xml ================================================ #3F51B5 #303F9F #FF4081 #FF78909C #FF03A9F4 #FF78909C #FF03A9F4 #FF0288D1 @color/primary_dark #bc1717 #dd4b39 @android:color/white #6699ff #3366cc #f2f2f2 #00000000 #ffcc00 @android:color/white @android:color/black #2b8cff #ff3824 #929292 #f8f8f9 #c9c9c9 #70dbdb #dddddd #00000000 @android:color/white #dcdcdc ================================================ FILE: base/src/main/res/values/dimens.xml ================================================ 16dp 16dp 100dp 48dp 8.0dip 5dp 10dp 20dp 30dp 2dp 80dp 2dp 1dp 5dp 10dp 16sp 18sp 22sp 400dp 200dp 18sp ================================================ FILE: base/src/main/res/values/strings.xml ================================================ Android组件化 没有更多了 点击屏幕,重新加载 暂无内容 加载中… 内容加载失败\r\n点击重新加载 没有可用的网络 没有可用的网络 正在加载... ================================================ FILE: base/src/main/res/values/styles.xml ================================================ ================================================ FILE: base/src/test/java/com/ccj/base/ExampleUnitTest.java ================================================ package com.ccj.base; import org.junit.Test; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { @Test public void addition_isCorrect() throws Exception { assertEquals(4, 2 + 2); } } ================================================ FILE: build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() //butterknife mavenCentral() maven { url 'https://maven.google.com/' name 'Google' } } dependencies { classpath 'com.android.tools.build:gradle:2.3.2' //butterknife classpath 'com.jakewharton:butterknife-gradle-plugin:8.4.0' } } allprojects { repositories { jcenter() maven { url 'https://maven.google.com/' name 'Google' } } } task clean(type: Delete) { delete rootProject.buildDir } def androidSupportVersion = '25.3.1' def butterknifeVersion = '8.4.0' // Define versions in a single place //时间:2017.2.13;每次修改版本号都要添加修改时间 ext { // Sdk and tools //localBuildToolsVersion是gradle.properties中的数据 buildToolsVersion = localBuildToolsVersion compileSdkVersion = 25 minSdkVersion = 16 targetSdkVersion = 25 versionCode = 1 versionName = "1.0" javaVersion = JavaVersion.VERSION_1_8 // App dependencies version appcompatV7 = "com.android.support:appcompat-v7:$androidSupportVersion" constraintLayout = 'com.android.support.constraint:constraint-layout:1.0.2' eventbusVersion = "3.0.0" //arouter arouterApi = 'com.alibaba:arouter-api:1.3.1' arouterCompiler = 'com.alibaba:arouter-compiler:1.1.4' //butterknife butterknife = "com.jakewharton:butterknife:$butterknifeVersion" butterknifeCompiler = "com.jakewharton:butterknife-compiler:$butterknifeVersion" //eventbusVersion eventbus= "org.greenrobot:eventbus:$eventbusVersion" cookieVersion = "v1.0.1" toastyVersion = "1.1.3" } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Mon Dec 28 10:00:20 PST 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip ================================================ FILE: gradle.properties ================================================ ## Project-wide Gradle settings. # # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx10248m -XX:MaxPermSize=256m #org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true #Tue Jul 05 10:55:37 CST 2016 localBuildToolsVersion=25.0.3 localGradlePluginVersion=2.3.3 # true代表模块开发,false代表合并到主app. isModule=true ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: module_home/.gitignore ================================================ /build ================================================ FILE: module_home/build.gradle ================================================ if (isModule.toBoolean()) { apply plugin: 'com.android.application' } else { apply plugin: 'com.android.library' } //butterknife apply plugin: 'com.jakewharton.butterknife' android { compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion defaultConfig { if (isModule.toBoolean()) { applicationId "com.ccj.home" } minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode rootProject.ext.versionCode versionName rootProject.ext.versionName // resourcePrefix "haojia_" //arouter javaCompileOptions { annotationProcessorOptions { arguments = [moduleName: project.getName()] } } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets { main { if (isModule.toBoolean()) { manifest.srcFile 'src/main/AndroidManifest.xml' } else { manifest.srcFile 'src/main/release/AndroidManifest.xml' java { exclude 'debug/**' } } } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) //butterknife annotationProcessor rootProject.ext.butterknifeCompiler //arouter compile rootProject.ext.arouterApi annotationProcessor rootProject.ext.arouterCompiler compile project(':base') } ================================================ FILE: module_home/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: module_home/src/main/AndroidManifest.xml ================================================ ================================================ FILE: module_home/src/main/java/com/ccj/home/HomeFragment.java ================================================ package com.ccj.home; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; import com.ccj.base.Constants; import com.ccj.base.RouterConstants; import com.ccj.base.base.BaseApplication; /** * Created by chenchangjun on 18/1/25. */ @Route(path = RouterConstants.HOME_MUDULE_FRAGMENT_HOME_HOME) public class HomeFragment extends Fragment implements View.OnClickListener { TextView button; Button button2; Button button3; Button button4; private View view; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { if (view == null) { view = inflater.inflate(R.layout.home_fragment_haojia_home, null); } return view; } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); button2= (Button) view.findViewById(R.id.button2); button3= (Button) view.findViewById(R.id.button3); button4= (Button) view.findViewById(R.id.button4); button2.setOnClickListener(this); button3.setOnClickListener(this); button4.setOnClickListener(this); } /** * Called when a view has been clicked. * * @param v The view that was clicked. */ @Override public void onClick(View v) { int i = v.getId(); if (i == R.id.button2) { navigateToLogin(); } else if (i == R.id.button3) { navigateMeiziDetail(); } else if (i == R.id.button4) { navigateTakePhoto(); } } private void navigateMeiziDetail() { ARouter.getInstance().build(RouterConstants.MEIZI_MUDULE_ACTIVITY_MEIZI_DETAIL). withString(Constants.PARAMS_REQUEST_FOR_DETAIL, "http://7xi8d6.com1.z0.glb.clouddn.com/20180129074038_O3ydq4_Screenshot.jpeg"). navigation(getActivity()); } private void navigateToLogin() { ARouter.getInstance().build(RouterConstants.USER_MOUDLE_ACTIVITY). withString(Constants.START_LOGIN_WITH_PARAMS, "I am params from HomeFragment"). navigation(getActivity(), Constants.REQUEST_START_LOGIN); } private void navigateTakePhoto() { ARouter.getInstance(). build(RouterConstants.VIDEO_MUDULE_ACTIVITY). withString(Constants.START_LOGIN_WITH_PARAMS, "I am params from HomeFragment"). navigation(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == Constants.REQUEST_START_LOGIN) { if (data == null) { return; } String str = data.getStringExtra(Constants.PARAMS_RESULT_FROM_LOGIN); if (str == null) { return; } button.setText(str); BaseApplication.showToast(str); } } } ================================================ FILE: module_home/src/main/java/com/ccj/home/MainActivity.java ================================================ package com.ccj.home; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } } ================================================ FILE: module_home/src/main/release/AndroidManifest.xml ================================================ ================================================ FILE: module_home/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: module_home/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: module_home/src/main/res/layout/activity_main.xml ================================================ ================================================ FILE: module_home/src/main/res/layout/home_fragment_haojia_home.xml ================================================