├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── rengwuxian │ │ └── rxjavasamples │ │ ├── App.java │ │ ├── BaseFragment.java │ │ ├── MainActivity.java │ │ ├── adapter │ │ ├── ItemListAdapter.java │ │ └── ZhuangbiListAdapter.java │ │ ├── model │ │ ├── FakeThing.java │ │ ├── FakeToken.java │ │ ├── GankBeauty.java │ │ ├── GankBeautyResult.java │ │ ├── Item.java │ │ └── ZhuangbiImage.java │ │ ├── module │ │ ├── cache_6 │ │ │ ├── CacheFragment.java │ │ │ └── data │ │ │ │ ├── Data.java │ │ │ │ └── Database.java │ │ ├── elementary_1 │ │ │ └── ElementaryFragment.java │ │ ├── map_2 │ │ │ └── MapFragment.java │ │ ├── token_4 │ │ │ └── TokenFragment.java │ │ ├── token_advanced_5 │ │ │ └── TokenAdvancedFragment.java │ │ └── zip_3 │ │ │ └── ZipFragment.java │ │ ├── network │ │ ├── Network.java │ │ └── api │ │ │ ├── FakeApi.java │ │ │ ├── GankApi.java │ │ │ └── ZhuangbiApi.java │ │ └── util │ │ └── GankBeautyResultToItemsMapper.java │ └── res │ ├── drawable │ └── upload.jpg │ ├── layout │ ├── activity_main.xml │ ├── dialog_cache.xml │ ├── dialog_elementary.xml │ ├── dialog_map.xml │ ├── dialog_token.xml │ ├── dialog_token_advanced.xml │ ├── dialog_zip.xml │ ├── fragment_cache.xml │ ├── fragment_elementary.xml │ ├── fragment_map.xml │ ├── fragment_token.xml │ ├── fragment_token_advanced.xml │ ├── fragment_zip.xml │ ├── grid_item.xml │ ├── list_item.xml │ └── tip_bt.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── screenshot_1.png ├── screenshot_2.png ├── screenshot_3.png ├── screenshot_4.png ├── screenshot_5.png └── screenshot_6.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 样例代码已正式切换到基于 RxJava 2 2 | ================ 3 | 4 | > 需要旧版 RxJava 1 代码的点[这里](https://github.com/rengwuxian/RxJavaSamples/tree/1.x) 5 | 6 | ### 项目介绍 7 | 8 | RxJava 2 和 Retrofit 结合使用的几个最常见使用方式举例。 9 | 10 | 1. **基本使用** 11 | 12 | 实现最基本的网络请求和结果处理。 13 | ![screenshot_1](./images/screenshot_1.png) 14 | 15 | 2. **转换(map)** 16 | 17 | 把返回的数据转换成更方便处理的格式再交给 Observer。 18 | ![screenshot_2](./images/screenshot_2.png) 19 | 20 | 3. **压合(zip)** 21 | 22 | 将不同接口并行请求获取到的数据糅合在一起后再处理。 23 | ![screenshot_3](./images/screenshot_3.png) 24 | 25 | 4. **一次性 token** 26 | 27 | 需要先请求 token 再访问的接口,使用 flatMap() 将 token 的请求和实际数据的请求连贯地串起来,而不必写嵌套的 Callback 结构。 28 | ![screenshot_4](./images/screenshot_4.png) 29 | 30 | 5. **非一次性 token** 31 | 32 | 对于非一次性的 token (即可重复使用的 token),在获取 token 后将它保存起来反复使用,并通过 retryWhen() 实现 token 失效时的自动重新获取,将 token 获取的流程彻底透明化,简化开发流程。 33 | ![screenshot_5](./images/screenshot_5.png) 34 | 35 | 6. **缓存** 36 | 37 | 使用 BehaviorSubject 缓存数据。 38 | ![screenshot_6](./images/screenshot_6.png) 39 | 40 | ### apk 下载 41 | [RxJavaSamples_2.0.apk](https://github.com/rengwuxian/RxJavaSamples/releases/download/2.0/RxJavaSamples_2.0.apk) -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | //noinspection GradleCompatible 2 | apply plugin: 'com.android.application' 3 | 4 | android { 5 | compileSdkVersion 26 6 | buildToolsVersion "26.0.1" 7 | 8 | defaultConfig { 9 | applicationId "com.rengwuxian.rxjavasamples" 10 | minSdkVersion 15 11 | targetSdkVersion 26 12 | versionCode 1 13 | versionName "2.0" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(include: ['*.jar'], dir: 'libs') 25 | testCompile 'junit:junit:4.12' 26 | compile 'com.android.support:appcompat-v7:26.1.0' 27 | compile 'com.android.support:support-v4:26.1.0' 28 | compile 'com.android.support:support-v13:26.1.0' 29 | compile 'com.android.support:design:26.1.0' 30 | compile 'com.android.support:cardview-v7:26.1.0' 31 | compile 'com.squareup.okhttp3:okhttp:3.8.0' 32 | compile 'com.squareup.retrofit2:retrofit:2.3.0' 33 | compile 'com.squareup.retrofit2:converter-gson:2.3.0' 34 | compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' 35 | compile 'com.github.bumptech.glide:glide:4.1.1' 36 | annotationProcessor 'com.github.bumptech.glide:compiler:4.1.1' 37 | compile 'com.jakewharton:butterknife:8.8.1' 38 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' 39 | compile 'io.reactivex.rxjava2:rxjava:2.0.7' 40 | compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 41 | compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0' 42 | } 43 | -------------------------------------------------------------------------------- /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 | # in /Users/rengwuxian/.android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/App.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples; 4 | 5 | import android.app.Application; 6 | 7 | public class App extends Application { 8 | private static App INSTANCE; 9 | 10 | public static App getInstance() { 11 | return INSTANCE; 12 | } 13 | 14 | @Override 15 | public void onCreate() { 16 | super.onCreate(); 17 | INSTANCE = this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/BaseFragment.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples; 4 | 5 | import android.app.AlertDialog; 6 | import android.app.Fragment; 7 | 8 | import butterknife.OnClick; 9 | import io.reactivex.disposables.Disposable; 10 | 11 | public abstract class BaseFragment extends Fragment { 12 | protected Disposable disposable; 13 | 14 | @OnClick(R.id.tipBt) 15 | void tip() { 16 | new AlertDialog.Builder(getActivity()) 17 | .setTitle(getTitleRes()) 18 | .setView(getActivity().getLayoutInflater().inflate(getDialogRes(), null)) 19 | .show(); 20 | } 21 | 22 | @Override 23 | public void onDestroyView() { 24 | super.onDestroyView(); 25 | unsubscribe(); 26 | } 27 | 28 | protected void unsubscribe() { 29 | if (disposable != null && !disposable.isDisposed()) { 30 | disposable.dispose(); 31 | } 32 | } 33 | 34 | protected abstract int getDialogRes(); 35 | 36 | protected abstract int getTitleRes(); 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.rengwuxian.rxjavasamples; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.TabLayout; 5 | import android.app.Fragment; 6 | import android.support.v13.app.FragmentPagerAdapter; 7 | import android.support.v4.view.ViewPager; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.support.v7.widget.Toolbar; 10 | 11 | import com.rengwuxian.rxjavasamples.module.token_advanced_5.TokenAdvancedFragment; 12 | import com.rengwuxian.rxjavasamples.module.token_4.TokenFragment; 13 | import com.rengwuxian.rxjavasamples.module.cache_6.CacheFragment; 14 | import com.rengwuxian.rxjavasamples.module.zip_3.ZipFragment; 15 | import com.rengwuxian.rxjavasamples.module.elementary_1.ElementaryFragment; 16 | import com.rengwuxian.rxjavasamples.module.map_2.MapFragment; 17 | 18 | import butterknife.BindView; 19 | import butterknife.ButterKnife; 20 | 21 | public class MainActivity extends AppCompatActivity { 22 | @BindView(android.R.id.tabs) TabLayout tabLayout; 23 | @BindView(R.id.viewPager) ViewPager viewPager; 24 | @BindView(R.id.toolBar) Toolbar toolBar; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_main); 30 | ButterKnife.bind(this); 31 | 32 | setSupportActionBar(toolBar); 33 | 34 | viewPager.setAdapter(new FragmentPagerAdapter(getFragmentManager()) { 35 | @Override 36 | public int getCount() { 37 | return 6; 38 | } 39 | 40 | @Override 41 | public Fragment getItem(int position) { 42 | switch (position) { 43 | case 0: 44 | return new ElementaryFragment(); 45 | case 1: 46 | return new MapFragment(); 47 | case 2: 48 | return new ZipFragment(); 49 | case 3: 50 | return new TokenFragment(); 51 | case 4: 52 | return new TokenAdvancedFragment(); 53 | case 5: 54 | return new CacheFragment(); 55 | default: 56 | return new ElementaryFragment(); 57 | } 58 | } 59 | 60 | @Override 61 | public CharSequence getPageTitle(int position) { 62 | switch (position) { 63 | case 0: 64 | return getString(R.string.title_elementary); 65 | case 1: 66 | return getString(R.string.title_map); 67 | case 2: 68 | return getString(R.string.title_zip); 69 | case 3: 70 | return getString(R.string.title_token); 71 | case 4: 72 | return getString(R.string.title_token_advanced); 73 | case 5: 74 | return getString(R.string.title_cache); 75 | default: 76 | return getString(R.string.title_elementary); 77 | } 78 | } 79 | }); 80 | tabLayout.setupWithViewPager(viewPager); 81 | } 82 | } -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/adapter/ItemListAdapter.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.adapter; 4 | 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.bumptech.glide.Glide; 13 | import com.rengwuxian.rxjavasamples.R; 14 | import com.rengwuxian.rxjavasamples.model.Item; 15 | 16 | import java.util.List; 17 | 18 | import butterknife.BindView; 19 | import butterknife.ButterKnife; 20 | 21 | public class ItemListAdapter extends RecyclerView.Adapter { 22 | List images; 23 | 24 | @Override 25 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 26 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.grid_item, parent, false); 27 | return new DebounceViewHolder(view); 28 | } 29 | 30 | @Override 31 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 32 | DebounceViewHolder debounceViewHolder = (DebounceViewHolder) holder; 33 | Item image = images.get(position); 34 | Glide.with(holder.itemView.getContext()).load(image.imageUrl).into(debounceViewHolder.imageIv); 35 | debounceViewHolder.descriptionTv.setText(image.description); 36 | } 37 | 38 | @Override 39 | public int getItemCount() { 40 | return images == null ? 0 : images.size(); 41 | } 42 | 43 | public void setItems(List images) { 44 | this.images = images; 45 | notifyDataSetChanged(); 46 | } 47 | 48 | static class DebounceViewHolder extends RecyclerView.ViewHolder { 49 | @BindView(R.id.imageIv) ImageView imageIv; 50 | @BindView(R.id.descriptionTv) TextView descriptionTv; 51 | public DebounceViewHolder(View itemView) { 52 | super(itemView); 53 | ButterKnife.bind(this, itemView); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/adapter/ZhuangbiListAdapter.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.adapter; 4 | 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.bumptech.glide.Glide; 13 | import com.rengwuxian.rxjavasamples.R; 14 | import com.rengwuxian.rxjavasamples.model.ZhuangbiImage; 15 | 16 | import java.util.List; 17 | 18 | import butterknife.BindView; 19 | import butterknife.ButterKnife; 20 | 21 | public class ZhuangbiListAdapter extends RecyclerView.Adapter { 22 | List images; 23 | 24 | @Override 25 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 26 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.grid_item, parent, false); 27 | return new DebounceViewHolder(view); 28 | } 29 | 30 | @Override 31 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 32 | DebounceViewHolder debounceViewHolder = (DebounceViewHolder) holder; 33 | ZhuangbiImage image = images.get(position); 34 | Glide.with(holder.itemView.getContext()).load(image.image_url).into(debounceViewHolder.imageIv); 35 | debounceViewHolder.descriptionTv.setText(image.description); 36 | } 37 | 38 | @Override 39 | public int getItemCount() { 40 | return images == null ? 0 : images.size(); 41 | } 42 | 43 | public void setImages(List images) { 44 | this.images = images; 45 | notifyDataSetChanged(); 46 | } 47 | 48 | static class DebounceViewHolder extends RecyclerView.ViewHolder { 49 | @BindView(R.id.imageIv) ImageView imageIv; 50 | @BindView(R.id.descriptionTv) TextView descriptionTv; 51 | public DebounceViewHolder(View itemView) { 52 | super(itemView); 53 | ButterKnife.bind(this, itemView); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/model/FakeThing.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.model; 4 | 5 | public class FakeThing { 6 | public int id; 7 | public String name; 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/model/FakeToken.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.model; 4 | 5 | public class FakeToken { 6 | public String token; 7 | public boolean expired; 8 | 9 | public FakeToken() { 10 | } 11 | 12 | public FakeToken(boolean expired) { 13 | this.expired = expired; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/model/GankBeauty.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.model; 4 | 5 | public class GankBeauty { 6 | public String createdAt; 7 | public String url; 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/model/GankBeautyResult.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.model; 4 | 5 | import com.google.gson.annotations.SerializedName; 6 | 7 | import java.util.List; 8 | 9 | public class GankBeautyResult { 10 | public boolean error; 11 | public @SerializedName("results") List beauties; 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/model/Item.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.model; 4 | 5 | public class Item { 6 | public String description; 7 | public String imageUrl; 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/model/ZhuangbiImage.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.model; 4 | 5 | public class ZhuangbiImage { 6 | public String description; 7 | public String image_url; 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/module/cache_6/CacheFragment.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.module.cache_6; 4 | 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.TextView; 15 | import android.widget.Toast; 16 | 17 | import com.rengwuxian.rxjavasamples.BaseFragment; 18 | import com.rengwuxian.rxjavasamples.R; 19 | import com.rengwuxian.rxjavasamples.adapter.ItemListAdapter; 20 | import com.rengwuxian.rxjavasamples.model.Item; 21 | import com.rengwuxian.rxjavasamples.module.cache_6.data.Data; 22 | 23 | import java.util.List; 24 | 25 | import butterknife.BindView; 26 | import butterknife.ButterKnife; 27 | import butterknife.OnClick; 28 | import io.reactivex.Observer; 29 | import io.reactivex.annotations.NonNull; 30 | import io.reactivex.functions.Consumer; 31 | 32 | public class CacheFragment extends BaseFragment { 33 | @BindView(R.id.loadingTimeTv) TextView loadingTimeTv; 34 | @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout swipeRefreshLayout; 35 | @BindView(R.id.cacheRv) RecyclerView cacheRv; 36 | ItemListAdapter adapter = new ItemListAdapter(); 37 | private long startingTime; 38 | 39 | @OnClick(R.id.clearMemoryCacheBt) 40 | void clearMemoryCache() { 41 | Data.getInstance().clearMemoryCache(); 42 | adapter.setItems(null); 43 | Toast.makeText(getActivity(), R.string.memory_cache_cleared, Toast.LENGTH_SHORT).show(); 44 | } 45 | 46 | @OnClick(R.id.clearMemoryAndDiskCacheBt) 47 | void clearMemoryAndDiskCache() { 48 | Data.getInstance().clearMemoryAndDiskCache(); 49 | adapter.setItems(null); 50 | Toast.makeText(getActivity(), R.string.memory_and_disk_cache_cleared, Toast.LENGTH_SHORT).show(); 51 | } 52 | 53 | @OnClick(R.id.loadBt) 54 | void load() { 55 | swipeRefreshLayout.setRefreshing(true); 56 | startingTime = System.currentTimeMillis(); 57 | unsubscribe(); 58 | disposable = Data.getInstance() 59 | .subscribeData(new Consumer>() { 60 | @Override 61 | public void accept(@NonNull List items) throws Exception { 62 | swipeRefreshLayout.setRefreshing(false); 63 | int loadingTime = (int) (System.currentTimeMillis() - startingTime); 64 | loadingTimeTv.setText(getString(R.string.loading_time_and_source, loadingTime, Data.getInstance().getDataSourceText())); 65 | adapter.setItems(items); 66 | } 67 | }, new Consumer() { 68 | @Override 69 | public void accept(@NonNull Throwable throwable) throws Exception { 70 | throwable.printStackTrace(); 71 | swipeRefreshLayout.setRefreshing(false); 72 | Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show(); 73 | } 74 | }); 75 | } 76 | 77 | @Nullable 78 | @Override 79 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 80 | View view = inflater.inflate(R.layout.fragment_cache, container, false); 81 | ButterKnife.bind(this, view); 82 | cacheRv.setLayoutManager(new GridLayoutManager(getActivity(), 2)); 83 | cacheRv.setAdapter(adapter); 84 | swipeRefreshLayout.setColorSchemeColors(Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW); 85 | swipeRefreshLayout.setEnabled(false); 86 | return view; 87 | } 88 | 89 | @Override 90 | protected int getDialogRes() { 91 | return R.layout.dialog_cache; 92 | } 93 | 94 | @Override 95 | protected int getTitleRes() { 96 | return R.string.title_cache; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/module/cache_6/data/Data.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.module.cache_6.data; 4 | 5 | import android.support.annotation.IntDef; 6 | import android.support.annotation.NonNull; 7 | 8 | import com.rengwuxian.rxjavasamples.App; 9 | import com.rengwuxian.rxjavasamples.network.Network; 10 | import com.rengwuxian.rxjavasamples.R; 11 | import com.rengwuxian.rxjavasamples.model.Item; 12 | import com.rengwuxian.rxjavasamples.util.GankBeautyResultToItemsMapper; 13 | 14 | import java.util.List; 15 | 16 | import io.reactivex.Observable; 17 | import io.reactivex.ObservableEmitter; 18 | import io.reactivex.ObservableOnSubscribe; 19 | import io.reactivex.Observer; 20 | import io.reactivex.android.schedulers.AndroidSchedulers; 21 | import io.reactivex.disposables.Disposable; 22 | import io.reactivex.functions.Consumer; 23 | import io.reactivex.observers.DefaultObserver; 24 | import io.reactivex.schedulers.Schedulers; 25 | import io.reactivex.subjects.BehaviorSubject; 26 | 27 | public class Data { 28 | private static Data instance; 29 | private static final int DATA_SOURCE_MEMORY = 1; 30 | private static final int DATA_SOURCE_DISK = 2; 31 | private static final int DATA_SOURCE_NETWORK = 3; 32 | 33 | @IntDef({DATA_SOURCE_MEMORY, DATA_SOURCE_DISK, DATA_SOURCE_NETWORK}) 34 | @interface DataSource { 35 | } 36 | 37 | BehaviorSubject> cache; 38 | 39 | private int dataSource; 40 | 41 | private Data() { 42 | } 43 | 44 | public static Data getInstance() { 45 | if (instance == null) { 46 | instance = new Data(); 47 | } 48 | return instance; 49 | } 50 | 51 | private void setDataSource(@DataSource int dataSource) { 52 | this.dataSource = dataSource; 53 | } 54 | 55 | public String getDataSourceText() { 56 | int dataSourceTextRes; 57 | switch (dataSource) { 58 | case DATA_SOURCE_MEMORY: 59 | dataSourceTextRes = R.string.data_source_memory; 60 | break; 61 | case DATA_SOURCE_DISK: 62 | dataSourceTextRes = R.string.data_source_disk; 63 | break; 64 | case DATA_SOURCE_NETWORK: 65 | dataSourceTextRes = R.string.data_source_network; 66 | break; 67 | default: 68 | dataSourceTextRes = R.string.data_source_network; 69 | } 70 | return App.getInstance().getString(dataSourceTextRes); 71 | } 72 | 73 | public void loadFromNetwork() { 74 | Network.getGankApi() 75 | .getBeauties(100, 1) 76 | .subscribeOn(Schedulers.io()) 77 | .map(GankBeautyResultToItemsMapper.getInstance()) 78 | .doOnNext(new Consumer>() { 79 | @Override 80 | public void accept(List items) { 81 | Database.getInstance().writeItems(items); 82 | } 83 | }) 84 | .subscribe(new Consumer>() { 85 | @Override 86 | public void accept(List items) { 87 | cache.onNext(items); 88 | } 89 | }, new Consumer() { 90 | @Override 91 | public void accept(Throwable throwable) { 92 | throwable.printStackTrace(); 93 | cache.onError(throwable); 94 | } 95 | }); 96 | } 97 | 98 | public Disposable subscribeData(@NonNull Consumer> onNext, @NonNull Consumer onError) { 99 | if (cache == null) { 100 | cache = BehaviorSubject.create(); 101 | Observable.create(new ObservableOnSubscribe>() { 102 | @Override 103 | public void subscribe(ObservableEmitter> e) throws Exception { 104 | List items = Database.getInstance().readItems(); 105 | if (items == null) { 106 | setDataSource(DATA_SOURCE_NETWORK); 107 | loadFromNetwork(); 108 | } else { 109 | setDataSource(DATA_SOURCE_DISK); 110 | e.onNext(items); 111 | } 112 | } 113 | }) 114 | .subscribeOn(Schedulers.io()) 115 | .subscribe(cache); 116 | } else { 117 | setDataSource(DATA_SOURCE_MEMORY); 118 | } 119 | return cache.doOnError(new Consumer() { 120 | @Override 121 | public void accept(@io.reactivex.annotations.NonNull Throwable throwable) throws Exception { 122 | cache = null; 123 | } 124 | }) 125 | .observeOn(AndroidSchedulers.mainThread()) 126 | .subscribe(onNext, onError); 127 | } 128 | 129 | public void clearMemoryCache() { 130 | cache = null; 131 | } 132 | 133 | public void clearMemoryAndDiskCache() { 134 | clearMemoryCache(); 135 | Database.getInstance().delete(); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/module/cache_6/data/Database.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.module.cache_6.data; 4 | 5 | import com.google.gson.Gson; 6 | import com.google.gson.reflect.TypeToken; 7 | import com.rengwuxian.rxjavasamples.App; 8 | import com.rengwuxian.rxjavasamples.model.Item; 9 | 10 | import java.io.File; 11 | import java.io.FileNotFoundException; 12 | import java.io.FileReader; 13 | import java.io.FileWriter; 14 | import java.io.IOException; 15 | import java.io.Reader; 16 | import java.io.Writer; 17 | import java.util.List; 18 | 19 | public class Database { 20 | private static String DATA_FILE_NAME = "data.db"; 21 | 22 | private static Database INSTANCE; 23 | 24 | File dataFile = new File(App.getInstance().getFilesDir(), DATA_FILE_NAME); 25 | Gson gson = new Gson(); 26 | 27 | private Database() { 28 | } 29 | 30 | public static Database getInstance() { 31 | if (INSTANCE == null) { 32 | INSTANCE = new Database(); 33 | } 34 | return INSTANCE; 35 | } 36 | 37 | public List readItems() { 38 | // Hard code adding some delay, to distinguish reading from memory and reading disk clearly 39 | try { 40 | Thread.sleep(100); 41 | } catch (InterruptedException e) { 42 | e.printStackTrace(); 43 | } 44 | 45 | try { 46 | Reader reader = new FileReader(dataFile); 47 | return gson.fromJson(reader, new TypeToken>(){}.getType()); 48 | } catch (FileNotFoundException e) { 49 | e.printStackTrace(); 50 | return null; 51 | } 52 | } 53 | 54 | public void writeItems(List items) { 55 | String json = gson.toJson(items); 56 | try { 57 | if (!dataFile.exists()) { 58 | try { 59 | dataFile.createNewFile(); 60 | } catch (IOException e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | Writer writer = new FileWriter(dataFile); 65 | writer.write(json); 66 | writer.flush(); 67 | } catch (IOException e) { 68 | e.printStackTrace(); 69 | } 70 | } 71 | 72 | public void delete() { 73 | dataFile.delete(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/module/elementary_1/ElementaryFragment.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.module.elementary_1; 4 | 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.RadioButton; 15 | import android.widget.Toast; 16 | 17 | import com.rengwuxian.rxjavasamples.BaseFragment; 18 | import com.rengwuxian.rxjavasamples.network.Network; 19 | import com.rengwuxian.rxjavasamples.R; 20 | import com.rengwuxian.rxjavasamples.adapter.ZhuangbiListAdapter; 21 | import com.rengwuxian.rxjavasamples.model.ZhuangbiImage; 22 | 23 | import java.util.List; 24 | 25 | import butterknife.BindView; 26 | import butterknife.ButterKnife; 27 | import butterknife.OnCheckedChanged; 28 | import io.reactivex.Observer; 29 | import io.reactivex.android.schedulers.AndroidSchedulers; 30 | import io.reactivex.annotations.NonNull; 31 | import io.reactivex.functions.Consumer; 32 | import io.reactivex.schedulers.Schedulers; 33 | 34 | public class ElementaryFragment extends BaseFragment { 35 | @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout swipeRefreshLayout; 36 | @BindView(R.id.gridRv) RecyclerView gridRv; 37 | 38 | ZhuangbiListAdapter adapter = new ZhuangbiListAdapter(); 39 | 40 | @OnCheckedChanged({R.id.searchRb1, R.id.searchRb2, R.id.searchRb3, R.id.searchRb4}) 41 | void onTagChecked(RadioButton searchRb, boolean checked) { 42 | if (checked) { 43 | unsubscribe(); 44 | adapter.setImages(null); 45 | swipeRefreshLayout.setRefreshing(true); 46 | search(searchRb.getText().toString()); 47 | } 48 | } 49 | 50 | private void search(String key) { 51 | disposable = Network.getZhuangbiApi() 52 | .search(key) 53 | .subscribeOn(Schedulers.io()) 54 | .observeOn(AndroidSchedulers.mainThread()) 55 | .subscribe(new Consumer>() { 56 | @Override 57 | public void accept(@NonNull List images) throws Exception { 58 | swipeRefreshLayout.setRefreshing(false); 59 | adapter.setImages(images); 60 | } 61 | }, new Consumer() { 62 | @Override 63 | public void accept(@NonNull Throwable throwable) throws Exception { 64 | swipeRefreshLayout.setRefreshing(false); 65 | Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show(); 66 | } 67 | }); 68 | } 69 | 70 | @Nullable 71 | @Override 72 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 73 | View view = inflater.inflate(R.layout.fragment_elementary, container, false); 74 | ButterKnife.bind(this, view); 75 | 76 | gridRv.setLayoutManager(new GridLayoutManager(getActivity(), 2)); 77 | gridRv.setAdapter(adapter); 78 | swipeRefreshLayout.setColorSchemeColors(Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW); 79 | swipeRefreshLayout.setEnabled(false); 80 | 81 | return view; 82 | } 83 | 84 | @Override 85 | protected int getDialogRes() { 86 | return R.layout.dialog_elementary; 87 | } 88 | 89 | @Override 90 | protected int getTitleRes() { 91 | return R.string.title_elementary; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/module/map_2/MapFragment.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.module.map_2; 4 | 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.RecyclerView; 10 | import android.support.v7.widget.StaggeredGridLayoutManager; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.Button; 15 | import android.widget.TextView; 16 | import android.widget.Toast; 17 | 18 | import com.rengwuxian.rxjavasamples.BaseFragment; 19 | import com.rengwuxian.rxjavasamples.network.Network; 20 | import com.rengwuxian.rxjavasamples.R; 21 | import com.rengwuxian.rxjavasamples.adapter.ItemListAdapter; 22 | import com.rengwuxian.rxjavasamples.model.Item; 23 | import com.rengwuxian.rxjavasamples.util.GankBeautyResultToItemsMapper; 24 | 25 | import java.util.List; 26 | 27 | import butterknife.BindView; 28 | import butterknife.ButterKnife; 29 | import butterknife.OnClick; 30 | import io.reactivex.Observer; 31 | import io.reactivex.android.schedulers.AndroidSchedulers; 32 | import io.reactivex.annotations.NonNull; 33 | import io.reactivex.functions.Consumer; 34 | import io.reactivex.schedulers.Schedulers; 35 | 36 | public class MapFragment extends BaseFragment { 37 | private int page = 0; 38 | 39 | @BindView(R.id.pageTv) TextView pageTv; 40 | @BindView(R.id.previousPageBt) Button previousPageBt; 41 | @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout swipeRefreshLayout; 42 | @BindView(R.id.gridRv) RecyclerView gridRv; 43 | 44 | ItemListAdapter adapter = new ItemListAdapter(); 45 | 46 | @OnClick(R.id.previousPageBt) 47 | void previousPage() { 48 | loadPage(--page); 49 | if (page == 1) { 50 | previousPageBt.setEnabled(false); 51 | } 52 | } 53 | 54 | @OnClick(R.id.nextPageBt) 55 | void nextPage() { 56 | loadPage(++page); 57 | if (page == 2) { 58 | previousPageBt.setEnabled(true); 59 | } 60 | } 61 | 62 | private void loadPage(int page) { 63 | swipeRefreshLayout.setRefreshing(true); 64 | unsubscribe(); 65 | disposable = Network.getGankApi() 66 | .getBeauties(10, page) 67 | .map(GankBeautyResultToItemsMapper.getInstance()) 68 | .subscribeOn(Schedulers.io()) 69 | .observeOn(AndroidSchedulers.mainThread()) 70 | .subscribe(new Consumer>() { 71 | @Override 72 | public void accept(@NonNull List items) throws Exception { 73 | swipeRefreshLayout.setRefreshing(false); 74 | pageTv.setText(getString(R.string.page_with_number, MapFragment.this.page)); 75 | adapter.setItems(items); 76 | } 77 | }, new Consumer() { 78 | @Override 79 | public void accept(@NonNull Throwable throwable) throws Exception { 80 | swipeRefreshLayout.setRefreshing(false); 81 | Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show(); 82 | } 83 | }); 84 | } 85 | 86 | @Nullable 87 | @Override 88 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 89 | View view = inflater.inflate(R.layout.fragment_map, container, false); 90 | ButterKnife.bind(this, view); 91 | 92 | gridRv.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)); 93 | gridRv.setAdapter(adapter); 94 | swipeRefreshLayout.setColorSchemeColors(Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW); 95 | swipeRefreshLayout.setEnabled(false); 96 | return view; 97 | } 98 | 99 | @Override 100 | protected int getDialogRes() { 101 | return R.layout.dialog_map; 102 | } 103 | 104 | @Override 105 | protected int getTitleRes() { 106 | return R.string.title_map; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/module/token_4/TokenFragment.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.module.token_4; 4 | 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.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.TextView; 13 | import android.widget.Toast; 14 | 15 | import com.rengwuxian.rxjavasamples.BaseFragment; 16 | import com.rengwuxian.rxjavasamples.network.Network; 17 | import com.rengwuxian.rxjavasamples.R; 18 | import com.rengwuxian.rxjavasamples.network.api.FakeApi; 19 | import com.rengwuxian.rxjavasamples.model.FakeThing; 20 | import com.rengwuxian.rxjavasamples.model.FakeToken; 21 | 22 | import butterknife.BindView; 23 | import butterknife.ButterKnife; 24 | import butterknife.OnClick; 25 | import io.reactivex.Observable; 26 | import io.reactivex.android.schedulers.AndroidSchedulers; 27 | import io.reactivex.functions.Consumer; 28 | import io.reactivex.functions.Function; 29 | import io.reactivex.schedulers.Schedulers; 30 | 31 | public class TokenFragment extends BaseFragment { 32 | 33 | @BindView(R.id.tokenTv) TextView tokenTv; 34 | @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout swipeRefreshLayout; 35 | 36 | @OnClick(R.id.requestBt) 37 | void upload() { 38 | swipeRefreshLayout.setRefreshing(true); 39 | unsubscribe(); 40 | final FakeApi fakeApi = Network.getFakeApi(); 41 | disposable = fakeApi.getFakeToken("fake_auth_code") 42 | .flatMap(new Function>() { 43 | @Override 44 | public Observable apply(FakeToken fakeToken) { 45 | return fakeApi.getFakeData(fakeToken); 46 | } 47 | }) 48 | .subscribeOn(Schedulers.io()) 49 | .observeOn(AndroidSchedulers.mainThread()) 50 | .subscribe(new Consumer() { 51 | @Override 52 | public void accept(FakeThing fakeData) { 53 | swipeRefreshLayout.setRefreshing(false); 54 | tokenTv.setText(getString(R.string.got_data, fakeData.id, fakeData.name)); 55 | } 56 | }, new Consumer() { 57 | @Override 58 | public void accept(Throwable throwable) { 59 | swipeRefreshLayout.setRefreshing(false); 60 | Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show(); 61 | } 62 | }); 63 | } 64 | 65 | @Nullable 66 | @Override 67 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 68 | View view = inflater.inflate(R.layout.fragment_token, container, false); 69 | ButterKnife.bind(this, view); 70 | swipeRefreshLayout.setColorSchemeColors(Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW); 71 | swipeRefreshLayout.setEnabled(false); 72 | return view; 73 | } 74 | 75 | @Override 76 | protected int getDialogRes() { 77 | return R.layout.dialog_token; 78 | } 79 | 80 | @Override 81 | protected int getTitleRes() { 82 | return R.string.title_token; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/module/token_advanced_5/TokenAdvancedFragment.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.module.token_advanced_5; 4 | 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.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.TextView; 13 | import android.widget.Toast; 14 | 15 | import com.rengwuxian.rxjavasamples.BaseFragment; 16 | import com.rengwuxian.rxjavasamples.network.Network; 17 | import com.rengwuxian.rxjavasamples.R; 18 | import com.rengwuxian.rxjavasamples.network.api.FakeApi; 19 | import com.rengwuxian.rxjavasamples.model.FakeThing; 20 | import com.rengwuxian.rxjavasamples.model.FakeToken; 21 | 22 | import butterknife.BindView; 23 | import butterknife.ButterKnife; 24 | import butterknife.OnClick; 25 | import io.reactivex.Observable; 26 | import io.reactivex.android.schedulers.AndroidSchedulers; 27 | import io.reactivex.functions.Consumer; 28 | import io.reactivex.functions.Function; 29 | import io.reactivex.schedulers.Schedulers; 30 | 31 | public class TokenAdvancedFragment extends BaseFragment { 32 | 33 | @BindView(R.id.tokenTv) TextView tokenTv; 34 | @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout swipeRefreshLayout; 35 | final FakeToken cachedFakeToken = new FakeToken(true); 36 | boolean tokenUpdated; 37 | 38 | @OnClick(R.id.invalidateTokenBt) 39 | void invalidateToken() { 40 | cachedFakeToken.expired = true; 41 | Toast.makeText(getActivity(), R.string.token_destroyed, Toast.LENGTH_SHORT).show(); 42 | } 43 | 44 | @OnClick(R.id.requestBt) 45 | void upload() { 46 | tokenUpdated = false; 47 | swipeRefreshLayout.setRefreshing(true); 48 | unsubscribe(); 49 | final FakeApi fakeApi = Network.getFakeApi(); 50 | disposable = Observable.just(1) 51 | .flatMap(new Function>() { 52 | @Override 53 | public Observable apply(Object o) { 54 | return cachedFakeToken.token == null 55 | ? Observable.error(new NullPointerException("Token is null!")) 56 | : fakeApi.getFakeData(cachedFakeToken); 57 | } 58 | }) 59 | .retryWhen(new Function, Observable>() { 60 | @Override 61 | public Observable apply(Observable observable) { 62 | return observable.flatMap(new Function>() { 63 | @Override 64 | public Observable apply(Throwable throwable) { 65 | if (throwable instanceof IllegalArgumentException || throwable instanceof NullPointerException) { 66 | return fakeApi.getFakeToken("fake_auth_code") 67 | .doOnNext(new Consumer() { 68 | @Override 69 | public void accept(FakeToken fakeToken) { 70 | tokenUpdated = true; 71 | cachedFakeToken.token = fakeToken.token; 72 | cachedFakeToken.expired = fakeToken.expired; 73 | } 74 | }); 75 | } 76 | return Observable.error(throwable); 77 | } 78 | }); 79 | } 80 | }) 81 | .subscribeOn(Schedulers.io()) 82 | .observeOn(AndroidSchedulers.mainThread()) 83 | .subscribe(new Consumer() { 84 | @Override 85 | public void accept(FakeThing fakeData) { 86 | swipeRefreshLayout.setRefreshing(false); 87 | String token = cachedFakeToken.token; 88 | if (tokenUpdated) { 89 | token += "(" + getString(R.string.updated) + ")"; 90 | } 91 | tokenTv.setText(getString(R.string.got_token_and_data, token, fakeData.id, fakeData.name)); 92 | } 93 | }, new Consumer() { 94 | @Override 95 | public void accept(Throwable throwable) { 96 | swipeRefreshLayout.setRefreshing(false); 97 | Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show(); 98 | } 99 | }); 100 | } 101 | 102 | @Nullable 103 | @Override 104 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 105 | View view = inflater.inflate(R.layout.fragment_token_advanced, container, false); 106 | ButterKnife.bind(this, view); 107 | swipeRefreshLayout.setColorSchemeColors(Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW); 108 | swipeRefreshLayout.setEnabled(false); 109 | return view; 110 | } 111 | 112 | @Override 113 | protected int getDialogRes() { 114 | return R.layout.dialog_token_advanced; 115 | } 116 | 117 | @Override 118 | protected int getTitleRes() { 119 | return R.string.title_token_advanced; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/module/zip_3/ZipFragment.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.module.zip_3; 4 | 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.rengwuxian.rxjavasamples.BaseFragment; 17 | import com.rengwuxian.rxjavasamples.network.Network; 18 | import com.rengwuxian.rxjavasamples.R; 19 | import com.rengwuxian.rxjavasamples.adapter.ItemListAdapter; 20 | import com.rengwuxian.rxjavasamples.model.Item; 21 | import com.rengwuxian.rxjavasamples.model.ZhuangbiImage; 22 | import com.rengwuxian.rxjavasamples.util.GankBeautyResultToItemsMapper; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | import butterknife.BindView; 28 | import butterknife.ButterKnife; 29 | import butterknife.OnClick; 30 | import io.reactivex.Observable; 31 | import io.reactivex.Observer; 32 | import io.reactivex.android.schedulers.AndroidSchedulers; 33 | import io.reactivex.annotations.NonNull; 34 | import io.reactivex.functions.BiFunction; 35 | import io.reactivex.functions.Consumer; 36 | import io.reactivex.schedulers.Schedulers; 37 | 38 | public class ZipFragment extends BaseFragment { 39 | @BindView(R.id.gridRv) RecyclerView gridRv; 40 | @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout swipeRefreshLayout; 41 | ItemListAdapter adapter = new ItemListAdapter(); 42 | 43 | @OnClick(R.id.zipLoadBt) 44 | void load() { 45 | swipeRefreshLayout.setRefreshing(true); 46 | unsubscribe(); 47 | disposable = Observable.zip(Network.getGankApi().getBeauties(200, 1).map(GankBeautyResultToItemsMapper.getInstance()), 48 | Network.getZhuangbiApi().search("装逼"), 49 | new BiFunction, List, List>() { 50 | @Override 51 | public List apply(List gankItems, List zhuangbiImages) { 52 | List items = new ArrayList(); 53 | for (int i = 0; i < gankItems.size() / 2 && i < zhuangbiImages.size(); i++) { 54 | items.add(gankItems.get(i * 2)); 55 | items.add(gankItems.get(i * 2 + 1)); 56 | Item zhuangbiItem = new Item(); 57 | ZhuangbiImage zhuangbiImage = zhuangbiImages.get(i); 58 | zhuangbiItem.description = zhuangbiImage.description; 59 | zhuangbiItem.imageUrl = zhuangbiImage.image_url; 60 | items.add(zhuangbiItem); 61 | } 62 | return items; 63 | } 64 | }) 65 | .subscribeOn(Schedulers.io()) 66 | .observeOn(AndroidSchedulers.mainThread()) 67 | .subscribe(new Consumer>() { 68 | @Override 69 | public void accept(@NonNull List items) throws Exception { 70 | swipeRefreshLayout.setRefreshing(false); 71 | adapter.setItems(items); 72 | } 73 | }, new Consumer() { 74 | @Override 75 | public void accept(@NonNull Throwable throwable) throws Exception { 76 | swipeRefreshLayout.setRefreshing(false); 77 | Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show(); 78 | } 79 | }); 80 | } 81 | 82 | @Nullable 83 | @Override 84 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 85 | View view = inflater.inflate(R.layout.fragment_zip, container, false); 86 | ButterKnife.bind(this, view); 87 | 88 | gridRv.setLayoutManager(new GridLayoutManager(getActivity(), 2)); 89 | gridRv.setAdapter(adapter); 90 | swipeRefreshLayout.setColorSchemeColors(Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW); 91 | swipeRefreshLayout.setEnabled(false); 92 | return view; 93 | } 94 | 95 | 96 | @Override 97 | protected int getDialogRes() { 98 | return R.layout.dialog_zip; 99 | } 100 | 101 | @Override 102 | protected int getTitleRes() { 103 | return R.string.title_zip; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/network/Network.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.network; 4 | 5 | import com.rengwuxian.rxjavasamples.network.api.FakeApi; 6 | import com.rengwuxian.rxjavasamples.network.api.GankApi; 7 | import com.rengwuxian.rxjavasamples.network.api.ZhuangbiApi; 8 | 9 | import okhttp3.OkHttpClient; 10 | import retrofit2.CallAdapter; 11 | import retrofit2.Converter; 12 | import retrofit2.Retrofit; 13 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 14 | import retrofit2.converter.gson.GsonConverterFactory; 15 | 16 | public class Network { 17 | private static ZhuangbiApi zhuangbiApi; 18 | private static GankApi gankApi; 19 | private static FakeApi fakeApi; 20 | private static OkHttpClient okHttpClient = new OkHttpClient(); 21 | private static Converter.Factory gsonConverterFactory = GsonConverterFactory.create(); 22 | private static CallAdapter.Factory rxJavaCallAdapterFactory = RxJava2CallAdapterFactory.create(); 23 | 24 | public static ZhuangbiApi getZhuangbiApi() { 25 | if (zhuangbiApi == null) { 26 | Retrofit retrofit = new Retrofit.Builder() 27 | .client(okHttpClient) 28 | .baseUrl("http://www.zhuangbi.info/") 29 | .addConverterFactory(gsonConverterFactory) 30 | .addCallAdapterFactory(rxJavaCallAdapterFactory) 31 | .build(); 32 | zhuangbiApi = retrofit.create(ZhuangbiApi.class); 33 | } 34 | return zhuangbiApi; 35 | } 36 | 37 | public static GankApi getGankApi() { 38 | if (gankApi == null) { 39 | Retrofit retrofit = new Retrofit.Builder() 40 | .client(okHttpClient) 41 | .baseUrl("http://gank.io/api/") 42 | .addConverterFactory(gsonConverterFactory) 43 | .addCallAdapterFactory(rxJavaCallAdapterFactory) 44 | .build(); 45 | gankApi = retrofit.create(GankApi.class); 46 | } 47 | return gankApi; 48 | } 49 | 50 | public static FakeApi getFakeApi() { 51 | if (fakeApi == null) { 52 | fakeApi = new FakeApi(); 53 | } 54 | return fakeApi; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/network/api/FakeApi.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.network.api; 4 | 5 | import android.support.annotation.NonNull; 6 | 7 | import com.rengwuxian.rxjavasamples.model.FakeThing; 8 | import com.rengwuxian.rxjavasamples.model.FakeToken; 9 | 10 | import java.util.Random; 11 | 12 | import io.reactivex.Observable; 13 | import io.reactivex.functions.Function; 14 | 15 | public class FakeApi { 16 | Random random = new Random(); 17 | 18 | public Observable getFakeToken(@NonNull String fakeAuth) { 19 | return Observable.just(fakeAuth) 20 | .map(new Function() { 21 | @Override 22 | public FakeToken apply(String fakeAuth) { 23 | // Add some random delay to mock the network delay 24 | int fakeNetworkTimeCost = random.nextInt(500) + 500; 25 | try { 26 | Thread.sleep(fakeNetworkTimeCost); 27 | } catch (InterruptedException e) { 28 | e.printStackTrace(); 29 | } 30 | 31 | FakeToken fakeToken = new FakeToken(); 32 | fakeToken.token = createToken(); 33 | return fakeToken; 34 | } 35 | }); 36 | } 37 | 38 | private static String createToken() { 39 | return "fake_token_" + System.currentTimeMillis() % 10000; 40 | } 41 | 42 | public Observable getFakeData(FakeToken fakeToken) { 43 | return Observable.just(fakeToken) 44 | .map(new Function() { 45 | @Override 46 | public FakeThing apply(FakeToken fakeToken) { 47 | // Add some random delay to mock the network delay 48 | int fakeNetworkTimeCost = random.nextInt(500) + 500; 49 | try { 50 | Thread.sleep(fakeNetworkTimeCost); 51 | } catch (InterruptedException e) { 52 | e.printStackTrace(); 53 | } 54 | 55 | if (fakeToken.expired) { 56 | throw new IllegalArgumentException("Token expired!"); 57 | } 58 | 59 | FakeThing fakeData = new FakeThing(); 60 | fakeData.id = (int) (System.currentTimeMillis() % 1000); 61 | fakeData.name = "FAKE_USER_" + fakeData.id; 62 | return fakeData; 63 | } 64 | }); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/network/api/GankApi.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.network.api; 4 | 5 | import com.rengwuxian.rxjavasamples.model.GankBeautyResult; 6 | 7 | import retrofit2.http.GET; 8 | import retrofit2.http.Path; 9 | import io.reactivex.Observable; 10 | 11 | public interface GankApi { 12 | @GET("data/福利/{number}/{page}") 13 | Observable getBeauties(@Path("number") int number, @Path("page") int page); 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/network/api/ZhuangbiApi.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.network.api; 4 | 5 | import com.rengwuxian.rxjavasamples.model.ZhuangbiImage; 6 | 7 | import java.util.List; 8 | 9 | import retrofit2.http.GET; 10 | import retrofit2.http.Query; 11 | import io.reactivex.Observable; 12 | 13 | public interface ZhuangbiApi { 14 | @GET("search") 15 | Observable> search(@Query("q") String query); 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/rengwuxian/rxjavasamples/util/GankBeautyResultToItemsMapper.java: -------------------------------------------------------------------------------- 1 | // (c)2016 Flipboard Inc, All Rights Reserved. 2 | 3 | package com.rengwuxian.rxjavasamples.util; 4 | 5 | import com.rengwuxian.rxjavasamples.model.GankBeauty; 6 | import com.rengwuxian.rxjavasamples.model.GankBeautyResult; 7 | import com.rengwuxian.rxjavasamples.model.Item; 8 | 9 | import java.text.ParseException; 10 | import java.text.SimpleDateFormat; 11 | import java.util.ArrayList; 12 | import java.util.Date; 13 | import java.util.List; 14 | 15 | import io.reactivex.functions.Function; 16 | 17 | public class GankBeautyResultToItemsMapper implements Function> { 18 | private static GankBeautyResultToItemsMapper INSTANCE = new GankBeautyResultToItemsMapper(); 19 | 20 | private GankBeautyResultToItemsMapper() { 21 | } 22 | 23 | public static GankBeautyResultToItemsMapper getInstance() { 24 | return INSTANCE; 25 | } 26 | 27 | @Override 28 | public List apply(GankBeautyResult gankBeautyResult) { 29 | List gankBeauties = gankBeautyResult.beauties; 30 | List items = new ArrayList<>(gankBeauties.size()); 31 | SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SS'Z'"); 32 | SimpleDateFormat outputFormat = new SimpleDateFormat("yy/MM/dd HH:mm:ss"); 33 | for (GankBeauty gankBeauty : gankBeauties) { 34 | Item item = new Item(); 35 | try { 36 | Date date = inputFormat.parse(gankBeauty.createdAt); 37 | item.description = outputFormat.format(date); 38 | } catch (ParseException e) { 39 | e.printStackTrace(); 40 | item.description = "unknown date"; 41 | } 42 | item.imageUrl = gankBeauty.url; 43 | items.add(item); 44 | } 45 | return items; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/upload.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rengwuxian/RxJavaSamples/f9e47c2e8347f124bc03ebe10e758f26a9b841b8/app/src/main/res/drawable/upload.jpg -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 20 | 21 | 26 | 27 | 28 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_cache.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_elementary.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_map.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_token.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_token_advanced.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_zip.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_cache.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 34 | 35 | 39 | 40 | 41 | 42 | 48 | 49 | 54 | 55 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_elementary.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 17 | 18 | 23 | 24 | 29 | 30 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 52 | 53 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_map.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | 19 | 25 | 26 | 33 | 34 | 35 | 36 | 37 | 38 | 42 | 43 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_token.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 16 | 17 | 22 | 23 | 24 | 25 |