RxJava,Retrofit,OkHttp3在項目中結合使用

簡單暴力:直接上代碼,清晰明瞭

代碼結構

- activitys - MainActivity.Java

- fragment - BuyGoodFragment.java

- network - factory - MyCustomFactory.java

- network - factory - UserResponseConverter.java

- network - manager - MyRetrofitConverter.java

- network - service - JtmlServer.java

亮出代碼

  1.   
  2. import java.lang.annotation.Annotation;  
  3. import java.lang.reflect.Type;  
  4.   
  5. import okhttp3.ResponseBody;  
  6. import retrofit2.Converter;  
  7. import retrofit2.Retrofit;  
  8.   

  9. public class MyCustomFactory extends Converter.Factory  
  10. {  
  11.     @Override  
  12.     public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit)  
  13.     {  
  14.         //根據type判斷是否是自己能處理的類型,不能的話,return null ,交給後面的Converter.Factory  
  15.         return new UserResponseConverter(type);  
  16.     }  
  17.   
  18. }  



  1.   
  2. import com.google.gson.Gson;  
  3.   
  4. import java.io.IOException;  
  5. import java.lang.reflect.Type;  
  6.   
  7. import okhttp3.ResponseBody;  
  8. import retrofit2.Converter;  
  9.   

  10.   
  11. public class UserResponseConverter<T> implements Converter<ResponseBody, T>  
  12. {  
  13.     private Type type;  
  14.     Gson gson = new Gson();  
  15.   
  16.     public UserResponseConverter(Type type) {  
  17.         this.type = type;  
  18.     }  
  19.   
  20.     @Override  
  21.     public T convert(ResponseBody responseBody) throws IOException {  
  22.         String result = responseBody.string();  
  23.         T users = gson.fromJson(result, type);  
  24.         return users;  
  25.     }  
  26. }  

  1. import java.io.File;  
  2. import java.io.IOException;  
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import java.util.concurrent.TimeUnit;  
  6.   
  7. import okhttp3.Cache;  
  8. import okhttp3.CacheControl;  
  9. import okhttp3.Interceptor;  
  10. import okhttp3.OkHttpClient;  
  11. import okhttp3.Request;  
  12. import okhttp3.Response;  
  13. import okhttp3.logging.HttpLoggingInterceptor;  
  14. import retrofit2.Retrofit;  
  15. import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;  
  16. import retrofit2.converter.gson.GsonConverterFactory;  
  17. import rx.Observable;  
  18.   
  19. /** 
  20.  */  
  21. public class MyRetrofitManager {  
  22.   
  23.     public static final String BASE_NIUPAI_URL   = "private url1";  
  24.     public static final String BASE_IMAGE_URL    = "private url2";  
  25.     //短緩存有效期爲1秒鐘  
  26.     public static final int    CACHE_STALE_SHORT = 1;  
  27.     //長緩存有效期爲7天  
  28.     public static final int    CACHE_STALE_LONG  = 60 * 60 * 24 * 7;  
  29.   
  30.     public static final String CACHE_CONTROL_AGE = "Cache-Control: public, max-age=";  
  31.   
  32.     //查詢緩存的Cache-Control設置,爲if-only-cache時只查詢緩存而不會請求服務器,max-stale可以配合設置緩存失效時間  
  33.     public static final String CACHE_CONTROL_CACHE   = "only-if-cached, max-stale=" + CACHE_STALE_LONG;  
  34.     //查詢網絡的Cache-Control設置,頭部Cache-Control設爲max-age=0時則不會使用緩存而請求服務器  
  35.     public static final String CACHE_CONTROL_NETWORK = "max-age=0";  
  36.     private static OkHttpClient mOkHttpClient;  
  37.     private final  JtmlServer   mJtmlService;  
  38.   
  39.     public static MyRetrofitManager builder() {  
  40.         return new MyRetrofitManager();  
  41.     }  
  42.   
  43.     private MyRetrofitManager() {  
  44.   
  45.         initOkHttpClient();  
  46.   
  47.         Retrofit retrofit = new Retrofit.Builder()  
  48.                 .baseUrl(BASE_NIUPAI_URL)  
  49.                 .client(mOkHttpClient)  
  50.                 .addCallAdapterFactory(RxJavaCallAdapterFactory.create())  
  51. //                .addConverterFactory(new MyCustomFactory())  
  52.                 .addConverterFactory(GsonConverterFactory.create())  
  53.                 .build();  
  54.         mJtmlService = retrofit.create(JtmlServer.class);  
  55.     }  
  56.   
  57.     private void initOkHttpClient() {  
  58.         HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();  
  59.         interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);  
  60.         if (mOkHttpClient == null) {  
  61.             synchronized (MyRetrofitManager.class) {  
  62.                 if (mOkHttpClient == null) {  
  63.   
  64.                     // 指定緩存路徑,緩存大小100Mb  
  65.                     Cache cache = new Cache(new File(App.getContext().getCacheDir(), "HttpCache"),  
  66.                             1024 * 1024 * 100);  
  67.   
  68.                     mOkHttpClient = new OkHttpClient.Builder()  
  69.                             .cache(cache)  
  70.                             .addInterceptor(mRewriteCacheControlInterceptor)  
  71.                             .addNetworkInterceptor(mRewriteCacheControlInterceptor)  
  72.                             .addInterceptor(interceptor)  
  73.                             .retryOnConnectionFailure(true)  
  74.                             .connectTimeout(15, TimeUnit.SECONDS)  
  75.                             .build();  
  76.                 }  
  77.             }  
  78.         }  
  79.     }  
  80.   
  81.     // 雲端響應頭攔截器,用來配置緩存策略  
  82.     private Interceptor mRewriteCacheControlInterceptor = new Interceptor() {  
  83.         @Override  
  84.         public Response intercept(Chain chain) throws IOException {  
  85.             Request request = chain.request();  
  86.             if (!NetUtil.isNetworkConnected()) {  
  87.                 request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();  
  88.             }  
  89.             Response originalResponse = chain.proceed(request);  
  90.             if (NetUtil.isNetworkConnected()) {  
  91.                 //有網的時候讀接口上的@Headers裏的配置,你可以在這裏進行統一的設置  
  92.                 String cacheControl = request.cacheControl().toString();  
  93.                 return originalResponse.newBuilder().header("Cache-Control", cacheControl).removeHeader("Pragma").build();  
  94.             } else {  
  95.                 return originalResponse.newBuilder().header("Cache-Control""public, only-if-cached, max-stale=" + CACHE_STALE_LONG).removeHeader("Pragma").build();  
  96.             }  
  97.         }  
  98.     };  
  99.   
  100.     public Observable<List<Comment>> getComments() {  
  101.         return mJtmlService.getComments();  
  102.     }  
  103.   
  104.     public Observable<List<Notifications>> getNotifications() {  
  105.         return mJtmlService.getNotifications();  
  106.     }  
  107.   
  108.     public Observable<List<ShowBill>> getShareBills(String id, String pageNo, String pageSize) {  
  109.         return mJtmlService.getShareBills(id, pageNo, pageNo);  
  110.     }  
  111.   
  112.     public Observable<GoodsDetail> getGoodsDetailById(String id) {  
  113.         return mJtmlService.getGoodsDetailById(id);  
  114.     }  
  115.   
  116.     public Observable<List<IndexImgList>> getIndexImgList() {  
  117.         return mJtmlService.getIndexImgList();  
  118.     }  
  119.   
  120.     public Observable<ArrayList<GoodsList>> getGoodList(String id, String pageNo, String pageSize) {  
  121.         return mJtmlService.getGoodList(id, pageNo, pageSize);  
  122.     }  
  123.   
  124. }  

  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3.   
  4. import retrofit2.http.GET;  
  5. import retrofit2.http.Headers;  
  6. import retrofit2.http.Query;  
  7. import rx.Observable;  
  8.   

  9. public interface JtmlServer {  
  10.   
  11.     //評論接口  
  12.     @Headers(MyRetrofitManager.CACHE_CONTROL_AGE + MyRetrofitManager.CACHE_STALE_SHORT)  
  13.     @GET("share/indexSharecommentsList.action")  
  14.     Observable<List<Comment>> getComments();  
  15.   
  16.     //最新揭曉接口  
  17.     @Headers(MyRetrofitManager.CACHE_CONTROL_AGE + MyRetrofitManager.CACHE_STALE_SHORT)  
  18.     @GET("lottery/lotteryproductutilList.action")  
  19.     Observable<List<Notifications>> getNotifications();  
  20.   
  21.     //商品詳情接口  
  22.     @Headers(MyRetrofitManager.CACHE_CONTROL_AGE + MyRetrofitManager.CACHE_STALE_SHORT)  
  23.     @GET("products/goodsDescAjax.action")  
  24.     Observable<GoodsDetail> getGoodsDetailById(@Query("id") String id);  
  25.   
  26.     //曬單接口  
  27.     @Headers(MyRetrofitManager.CACHE_CONTROL_AGE + MyRetrofitManager.CACHE_STALE_SHORT)  
  28.     @GET("share/ajaxPage.action")  
  29.     Observable<List<ShowBill>> getShareBills(@Query("id") String id, @Query("pageNo") String pageNo, @Query("pageSize") String pageSize);  
  30.   
  31.     //主頁面輪播圖接口  
  32.     @Headers(MyRetrofitManager.CACHE_CONTROL_AGE + MyRetrofitManager.CACHE_STALE_SHORT)  
  33.     @GET("list/indexImgList.action")  
  34.     Observable<List<IndexImgList>> getIndexImgList();  
  35.   
  36.     //主頁面商品列表接口  
  37.     @Headers(MyRetrofitManager.CACHE_CONTROL_AGE + MyRetrofitManager.CACHE_STALE_SHORT)  
  38.     @GET("list/goodsList.action")  
  39.     Observable<ArrayList<GoodsList>> getGoodList(@Query("id") String id, @Query("pageNo") String pageNo, @Query("pageSize") String pageSize);  
  40. }  


  1. import butterknife.Bind;  
  2. import rx.android.schedulers.AndroidSchedulers;  
  3. import rx.functions.Action0;  
  4. import rx.functions.Action1;  
  5. import rx.functions.Func1;  
  6. import rx.schedulers.Schedulers;  
  7.   

  8. public class GoodsDetailActivity extends BaseActivity {  
  9.   
  10.   
  11.     @Bind(R.id.activity_goosdetail_img_back)  
  12.     ImageView                       mImgBack;  
  13.     @Bind(R.id.activity_goosdetail_img_share)  
  14.     ImageView                       mImgShare;  
  15.     @Bind(R.id.activity_goosdetail_img_cart)  
  16.     ImageView                       mImgCart;  
  17.     @Bind(R.id.activity_goosdetail_img_home)  
  18.     ImageView                       mImgHome;  
  19.     @Bind(R.id.activity_goosdetail_rv_detail)  
  20.     RecyclerViewBaseOnPullToRefresh mRvDetail;  
  21.     @Bind(R.id.activity_goosdetail_ll_unsell)  
  22.     LinearLayout                    mLlUnsell;  
  23.     @Bind(R.id.activity_goosdetail_ll_selling)  
  24.     LinearLayout                    mLlSelling;  
  25.     @Bind(R.id.activity_goosdetail_buy_now)  
  26.     TextView                        mTxtBuyNow;  
  27.     @Bind(R.id.activity_goosdetail_add_bill)  
  28.     TextView                        mTxtAddBill;  
  29.     @Bind(R.id.activity_goosdetail_go_now)  
  30.     TextView                        mTxtGoNow;  
  31.   
  32.     public final static String LOTTERY_ID  = "lotteryProductId";  
  33.     public final static String INTENT_FROM = "intentFrom";  
  34.     private String  lotteryProductId;  
  35.     private String  intentFrom;  
  36.     private Context mContext;  
  37.     private App     app;  
  38.   
  39.     private RecyclerView.LayoutManager mLayoutManager;  
  40.     private GoodsDetailAdapter         mGoodsDetailAdapter;  
  41.     private View                       mHeadView;  
  42.     private Kanner                     mKanner;  
  43.   
  44.     public static void start(Context context, String lotteryProductId, String intentFrom) {  
  45.         Intent intent = new Intent(context, GoodsDetailActivity.class);  
  46.         intent.putExtra(LOTTERY_ID, lotteryProductId);  
  47.         intent.putExtra(INTENT_FROM, intentFrom);  
  48.         context.startActivity(intent);  
  49.     }  
  50.   
  51.     @Override  
  52.     protected void onSaveInstanceState(Bundle outState) {  
  53.         super.onSaveInstanceState(outState);  
  54.         outState.putString(LOTTERY_ID, this.lotteryProductId);  
  55.         outState.putString(INTENT_FROM, this.intentFrom);  
  56.     }  
  57.   
  58.     @Override  
  59.     protected int getLayoutId() {  
  60.         return R.layout.activity_goods_detail;  
  61.     }  
  62.   
  63.     @Override  
  64.     protected void afterCreate(Bundle savedInstanceState) {  
  65.         mContext = this;  
  66.         app = (App) mContext.getApplicationContext();  
  67.         app.addClearActivity(this);  
  68.         initData(savedInstanceState);  
  69.         loadGoodsDetailById();  
  70.     }  
  71.   
  72.     private void initData(Bundle savedInstanceState) {  
  73.         if (savedInstanceState != null) {  
  74.             if (savedInstanceState.containsKey(LOTTERY_ID) && savedInstanceState.containsKey(INTENT_FROM)) {  
  75.                 this.lotteryProductId = savedInstanceState.getString(LOTTERY_ID);  
  76.                 this.intentFrom = savedInstanceState.getString(INTENT_FROM);  
  77.             }  
  78.         }else{  
  79.             this.lotteryProductId = getIntent().getExtras().getString(LOTTERY_ID);  
  80.             this.intentFrom = getIntent().getExtras().getString(INTENT_FROM);  
  81.         }  
  82.   
  83.         mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);  
  84.         mRvDetail.getRefreshableView().setLayoutManager(mLayoutManager);  
  85.         mRvDetail.getRefreshableView().setItemAnimator(new DefaultItemAnimator());  
  86.         mGoodsDetailAdapter = new GoodsDetailAdapter();  
  87.         setHeader(mRvDetail.getRefreshableView());  
  88.         mRvDetail.getRefreshableView().setAdapter(mGoodsDetailAdapter);  
  89.         mGoodsDetailAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {  
  90.             @Override  
  91.             public void onItemClick(int position, Object data) {  
  92.   
  93.             }  
  94.         });  
  95.         //showNewsDetailFragment(news);  
  96.     }  
  97.     private void setHeader(RecyclerView view) {  
  98.         View header = LayoutInflater.from(this).inflate(R.layout.item_good_detail_first, view, false);  
  99.         mHeadView = header;  
  100.         mGoodsDetailAdapter.setHeaderView(header);  
  101.     }  
  102.   
  103.     private void loadGoodsDetailById() {  
  104.         MyRetrofitManager.builder().  
  105.         getGoodsDetailById(this.lotteryProductId)  
  106.         .subscribeOn(Schedulers.io())  
  107.         .observeOn(AndroidSchedulers.mainThread())  
  108.         .doOnSubscribe(new Action0() {  
  109.             @Override  
  110.             public void call() {  
  111.                 // showProgress();  
  112.             }  
  113.         }).map(new Func1<GoodsDetail, GoodsDetail>() {  
  114.             @Override  
  115.             public GoodsDetail call(GoodsDetail goodsDetail) {  
  116.                 L.e(goodsDetail.getId());  
  117.                 return goodsDetail;  
  118.             }  
  119.         }).subscribe(new Action1<GoodsDetail>() {  
  120.             @Override  
  121.             public void call(GoodsDetail goodsDetail) {  
  122.                 L.e(goodsDetail.getId());  
  123.             }  
  124.         },new Action1<Throwable>() {  
  125.             @Override  
  126.             public void call(Throwable throwable) {  
  127.                 L.e(throwable,"Load news detail error");  
  128.             }  
  129.         });  
  130.     }  
  131. }  

  1. mport java.util.ArrayList;  
  2. import java.util.List;  
  3.   
  4. import butterknife.Bind;  
  5. import rx.android.schedulers.AndroidSchedulers;  
  6. import rx.functions.Action0;  
  7. import rx.functions.Action1;  
  8. import rx.functions.Func1;  
  9. import rx.schedulers.Schedulers;  
  10.   
  11. /** 
  12.  * Created by pengjf on 2016/5/12. 
  13.  */  
  14. public class FragmentBuyGood extends BaseFragment {  
  15.   
  16.     @Bind(R.id.fragment_buy_txt_title)  
  17.     TextView                        mTxtTitle;  
  18.     @Bind(R.id.fragment_buy_img_search)  
  19.     ImageView                       mImgSearch;  
  20.     @Bind(R.id.fragment_buy_rv_goods)  
  21.     RecyclerViewBaseOnPullToRefresh mBuyRv;  
  22.   
  23.     Kanner mKanner;  
  24.   
  25.     private App                        app;  
  26.     private Context                    mContext;  
  27.     private RecyclerView.LayoutManager mLayoutManager;  
  28.     private GoodListAdapter            mGoodListAdapter;  
  29.     private String[]                   imgUrls;       //輪播圖需要的url數組  
  30.     private String id = "hot20";    //商品默認查詢id  
  31.     private String typeId;          //商品默認分類id  
  32.     private String pageNo   = "0";    //商品默認頁數  
  33.     private String pageSize = "10"//商品默認每頁個數  
  34.   
  35.     @Override  
  36.     protected int getLayoutId() {  
  37.         return R.layout.fragment_buy_good;  
  38.     }  
  39.   
  40.     @Override  
  41.     protected void afterCreate(Bundle savedInstanceState) {  
  42.         mContext = getActivity();  
  43.         app = (App) mContext.getApplicationContext();  
  44.         initData();  
  45.         //      if (mNewsListAdapter.getmNewsList().size() == 0) {  
  46.         //            loadLatestNews();  
  47.         //      }  
  48.     }  
  49.   
  50.     public static FragmentBuyGood newInstance() {  
  51.         return new FragmentBuyGood();  
  52.     }  
  53.   
  54.   
  55.     private void initData() {  
  56.         mLayoutManager = new GridLayoutManager(mContext, 2);  
  57.         mGoodListAdapter = new GoodListAdapter();  
  58.         mBuyRv.getRefreshableView().setLayoutManager(mLayoutManager);  
  59.         //        mBuyRv.getRefreshableView().setItemAnimator(new DefaultItemAnimator());  
  60.         //        mBuyRv.getRefreshableView().addItemDecoration(new GridItemDecoration(mContext, true));  
  61.         mBuyRv.getRefreshableView().setAdapter(mGoodListAdapter);  
  62.         setHeader(mBuyRv.getRefreshableView());  
  63.         loadHeaderViewData();  
  64.         loadGoodListData();  
  65.     }  
  66.   
  67.   
  68.     /** 
  69.      * 給RecycleView設置頭部的View 
  70.      * @param view 
  71.      */  
  72.     private void setHeader(RecyclerView view) {  
  73.         View header = LayoutInflater.from(mContext).inflate(R.layout.item_fragment_buy_frist, view, false);  
  74.         mKanner = (Kanner) header.findViewById(R.id.fragment_buy_kanner);  
  75.         mKanner.setVisibility(View.VISIBLE);  
  76.         mGoodListAdapter.setHeaderView(header);  
  77.     }  
  78.   
  79.     /** 
  80.      * 加載RecycleView頭部的View需要的數據 
  81.      */  
  82.     private void loadHeaderViewData() {  
  83.         MyRetrofitManager.builder().getIndexImgList()  
  84.                 .subscribeOn(Schedulers.io())  
  85.                 .observeOn(AndroidSchedulers.mainThread())  
  86.                 .doOnSubscribe(new Action0() {  
  87.                     @Override  
  88.                     public void call() {  
  89.                         // showProgress();  
  90.                     }  
  91.                 }).map(new Func1<List<IndexImgList>, List<IndexImgList>>() {  
  92.                     @Override  
  93.                     public List<IndexImgList> call(List<IndexImgList> result) {  
  94.   
  95.                         return result;  
  96.                     }  
  97.                 })  
  98.                 .subscribe(new Action1<List<IndexImgList>>() {  
  99.                     @Override  
  100.                     public void call(List<IndexImgList> result) {  
  101.                         if(result!=null && result.size()>0) {  
  102.                             imgUrls = new String[result.size()];  
  103.                             for (int i = 0; i < result.size(); i++) {  
  104.                                 imgUrls[i] = MyRetrofitManager.BASE_IMAGE_URL + result.get(i).getProImg();  
  105.                             }  
  106.                             mKanner.setImagesUrl(imgUrls);  
  107.                         }else{  //服務器返回數據異常情況待處理  
  108.   
  109.                         }  
  110.                     }  
  111.                 }, new Action1<Throwable>() {  
  112.                     @Override  
  113.                     public void call(Throwable throwable) {  
  114.                         //mAutoLoadListener.setLoading(false);  
  115.                         L.e(throwable, "Load before news error");  
  116.                         throwable.printStackTrace();  
  117.                         //mLoadBeforeSnackbar.show();  
  118.                     }  
  119.                 });  
  120.     }  
  121.   
  122.     private void loadGoodListData() {  
  123.         MyRetrofitManager.builder().getGoodList(this.id,this.pageNo,this.pageSize)  
  124.                 .subscribeOn(Schedulers.io())  
  125.                 .observeOn(AndroidSchedulers.mainThread())  
  126.                 .doOnSubscribe(new Action0() {  
  127.                     @Override  
  128.                     public void call() {  
  129.                         // showProgress();  
  130.                     }  
  131.                 }).map(new Func1<ArrayList<GoodsList>, ArrayList<GoodsList>>() {  
  132.             @Override  
  133.             public ArrayList<GoodsList> call(ArrayList<GoodsList> result) {  
  134.   
  135.                 return result;  
  136.             }  
  137.         })  
  138.                 .subscribe(new Action1<ArrayList<GoodsList>>() {  
  139.                     @Override  
  140.                     public void call(ArrayList<GoodsList> result) {  
  141.                         //服務器成功返回數據  
  142.                         if(result!=null && result.size()>0) {  
  143.                             mGoodListAdapter.addDatas(result);  
  144.                         }else{  //服務器返回數據異常情況待處理  
  145.   
  146.                         }  
  147.                     }  
  148.                 }, new Action1<Throwable>() {  
  149.                     @Override  
  150.                     public void call(Throwable throwable) {  
  151.                         //mAutoLoadListener.setLoading(false);  
  152.                         L.e(throwable, "Load before news error");  
  153.                         throwable.printStackTrace();  
  154.                         //mLoadBeforeSnackbar.show();  
  155.                     }  
  156.                 });  
  157.     }  
  158. }  


gradle
  1.   
  2. dependencies {  
  3.     compile fileTree(include: ['*.jar'], dir: 'libs')  
  4.     testCompile 'junit:junit:4.12'  
  5.     compile 'com.android.support:appcompat-v7:23.2.0'  
  6.     compile 'com.android.support:support-v4:23.2.1'  
  7.     compile 'com.android.support:recyclerview-v7:23.2.1'  
  8.     compile 'com.android.support:cardview-v7:23.2.1'  
  9.     compile 'com.android.support:design:23.2.1'  
  10.     // retrofit2 + okhttp3  
  11.     compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'  
  12.     compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'  
  13.     compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'  
  14.     compile 'com.squareup.okhttp3:okhttp:3.2.0'  
  15.     compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'  
  16.     // rxjava  
  17.     compile 'io.reactivex:rxandroid:1.1.0'  
  18.     compile 'io.reactivex:rxjava:1.1.0'  
  19.     // gson react  
  20.     compile 'com.google.code.gson:gson:2.6.2'  
  21.     compile 'com.jakewharton:butterknife:7.0.1'  
  22.     compile project(':pulltorefresh_lib')  
  23.     compile 'com.android.support:support-v13:+'  
  24.     compile 'com.github.bumptech.glide:glide:3.7.0'  
  25.     compile 'com.yalantis:phoenix:1.2.3'  
  26. }  




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章