├── CleanArch.iml ├── LICENSE ├── QA ├── findbugs │ └── findbugs-filter.xml └── quality.gradle ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── tencent │ │ └── clean │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── tencent │ │ │ └── clean │ │ │ ├── AndroidApplication.java │ │ │ ├── RxBus.java │ │ │ ├── data │ │ │ ├── RetrofitHelper.java │ │ │ ├── interactors │ │ │ │ └── SampleInteractor.java │ │ │ ├── model │ │ │ │ ├── SampleModel.java │ │ │ │ └── SampleResult.java │ │ │ ├── repository │ │ │ │ ├── SampleLocalRepositoryImpl.java │ │ │ │ ├── SampleRemoveRepositroyImpl.java │ │ │ │ └── SampleRepository.java │ │ │ └── service │ │ │ │ └── SampleService.java │ │ │ ├── domain │ │ │ ├── logic │ │ │ │ └── Converter.java │ │ │ └── usercase │ │ │ │ └── SampleUserCase.java │ │ │ └── presentation │ │ │ ├── BasePresenter.java │ │ │ ├── BaseView.java │ │ │ ├── adapter │ │ │ └── SampleAdapter.java │ │ │ ├── event │ │ │ ├── SampleReloadEvent.java │ │ │ └── SampleRxEventClearDb.java │ │ │ ├── presenters │ │ │ ├── MainPresenter.java │ │ │ └── impl │ │ │ │ └── MainPresenterImpl.java │ │ │ └── ui │ │ │ ├── activities │ │ │ └── MainActivity.java │ │ │ └── fragment │ │ │ └── SampleFragment.java │ └── res │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── sample_fagment.xml │ │ └── sample_item.xml │ │ ├── menu │ │ └── main_menu.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ ├── celanarch.png │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── tencent │ └── clean │ └── ExampleUnitTest.java ├── build.gradle ├── build ├── generated │ └── mockable-android-23.jar └── intermediates │ └── dex-cache │ └── cache.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties └── settings.gradle /CleanArch.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Dario Miličić 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /QA/findbugs/findbugs-filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /QA/quality.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'findbugs' 2 | 3 | task findbugs(type: FindBugs) { 4 | ignoreFailures = true 5 | effort = "default" 6 | reportLevel = "medium" 7 | excludeFilter = new File("${project.rootDir}/QA/findbugs/findbugs-filter.xml") 8 | classes = files("${project.rootDir}/app/build/intermediates/classes") 9 | source = fileTree('src/main/java/') 10 | classpath = files() 11 | reports { 12 | xml.enabled = false 13 | html.enabled = true 14 | html { 15 | destination "${project.buildDir}/reports/findbugs/findbugs-output.html" 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # overview 2 | 3 | 如果图片看不到,可能是因为防盗链了,请直接看这里 http://www.jianshu.com/p/cba6663435c7 4 | 也可以在这里提出你的疑问,多谢 5 | 6 | # 特性 7 | 简单的实现了一下Android-Clean-Architecture,使用到了一些比较优秀的库 8 | #### realm 目前最流弊的移动端db 9 | #### rxjava 一切都是流,你懂得 10 | #### butterknife 视图注入框架 11 | #### glide 图片加载框架 12 | #### retrofit 网络请求框架,可以与rxjava搭配 13 | # 原理 14 | 15 | 最近 ***Android-CleanArchitecture*** 闹得是沸沸扬扬,然而笔者也不甘寂寞,一直在研究这个东西,看过,不少的实现。 16 | 17 | 比如 android10 大神的实现 https://github.com/android10/Android-CleanArchitecture 18 | 比如 googlesample 的实现 https://github.com/googlesamples/android-architecture/tree/todo-mvp-clean/ 19 | 比如 dmilicic 大神的实现 https://github.com/dmilicic/Android-Clean-Boilerplate 20 | 21 | Boilerplate 其实就是 模板的意思了,相信大家也看过不少这个词了。 22 | 23 | 然而,架构方面的文章也很多,但是,究其源头,无非都是出自uncle-bob 叔叔的这篇 https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html。 24 | 至于大家为什么大谈特谈,那一定是有他的道理的。就好比,用了CleanArchitecture,你会得到以下好处。 25 | 26 | >**代码复用性更高** 27 | >**更易于测试** 28 | >**耦合度更小** 29 | 30 | 下面这幅图,是googlesample下面的了。 31 | ![googlesamples](http://upload-images.jianshu.io/upload_images/1019822-cda363d399934d04.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 32 | 33 | 下面这幅图,是uncle-bob画的了。 34 | ![uncle-bob](http://upload-images.jianshu.io/upload_images/1019822-b2acfd9ed6182541.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 35 | 36 | 细心的你已经发现了,这两个图其实是一个意思。从大的方向上看,都是三层结构。 37 | 38 | > **DataLayer** 39 | 40 | 最底层,完全不知道有`DomainLayer`,`PresentationLayer`的存在,听到这里,你还在怀疑这个架构的`可测试性`和`耦合度低`吗?那么`DataLayer`的主要职责是什么? 41 | 1、从网络获取数据,向网络提交数据,总之就是和网络打交道。 42 | 2、从本地DB,shareprefence等等,内存等,总之就是本地获取数据,缓存数据,总之就是和本地数据打交道的。 43 | 这也就是你为什么看到很多Android-CleanArchitecture 的 package里面有一个local ,和一个remote了,然而是否有必要分的这么细,个人习惯啊~,不强求。反正这一层如果出现了 anroid.os***,我就更你拼了,对不起,你已经偏离了Android-CleanArchitecture了。 44 | > **DomainLayer** 45 | 46 | 中间层,他完全不知道有一个`PresentationLayer`存在,他只知道,有DataLayer,他可以基于这些数据,建立很多玩法,比如去网络拿一堆名人回来,然后将这些数据缓存到本地,在比如,他写了一篇黑某明星的文章,将文字发布到网上等等。因此他的主要职责是: 47 | 1、控制`DataLayer`对数据做***增删改查***,没错,就这么简单,然后就没有然后了。 48 | 2、真的没有了,不骗你,但是这一层如果出现了 anroid.os***,我就更你拼了,对不起,你已经偏离了Android-CleanArchitecture了。 49 | 50 | > **PresentationLayer** 51 | 52 | 最上层,他知道`DomainLayer`,有人要问了,那么他知道`DataLayer`,回答,他知道你妹~ 他累不累啊,要知道这么多? 53 | 因此,它只知道`DomainLayer`,那么他的职责有哪些? 54 | 1、通知`DomainLayer`有活干了,根据`DomainLayer`反馈变化界面 55 | 2、通知`DomainLayer`有活干了,根据`DomainLayer`反馈变化界面 56 | 3、通知`DomainLayer`有活干了,根据`DomainLayer`反馈变化界面 57 | 这年头,重要的时间一定要说三遍,而且,就是这么任性~~ 58 | 59 | 分析了每层之后,我们发现,依赖的关系是 **PresentationLayer** --> **DomainLayer** --> **DataLayer** 的。 60 | **DomainLayer** --> **DataLayer** 不知道有android平台的存在。 61 | 因此,只要我们围绕这个原则去做架构,那么就称的上是Android-CleanArchitecture。 62 | 63 | 64 | # todo 65 | dagger2依赖注入,随便比较难理解吧,但是用上之后,显然要大幅降低耦合。 66 | 67 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply from: "${project.rootDir}/QA/quality.gradle" 4 | apply plugin: 'realm-android' 5 | 6 | android { 7 | compileSdkVersion 23 8 | buildToolsVersion "23.0.2" 9 | 10 | defaultConfig { 11 | applicationId "com.tencent.clean" 12 | minSdkVersion 17 13 | targetSdkVersion 23 14 | versionCode 1 15 | versionName "1.0" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | 27 | compile fileTree(dir: 'libs', include: ['*.jar']) 28 | 29 | // general 30 | compile 'com.android.support:appcompat-v7:23.2.1' 31 | compile 'com.android.support:support-v4:23.2.1' 32 | compile 'com.android.support:support-v13:23.2.1' 33 | compile 'com.android.support:design:23.2.1' 34 | compile 'com.android.support:cardview-v7:23.2.1' 35 | compile 'com.jakewharton:butterknife:7.0.1' 36 | compile 'com.jakewharton.timber:timber:4.1.0' 37 | compile 'io.reactivex:rxandroid:1.2.0' 38 | compile 'io.reactivex:rxjava:1.1.5' 39 | 40 | // network 41 | compile 'com.squareup.retrofit2:retrofit:2.0.2' 42 | compile 'com.squareup.retrofit2:converter-gson:2.0.2' 43 | compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0' 44 | 45 | //iamgeLoader 46 | compile 'com.github.bumptech.glide:glide:3.7.0' 47 | 48 | //persistence 49 | 50 | // tests 51 | testCompile 'junit:junit:4.12' 52 | testCompile "org.mockito:mockito-core:1.+" 53 | } 54 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # You can edit the include path and order by changing the proguardFiles 4 | # directive in build.gradle. 5 | # 6 | # For more details, see 7 | # http://developer.android.com/guide/developing/tools/proguard.html 8 | 9 | # Add any project specific keep options here: 10 | 11 | # If your project uses WebView with JS, uncomment the following 12 | # and specify the fully qualified class name to the JavaScript interface 13 | # class: 14 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 15 | # public *; 16 | #} 17 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/tencent/clean/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/AndroidApplication.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean; 2 | 3 | import android.app.Application; 4 | 5 | import timber.log.Timber; 6 | import timber.log.Timber.DebugTree; 7 | 8 | public class AndroidApplication extends Application { 9 | @Override 10 | public void onCreate() { 11 | super.onCreate(); 12 | 13 | // initiate Timber 14 | if (BuildConfig.DEBUG){ 15 | Timber.plant(new DebugTree()); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/RxBus.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import rx.functions.Action1; 6 | import rx.subjects.PublishSubject; 7 | import rx.subjects.SerializedSubject; 8 | import rx.subjects.Subject; 9 | import rx.subscriptions.CompositeSubscription; 10 | 11 | /** 12 | * Created by hoollyzhang on 16/5/25. 13 | * Description : 14 | */ 15 | public final class RxBus { 16 | 17 | private static RxBus sRxBus; 18 | private final Subject _bus = new SerializedSubject<>(PublishSubject.create()); 19 | 20 | 21 | /** 22 | * 消息发送者调用 23 | * @param o 24 | */ 25 | public void send(Object o) { 26 | if (_bus.hasObservers()){ 27 | _bus.onNext(o); 28 | } 29 | } 30 | 31 | /** 32 | * 拿到事件总线 33 | * @return 34 | */ 35 | public static RxBus getRxBusSingleton() { 36 | if (sRxBus == null) { 37 | sRxBus = new RxBus(); 38 | } 39 | 40 | return sRxBus; 41 | } 42 | 43 | /** 44 | * 订阅 45 | * @param subscription new CompositeSubscription() 46 | * @param eventLisener 47 | */ 48 | public void subscribe(@NonNull CompositeSubscription subscription,@NonNull final EventLisener eventLisener){ 49 | subscription.add(_bus.subscribe(new Action1() { 50 | @Override 51 | public void call(Object event) { 52 | eventLisener.dealRxEvent(event); 53 | } 54 | })); 55 | } 56 | 57 | public interface EventLisener{ 58 | /** 59 | * 处理总线事件 60 | * @param event 61 | */ 62 | void dealRxEvent(Object event); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/data/RetrofitHelper.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.data; 2 | 3 | import com.tencent.clean.data.service.SampleService; 4 | 5 | import retrofit2.Retrofit; 6 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 7 | import retrofit2.converter.gson.GsonConverterFactory; 8 | 9 | /** 10 | * Created by hoollyzhang on 16/5/25. 11 | * Description : 12 | */ 13 | public class RetrofitHelper { 14 | static SampleService service; 15 | 16 | public static SampleService getSampleService() { 17 | 18 | if (service == null) { 19 | 20 | synchronized (RetrofitHelper.class) { 21 | 22 | if (service == null) { 23 | Retrofit retrofit = new Retrofit.Builder() 24 | .baseUrl("http://gank.io/") 25 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 26 | .addConverterFactory(GsonConverterFactory.create()) 27 | .build(); 28 | service = retrofit.create(SampleService.class); 29 | } 30 | } 31 | } 32 | 33 | return service; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/data/interactors/SampleInteractor.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.data.interactors; 2 | 3 | /*** 4 | * 5 | */ 6 | public interface SampleInteractor { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/data/model/SampleModel.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.data.model; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | 6 | import io.realm.RealmObject; 7 | import io.realm.annotations.PrimaryKey; 8 | 9 | /** 10 | * A sample model. Replace this with your own. 11 | */ 12 | public class SampleModel extends RealmObject { 13 | 14 | 15 | @SerializedName("_id") 16 | @PrimaryKey 17 | @Expose 18 | private String id; 19 | @SerializedName("createdAt") 20 | @Expose 21 | private String createdAt; 22 | @SerializedName("desc") 23 | @Expose 24 | private String desc; 25 | @SerializedName("publishedAt") 26 | @Expose 27 | private String publishedAt; 28 | @SerializedName("source") 29 | @Expose 30 | private String source; 31 | @SerializedName("type") 32 | @Expose 33 | private String type; 34 | @SerializedName("url") 35 | @Expose 36 | private String url; 37 | @SerializedName("used") 38 | @Expose 39 | private Boolean used; 40 | @SerializedName("who") 41 | @Expose 42 | private String who; 43 | 44 | public String getId() { 45 | return id; 46 | } 47 | 48 | public void setId(String id) { 49 | this.id = id; 50 | } 51 | 52 | public String getCreatedAt() { 53 | return createdAt; 54 | } 55 | 56 | public void setCreatedAt(String createdAt) { 57 | this.createdAt = createdAt; 58 | } 59 | 60 | public String getDesc() { 61 | return desc; 62 | } 63 | 64 | public void setDesc(String desc) { 65 | this.desc = desc; 66 | } 67 | 68 | public String getPublishedAt() { 69 | return publishedAt; 70 | } 71 | 72 | public void setPublishedAt(String publishedAt) { 73 | this.publishedAt = publishedAt; 74 | } 75 | 76 | public String getSource() { 77 | return source; 78 | } 79 | 80 | public void setSource(String source) { 81 | this.source = source; 82 | } 83 | 84 | public String getType() { 85 | return type; 86 | } 87 | 88 | public void setType(String type) { 89 | this.type = type; 90 | } 91 | 92 | public String getUrl() { 93 | return url; 94 | } 95 | 96 | public void setUrl(String url) { 97 | this.url = url; 98 | } 99 | 100 | public Boolean getUsed() { 101 | return used; 102 | } 103 | 104 | public void setUsed(Boolean used) { 105 | this.used = used; 106 | } 107 | 108 | public String getWho() { 109 | return who; 110 | } 111 | 112 | public void setWho(String who) { 113 | this.who = who; 114 | } 115 | 116 | @Override 117 | public String toString() { 118 | return "SampleModel{" + 119 | "id='" + id + '\'' + 120 | ", createdAt='" + createdAt + '\'' + 121 | ", desc='" + desc + '\'' + 122 | ", publishedAt='" + publishedAt + '\'' + 123 | ", source='" + source + '\'' + 124 | ", type='" + type + '\'' + 125 | ", url='" + url + '\'' + 126 | ", used=" + used + 127 | ", who='" + who + '\'' + 128 | '}'; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/data/model/SampleResult.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.data.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by hoollyzhang on 16/5/30. 9 | * Description : 10 | */ 11 | public class SampleResult { 12 | public boolean error; 13 | public @SerializedName("results") 14 | List beauties; 15 | 16 | public boolean isError() { 17 | return error; 18 | } 19 | 20 | public void setError(boolean error) { 21 | this.error = error; 22 | } 23 | 24 | public List getBeauties() { 25 | return beauties; 26 | } 27 | 28 | public void setBeauties(List beauties) { 29 | this.beauties = beauties; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "SampleResult{" + 35 | "error=" + error + 36 | ", beauties=" + beauties + 37 | '}'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/data/repository/SampleLocalRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.data.repository; 2 | 3 | import android.util.Log; 4 | 5 | import com.tencent.clean.data.model.SampleModel; 6 | import com.tencent.clean.domain.logic.Converter; 7 | 8 | import java.util.List; 9 | 10 | import io.realm.Realm; 11 | import io.realm.RealmResults; 12 | import rx.Observable; 13 | import rx.functions.Func1; 14 | 15 | /** 16 | * Created by hoollyzhang on 16/5/30. 17 | * Description : 18 | */ 19 | public class SampleLocalRepositoryImpl implements SampleRepository { 20 | 21 | public SampleLocalRepositoryImpl() { 22 | } 23 | 24 | 25 | //realm maybe have some bugs....,fuck 26 | @Override 27 | public Observable> lists(int count, int page) { 28 | Realm realm = Realm.getDefaultInstance(); 29 | Observable observable = realm.where(SampleModel.class).findAll().asObservable().map(new Func1, List>() { 30 | @Override 31 | public List call(RealmResults sampleModels) { 32 | return Converter.RealmResultList2SampleModel(sampleModels); 33 | } 34 | }); 35 | return observable.first(); 36 | } 37 | //realm maybe have some bugs....,fuck 38 | /* @Override 39 | public Observable> lists(int count, int page) { 40 | Realm realm = Realm.getDefaultInstance(); 41 | RealmResults realmResults = realm.where(SampleModel.class).findAll(); 42 | if (realmResults.size() > 0){ 43 | return realmResults.asObservable().map(new Func1, List>() { 44 | @Override 45 | public List call(RealmResults sampleModels) { 46 | return Converter.RealmResultList2SampleModel(sampleModels); 47 | } 48 | }); 49 | }else{ 50 | return Observable.empty(); 51 | 52 | } 53 | 54 | }*/ 55 | 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/data/repository/SampleRemoveRepositroyImpl.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.data.repository; 2 | 3 | import com.tencent.clean.data.model.SampleModel; 4 | import com.tencent.clean.data.RetrofitHelper; 5 | import com.tencent.clean.data.model.SampleResult; 6 | 7 | import java.util.List; 8 | import rx.Observable; 9 | import rx.functions.Func1; 10 | import rx.schedulers.Schedulers; 11 | 12 | /** 13 | * Created by hoollyzhang on 16/5/26. 14 | * Description : 15 | */ 16 | public class SampleRemoveRepositroyImpl implements SampleRepository { 17 | 18 | 19 | public SampleRemoveRepositroyImpl() { 20 | 21 | } 22 | 23 | @Override 24 | public Observable> lists(int count, int page) { 25 | return RetrofitHelper.getSampleService().listPic(count, page) 26 | .map(new Func1>() { 27 | @Override 28 | public List call(SampleResult sampleResult) { 29 | return sampleResult.beauties; 30 | } 31 | }).subscribeOn(Schedulers.io()); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/data/repository/SampleRepository.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.data.repository; 2 | 3 | import com.tencent.clean.data.model.SampleModel; 4 | 5 | import java.util.List; 6 | 7 | import rx.Observable; 8 | 9 | /** 10 | * A sample repository with CRUD operations on a model. 11 | */ 12 | public interface SampleRepository { 13 | 14 | Observable> lists(int count,int page); 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/data/service/SampleService.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.data.service; 2 | 3 | import com.tencent.clean.data.model.SampleModel; 4 | import com.tencent.clean.data.model.SampleResult; 5 | 6 | import java.util.List; 7 | 8 | import retrofit2.http.GET; 9 | import retrofit2.http.Path; 10 | import rx.Observable; 11 | 12 | /** 13 | * Created by hoollyzhang on 16/5/25. 14 | * Description : 15 | */ 16 | public interface SampleService { 17 | 18 | @GET("api/data/福利/{count}/{page}") 19 | Observable listPic(@Path("count") int count, @Path("page") int page); 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/domain/logic/Converter.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.domain.logic; 2 | 3 | import com.tencent.clean.data.model.SampleModel; 4 | 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | 8 | import io.realm.RealmResults; 9 | 10 | /** 11 | * Created by hoollyzhang on 16/5/31. 12 | * Description : 13 | */ 14 | public class Converter { 15 | public static List RealmResultList2SampleModel(RealmResults sampleModels){ 16 | 17 | List list = new LinkedList<>(); 18 | 19 | for (SampleModel sampleModelRealmObject : sampleModels){ 20 | list.add(sampleModelRealmObject); 21 | } 22 | return list; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/domain/usercase/SampleUserCase.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.domain.usercase; 2 | 3 | import android.content.Context; 4 | 5 | import com.tencent.clean.data.model.SampleModel; 6 | import com.tencent.clean.data.repository.SampleLocalRepositoryImpl; 7 | import com.tencent.clean.data.repository.SampleRemoveRepositroyImpl; 8 | import com.tencent.clean.data.repository.SampleRepository; 9 | 10 | import java.util.List; 11 | 12 | import io.realm.Realm; 13 | import io.realm.RealmConfiguration; 14 | import rx.Observable; 15 | import rx.functions.Action1; 16 | import rx.functions.Func1; 17 | import timber.log.Timber; 18 | 19 | /** 20 | * Created by hoollyzhang on 16/5/26. 21 | * Description : 22 | */ 23 | public class SampleUserCase { 24 | SampleRepository sampleRepositoryRemote ; 25 | SampleRepository sampleRepositoryLocal ; 26 | 27 | public SampleUserCase(Context context) { 28 | RealmConfiguration config = new RealmConfiguration.Builder(context) 29 | .name("sample.realm") 30 | .deleteRealmIfMigrationNeeded() 31 | .schemaVersion(1) 32 | .build(); 33 | Realm.setDefaultConfiguration(config); 34 | sampleRepositoryRemote = new SampleRemoveRepositroyImpl(); 35 | sampleRepositoryLocal = new SampleLocalRepositoryImpl(); 36 | } 37 | 38 | //get data from mutil data source 39 | public Observable> sample() { 40 | return Observable.concat( 41 | sampleRepositoryLocal.lists(100, 1), 42 | sampleRepositoryRemote.lists(100, 1) 43 | ).first(new Func1, Boolean>() { 44 | @Override 45 | public Boolean call(List sampleModels) {// TODO: 16/5/31 这里应该加入缓存过期判断的策略 46 | return sampleModels != null && sampleModels.size() > 0; 47 | } 48 | }).doOnNext(new Action1>() { 49 | @Override 50 | public void call(List sampleModels) { 51 | Realm realm = Realm.getDefaultInstance(); 52 | realm.beginTransaction(); 53 | realm.copyToRealmOrUpdate(sampleModels); 54 | realm.commitTransaction(); 55 | } 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/presentation/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.presentation; 2 | 3 | public interface BasePresenter { 4 | /** 5 | * Method that control the lifecycle of the view. It should be called in the view's 6 | * (Activity or Fragment) onResume() method. 7 | */ 8 | void resume(); 9 | 10 | /** 11 | * Method that controls the lifecycle of the view. It should be called in the view's 12 | * (Activity or Fragment) onPause() method. 13 | */ 14 | void pause(); 15 | 16 | /** 17 | * Method that controls the lifecycle of the view. It should be called in the view's 18 | * (Activity or Fragment) onStop() method. 19 | */ 20 | void stop(); 21 | 22 | /** 23 | * Method that control the lifecycle of the view. It should be called in the view's 24 | * (Activity or Fragment) onDestroy() method. 25 | */ 26 | void destroy(); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/presentation/BaseView.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.presentation; 2 | 3 | /** 4 | *

5 | * This interface represents a basic view. All views should implement these common methods. 6 | *

7 | */ 8 | public interface BaseView { 9 | 10 | /** 11 | * This is a general method used for showing some kind of progress during a background task. For example, this 12 | * method should show a progress bar and/or disable buttons before some background work starts. 13 | */ 14 | void showProgress(); 15 | 16 | /** 17 | * This is a general method used for hiding progress information after a background task finishes. 18 | */ 19 | void hideProgress(); 20 | 21 | /** 22 | * This method is used for showing error messages on the UI. 23 | * 24 | * @param message The error message to be displayed. 25 | */ 26 | void showError(String message); 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/presentation/adapter/SampleAdapter.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.presentation.adapter; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import com.bumptech.glide.Glide; 11 | import com.tencent.clean.R; 12 | import com.tencent.clean.data.model.SampleModel; 13 | 14 | import java.util.List; 15 | 16 | import butterknife.Bind; 17 | import butterknife.ButterKnife; 18 | 19 | /** 20 | * Created by hoollyzhang on 16/5/30. 21 | * Description : 22 | */ 23 | public class SampleAdapter extends RecyclerView.Adapter { 24 | 25 | public void setImages(List images) { 26 | this.images = images; 27 | notifyDataSetChanged(); 28 | } 29 | 30 | List images; 31 | @Override 32 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 33 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.sample_item,parent,false); 34 | return new SampleViewHolder(view); 35 | } 36 | 37 | @Override 38 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 39 | SampleViewHolder viewholder = (SampleViewHolder) holder; 40 | SampleModel simpleModel = images.get(position); 41 | Glide.with(viewholder.imagevi.getContext()).load(simpleModel.getUrl()).into(viewholder.imagevi); 42 | viewholder.descriptionTv.setText(simpleModel.getDesc()); 43 | } 44 | 45 | @Override 46 | public int getItemCount() { 47 | return images == null ? 0 : images.size(); 48 | } 49 | 50 | static class SampleViewHolder extends RecyclerView.ViewHolder{ 51 | 52 | @Bind(R.id.imageIv) 53 | ImageView imagevi; 54 | @Bind(R.id.descriptionTv) 55 | TextView descriptionTv; 56 | 57 | public SampleViewHolder(View itemView) { 58 | super(itemView); 59 | ButterKnife.bind(this,itemView); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/presentation/event/SampleReloadEvent.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.presentation.event; 2 | 3 | /** 4 | * Created by hoollyzhang on 16/6/1. 5 | * Description : 6 | */ 7 | public class SampleReloadEvent { 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/presentation/event/SampleRxEventClearDb.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.presentation.event; 2 | 3 | /** 4 | * Created by hoollyzhang on 16/6/1. 5 | * Description : 6 | */ 7 | public class SampleRxEventClearDb { 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/presentation/presenters/MainPresenter.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.presentation.presenters; 2 | 3 | import com.tencent.clean.data.model.SampleModel; 4 | import com.tencent.clean.presentation.BasePresenter; 5 | import com.tencent.clean.presentation.BaseView; 6 | 7 | import java.util.List; 8 | 9 | 10 | /** 11 | * 放在一起,一个Presenter 对应一个view 12 | */ 13 | public interface MainPresenter extends BasePresenter { 14 | 15 | interface View extends BaseView { 16 | // TODO: Add your view methods 17 | void showSampleData(List sampleModels); 18 | } 19 | 20 | // TODO: Add your presenter methods,for example unsubscribe 21 | void unsubscribe(); 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/presentation/presenters/impl/MainPresenterImpl.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.presentation.presenters.impl; 2 | 3 | import android.util.Log; 4 | import android.widget.Toast; 5 | 6 | import com.tencent.clean.data.model.SampleModel; 7 | import com.tencent.clean.domain.usercase.SampleUserCase; 8 | import com.tencent.clean.presentation.presenters.MainPresenter; 9 | 10 | import java.util.List; 11 | 12 | import rx.Observer; 13 | import rx.Subscriber; 14 | import rx.Subscription; 15 | import rx.android.schedulers.AndroidSchedulers; 16 | import rx.schedulers.Schedulers; 17 | import timber.log.Timber; 18 | 19 | /** 20 | * 展现其实例 21 | */ 22 | public class MainPresenterImpl implements MainPresenter { 23 | 24 | private MainPresenter.View mView; 25 | private SampleUserCase sampleUserCase; 26 | private Subscription _subscription; 27 | 28 | public MainPresenterImpl(View mView, SampleUserCase sampleUserCase) { 29 | this.mView = mView; 30 | this.sampleUserCase = sampleUserCase; 31 | } 32 | 33 | @Override 34 | public void resume() { 35 | mView.showProgress(); 36 | final long startTime = System.currentTimeMillis(); 37 | _subscription = sampleUserCase.sample() 38 | .observeOn(AndroidSchedulers.mainThread()) 39 | .subscribe(new Subscriber>() { 40 | @Override 41 | public void onCompleted() { 42 | mView.hideProgress(); 43 | long ellipseTime = System.currentTimeMillis() - startTime; 44 | mView.showError("执行时间: "+ellipseTime); 45 | } 46 | 47 | @Override 48 | public void onError(Throwable e) { 49 | Log.e("test",e.getMessage()); 50 | mView.hideProgress(); 51 | } 52 | 53 | @Override 54 | public void onNext(List sampleModels) { 55 | mView.showSampleData(sampleModels); 56 | } 57 | }); 58 | 59 | } 60 | 61 | @Override 62 | public void pause() { 63 | 64 | } 65 | 66 | @Override 67 | public void stop() { 68 | 69 | } 70 | 71 | @Override 72 | public void destroy() { 73 | unsubscribe(); 74 | } 75 | 76 | 77 | @Override 78 | public void unsubscribe() { 79 | if (_subscription != null && !_subscription.isUnsubscribed()) { 80 | _subscription.unsubscribe(); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/presentation/ui/activities/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.presentation.ui.activities; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.FloatingActionButton; 5 | import android.support.design.widget.Snackbar; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.support.v7.widget.Toolbar; 8 | import android.view.Menu; 9 | import android.view.MenuInflater; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | 13 | import com.tencent.clean.R; 14 | import com.tencent.clean.RxBus; 15 | import com.tencent.clean.presentation.event.SampleReloadEvent; 16 | import com.tencent.clean.presentation.event.SampleRxEventClearDb; 17 | 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_main); 24 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 25 | setSupportActionBar(toolbar); 26 | FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); 27 | fab.setOnClickListener(new View.OnClickListener() { 28 | @Override 29 | public void onClick(View view) { 30 | Snackbar.make(view, "waht do you want", Snackbar.LENGTH_LONG) 31 | .setAction("Action", null).show(); 32 | } 33 | }); 34 | } 35 | 36 | @Override 37 | public boolean onCreateOptionsMenu(Menu menu) { 38 | MenuInflater inflater = getMenuInflater(); 39 | inflater.inflate(R.menu.main_menu, menu); 40 | return true; 41 | } 42 | 43 | @Override 44 | public boolean onOptionsItemSelected(MenuItem item) { 45 | switch (item.getItemId()) { 46 | case R.id.clear_db: 47 | RxBus.getRxBusSingleton().send(new SampleRxEventClearDb()); 48 | return true; 49 | case R.id.reload_data: 50 | RxBus.getRxBusSingleton().send(new SampleReloadEvent()); 51 | return true; 52 | default: 53 | return super.onOptionsItemSelected(item); 54 | } 55 | } 56 | 57 | 58 | } 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/tencent/clean/presentation/ui/fragment/SampleFragment.java: -------------------------------------------------------------------------------- 1 | package com.tencent.clean.presentation.ui.fragment; 2 | 3 | import android.app.Fragment; 4 | import android.app.ProgressDialog; 5 | import android.graphics.Color; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.widget.SwipeRefreshLayout; 9 | import android.support.v7.widget.GridLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.Toast; 15 | 16 | import com.tencent.clean.R; 17 | import com.tencent.clean.RxBus; 18 | import com.tencent.clean.data.model.SampleModel; 19 | import com.tencent.clean.domain.usercase.SampleUserCase; 20 | import com.tencent.clean.presentation.adapter.SampleAdapter; 21 | import com.tencent.clean.presentation.event.SampleReloadEvent; 22 | import com.tencent.clean.presentation.event.SampleRxEventClearDb; 23 | import com.tencent.clean.presentation.presenters.MainPresenter; 24 | import com.tencent.clean.presentation.presenters.impl.MainPresenterImpl; 25 | 26 | import java.util.LinkedList; 27 | import java.util.List; 28 | 29 | import butterknife.Bind; 30 | import butterknife.ButterKnife; 31 | import io.realm.Realm; 32 | import rx.subscriptions.CompositeSubscription; 33 | 34 | /** 35 | * Created by hoollyzhang on 16/5/30. 36 | * Description : 37 | */ 38 | public class SampleFragment extends Fragment implements MainPresenter.View,RxBus.EventLisener { 39 | 40 | private ProgressDialog sProgressDialog ; 41 | MainPresenter mainPresenter; 42 | 43 | @Bind(R.id.gridRv) 44 | RecyclerView gridRv; 45 | @Bind(R.id.swipeRefreshLayout) 46 | SwipeRefreshLayout swipeRefreshLayout; 47 | 48 | SampleAdapter adapter = new SampleAdapter(); 49 | 50 | CompositeSubscription _subscription = new CompositeSubscription(); 51 | @Nullable 52 | @Override 53 | public android.view.View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 54 | View view = inflater.inflate(R.layout.sample_fagment, container, false); 55 | ButterKnife.bind(this, view); 56 | gridRv.setLayoutManager(new GridLayoutManager(getActivity(), 2)); 57 | gridRv.setAdapter(adapter); 58 | swipeRefreshLayout.setColorSchemeColors(Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW); 59 | swipeRefreshLayout.setEnabled(false); 60 | SampleUserCase sampleUserCase = new SampleUserCase(getActivity()); 61 | mainPresenter = new MainPresenterImpl(this,sampleUserCase); 62 | return view; 63 | } 64 | 65 | 66 | @Override 67 | public void onResume() { 68 | super.onResume(); 69 | sProgressDialog = ProgressDialog.show(getActivity(),"提示","加载中。。。。。"); 70 | mainPresenter.resume(); 71 | RxBus.getRxBusSingleton().subscribe(_subscription,this); 72 | } 73 | 74 | @Override 75 | public void onDestroyView() { 76 | if (sProgressDialog != null){ 77 | sProgressDialog.dismiss(); 78 | sProgressDialog = null; 79 | } 80 | mainPresenter.destroy(); 81 | _subscription.clear(); 82 | super.onDestroyView(); 83 | } 84 | 85 | @Override 86 | public void onDestroy() { 87 | if (sProgressDialog != null){ 88 | sProgressDialog.dismiss(); 89 | sProgressDialog = null; 90 | } 91 | super.onDestroy(); 92 | 93 | } 94 | 95 | @Override 96 | public void showProgress() { 97 | sProgressDialog.show(); 98 | } 99 | 100 | @Override 101 | public void hideProgress() { 102 | sProgressDialog.hide(); 103 | } 104 | 105 | @Override 106 | public void showError(String message) { 107 | if (getActivity()!=null){ 108 | Toast.makeText(getActivity(),message,Toast.LENGTH_LONG).show(); 109 | } 110 | } 111 | 112 | @Override 113 | public void showSampleData(List sampleModels) { 114 | adapter.setImages(sampleModels); 115 | } 116 | 117 | @Override 118 | public void dealRxEvent(Object event) { 119 | if (event instanceof SampleRxEventClearDb){ 120 | clearDb(); 121 | }else if (event instanceof SampleReloadEvent){ 122 | reloadData(); 123 | } 124 | } 125 | 126 | private void clearDb() { 127 | Realm realm = Realm.getDefaultInstance(); 128 | realm.beginTransaction(); 129 | realm.delete(SampleModel.class); 130 | realm.commitTransaction(); 131 | adapter.setImages(new LinkedList()); 132 | Toast.makeText(getActivity(), "缓存清除成功", Toast.LENGTH_LONG).show(); 133 | } 134 | 135 | private void reloadData(){ 136 | mainPresenter.resume(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 22 | 23 | 24 | 25 | 32 | 33 | 34 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/sample_fagment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/sample_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 18 | 19 | 23 | 24 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/CleanArch/aaa1e2da374acb4ebe8aee060da7b851dbbfe4fb/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/CleanArch/aaa1e2da374acb4ebe8aee060da7b851dbbfe4fb/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/CleanArch/aaa1e2da374acb4ebe8aee060da7b851dbbfe4fb/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/celanarch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/CleanArch/aaa1e2da374acb4ebe8aee060da7b851dbbfe4fb/app/src/main/res/mipmap-xxhdpi/celanarch.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/CleanArch/aaa1e2da374acb4ebe8aee060da7b851dbbfe4fb/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/CleanArch/aaa1e2da374acb4ebe8aee060da7b851dbbfe4fb/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | 16dp 8 | 160dp 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CleanArch 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 14 |