(一)Rxjava2+Retrofit完美封裝
* 本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出
要說2016年最火的Android技術是什麼,毫無疑問肯定是RxJava+Retrofit+Mvp。現如今2017年也已經過了快一半了。相信做android開發的小夥伴對RxJava和Retrofit也不再陌生。即使沒有刻意的去學習過,也應該對RxJava和Retrofit有個一知半解。去年的時候學習了Rxjava和Retrofit的基本用法,但一直沒有在實際專案中運用。今年開做新專案,果斷在新專案中引入了RxJava和Retrofit。本篇文章將介紹筆者在專案中對Retrofit的封裝。
先來看一下封裝過後的Retrofit如何使用。
RetrofitHelper.getApiService()
.getMezi()
.compose(this.<List<MeiZi>>bindToLifecycle())
.compose(ProgressUtils.<List<MeiZi>>applyProgressBar(this))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread ())
.subscribe(new DefaultObserver<List<MeiZi>>() {
@Override
public void onSuccess(List<MeiZi> response) {
showToast("請求成功,妹子個數為" + response.size());
}
});
沒錯,就是這麼簡潔的一個鏈式呼叫,可以顯示載入動畫,還加入了Retrofit生命週期的管理。
開始之前需要先在module專案裡的Gradle檔案中新增用到的依賴庫
compile "io.reactivex.rxjava2:rxjava:$rootProject.ext.rxjava2Version"
compile "com.squareup.retrofit2:retrofit:$rootProject.ext.retrofit2Version"
compile "com.squareup.retrofit2:converter-scalars:$rootProject.ext.retrofit2Version"
compile "com.squareup.retrofit2:converter-gson:$rootProject.ext.retrofit2Version"
compile "com.squareup.retrofit2:adapter-rxjava2:$rootProject.ext.retrofit2Version"
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
compile "com.trello.rxlifecycle2:rxlifecycle:$rootProject.ext.rxlifecycle"
//compile "com.trello.rxlifecycle2:rxlifecycle-android:$rootProject.ext.rxlifecycle"
compile "com.trello.rxlifecycle2:rxlifecycle-components:$rootProject.ext.rxlifecycle"
為了方便依賴庫版本的修改我們採用”io.reactivex.rxjava2:rxjava:$rootProject.ext.rxjava2Version”這中方式新增依賴,因此需要在project的build.gradle檔案的加上以下內容:
ext {
supportLibVersion = '25.1.0'
butterknifeVersion = '8.5.1'
rxjava2Version = '2.0.8'
retrofit2Version = '2.2.0'
rxlifecycle='2.1.0'
gsonVersion = '2.8.0'
}
下面將通過幾個小節對本次封裝作詳細的解析:
- 伺服器響應資料的基類BasicResponse
- 構建初始化Retrofit的工具類IdeaApi
- 通過GsonConverterFactory獲取真實響應資料
- 封裝DefaultObserver處理伺服器響應
- 處理載入Loading
- 管理Retrofit生命週期
- 如何使用封裝
- 小結
一.伺服器響應資料的基類BasicResponse。
假定伺服器返回的Json資料格式如下:
{
"code": 200,
"message": "成功",
"content": {
...
}
}
根據Json資料格式構建我們的BasicResponse(BasicResponse中的欄位內容需要根據自己伺服器返回的資料確定)。程式碼如下:
public class BasicResponse<T> {
private int code;
private String message;
private T content;
...此處省去get、set方法。
二.構建初始化Retrofit的工具類IdeaApi。
該類通過RetrofitUtils來獲取ApiService的例項。程式碼如下:
public class IdeaApi {
public static <T> T getApiService(Class<T> cls,String baseUrl) {
Retrofit retrofit = RetrofitUtils .getRetrofitBuilder(baseUrl).build();
return retrofit.create(cls);
}
}
RetrofitUtils用來構建Retrofit.Builder,並對OkHttp做以下幾個方面的配置:
1. 設定日誌攔截器,攔截伺服器返回的json資料。Retrofit將請求到json資料直接轉換成了實體類,但有時候我們需要檢視json資料,Retrofit並沒有提供直接獲取json資料的功能。因此我們需要自定義一個日誌攔截器攔截json資料,並輸入到控制檯。
2. 設定Http請求頭。給OkHttp 新增請求頭攔截器,配置請求頭資訊。還可以為介面統一新增請求頭資料。例如,把使用者名稱、密碼(或者token)統一新增到請求頭。後續每個介面的請求頭中都會攜帶使用者名稱、密碼(或者token)資料,避免了為每個介面單獨新增。
3. 為OkHttp配置快取。同樣可以同過攔截器實現快取處理。包括控制快取的最大生命值,控制快取的過期時間。
4. 如果採用https,我們還可以在此處理證書校驗以及伺服器校驗。
5. 為Retrofit新增GsonConverterFactory。此處是一個比較重要的環節,將在後邊詳細講解。
RetrofitUtils 程式碼如下:
public class RetrofitUtils {
public static OkHttpClient.Builder getOkHttpClientBuilder() {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
try {
LogUtils.e("OKHttp-----", URLDecoder.decode(message, "utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
LogUtils.e("OKHttp-----", message);
}
}
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
File cacheFile = new File(Utils.getContext().getCacheDir(), "cache");
Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb
return new OkHttpClient.Builder()
.readTimeout(Constants.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
.connectTimeout(Constants.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
.addInterceptor(loggingInterceptor)
.addInterceptor(new HttpHeaderInterceptor())
.addNetworkInterceptor(new HttpCacheInterceptor())
// .sslSocketFactory(SslContextFactory.getSSLSocketFactoryForTwoWay()) // https認證 如果要使用https且為自定義證書 可以去掉這兩行註釋,並自行配製證書。
// .hostnameVerifier(new SafeHostnameVerifier())
.cache(cache);
}
public static Retrofit.Builder getRetrofitBuilder(String baseUrl) {
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").serializeNulls().create();
OkHttpClient okHttpClient = RetrofitUtils.getOkHttpClientBuilder().build();
return new Retrofit.Builder()
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(baseUrl);
}
}
三.通過GsonConverterFactory獲取真實響應資料
在第一節中我們構建了伺服器響應資料BasicResponse,BasicResponse由code、message、和content三個欄位。其中code為伺服器返回的錯誤碼。我們會事先和伺服器約定成功時的code值,比如200表示請求成功。但通常在請求伺服器資料過程中免不了會出現各種錯誤。例如使用者登入時密碼錯誤、請求引數錯誤的情況。此時伺服器會根據錯誤情況返回對應的錯誤碼。一般來說,我們只關心成功時即code為200時的content資料。而對於code不為200時我們只需要給出對應的Toast提示即可。事實上我們對我們有用的僅僅時code為200時的content資料。因此我們可以考慮過濾掉code和message,在請求成功的回撥中只返回content的內容。
在此種情況下就需要我們通過自定義GsonConverterFactory來實現了。我們可以直接從Retrofit的原始碼中copy出GsonConverterFactory的三個相關類來做修改。
其中最終要的一部分是修改GsonResponseBodyConverter中的convert方法。在該方法中拿到伺服器響應資料並判斷code是否為200。如果是,則獲取到content並返回,如果不是,則在此處可以丟擲對應的自定義的異常。然後再Observer中統一處理異常情況。GsonResponseBodyConverter程式碼如下:
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, Object> {
private final TypeAdapter<T> adapter;
GsonResponseBodyConverter(TypeAdapter<T> adapter) {
this.adapter = adapter;
}
@Override
public Object convert(ResponseBody value) throws IOException {
try {
BasicResponse response = (BasicResponse) adapter.fromJson(value.charStream());
if (response.getCode()==200) {
return response.getResults();
} else {
// 特定 API 的錯誤,在相應的 DefaultObserver 的 onError 的方法中進行處理
throw new ServerResponseException(response.getCode(), response.getMessage());
}
} finally {
value.close();
}
return null;
}
}
四.構建DefaultObserver處理伺服器響應。
上一節中我們講到了在請求伺服器時可能出現的一些例如密碼錯誤、引數錯誤的情況,伺服器給我們返回了對應的錯誤碼,我們根據錯誤碼丟擲了對應自定義異常。除此之外在我們發起網路請求時還可能發生一些異常情況。例如沒有網路、請求超時或者伺服器返回了資料但在解析時出現了資料解析異常等。對於這樣的情況我們也要進行統一處理的。那麼我們就需要自定義一個DefaultObserver類繼承Observer,並重寫相應的方法。
該類中最重要的兩個方法時onNext和onError。
1.在伺服器返回資料成功的情況下會回撥到onNext方法。因此我們可以在DefaultObserver中定義一個抽象方法onSuccess(T response),在呼叫網路時重寫onSuccess方法即可。
2.如果在請求伺服器過程中出現任何異常,都會回撥到onError方法中。包括上節中我們自己丟擲的異常都會回撥到onError。因此我們的重頭戲就是處理onError。在onError中我們根據異常資訊給出對應的Toast提示即可。
DefaultObserver類的程式碼如下:
public abstract class DefaultObserver<T> implements Observer<T> {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(T response) {
onSuccess(response);
onFinish();
}
@Override
public void onError(Throwable e) {
LogUtils.e("Retrofit", e.getMessage());
if (e instanceof HttpException) { // HTTP錯誤
onException(ExceptionReason.BAD_NETWORK);
} else if (e instanceof ConnectException
|| e instanceof UnknownHostException) { // 連線錯誤
onException(ExceptionReason.CONNECT_ERROR);
} else if (e instanceof InterruptedIOException) { // 連線超時
onException(ExceptionReason.CONNECT_TIMEOUT);
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException) { // 解析錯誤
onException(ExceptionReason.PARSE_ERROR);
}else if(e instanceof ServerResponseException){
onFail(e.getMessage());
} else {
onException(ExceptionReason.UNKNOWN_ERROR);
}
onFinish();
}
@Override
public void onComplete() {
}
/**
* 請求成功
*
* @param response 伺服器返回的資料
*/
abstract public void onSuccess(T response);
/**
* 伺服器返回資料,但響應碼不為200
*
*/
public void onFail(String message) {
ToastUtils.show(message);
}
public void onFinish(){}
/**
* 請求異常
*
* @param reason
*/
public void onException(ExceptionReason reason) {
switch (reason) {
case CONNECT_ERROR:
ToastUtils.show(R.string.connect_error, Toast.LENGTH_SHORT);
break;
case CONNECT_TIMEOUT:
ToastUtils.show(R.string.connect_timeout, Toast.LENGTH_SHORT);
break;
case BAD_NETWORK:
ToastUtils.show(R.string.bad_network, Toast.LENGTH_SHORT);
break;
case PARSE_ERROR:
ToastUtils.show(R.string.parse_error, Toast.LENGTH_SHORT);
break;
case UNKNOWN_ERROR:
default:
ToastUtils.show(R.string.unknown_error, Toast.LENGTH_SHORT);
break;
}
}
/**
* 請求網路失敗原因
*/
public enum ExceptionReason {
/**
* 解析資料失敗
*/
PARSE_ERROR,
/**
* 網路問題
*/
BAD_NETWORK,
/**
* 連線錯誤
*/
CONNECT_ERROR,
/**
* 連線超時
*/
CONNECT_TIMEOUT,
/**
* 未知錯誤
*/
UNKNOWN_ERROR,
}
}
五.處理載入Loading
關於Loading我們可以通過RxJava的compose操作符來做一個非常優雅的處理。首先定義一個ProgressUtils工具類,然後通過RxJava的ObservableTransformer做一個變換來處理Loading。想要顯示Loading,只需要加上.compose(ProgressUtils.< T >applyProgressBar(this))即可。
ProgressUtils程式碼如下:
public class ProgressUtils {
public static <T> ObservableTransformer<T, T> applyProgressBar(
@NonNull final Activity activity, String msg) {
final WeakReference<Activity> activityWeakReference = new WeakReference<>(activity);
final DialogUtils dialogUtils = new DialogUtils();
dialogUtils.showProgress(activityWeakReference.get());
return new ObservableTransformer<T, T>() {
@Override
public ObservableSource<T> apply(Observable<T> upstream) {
return upstream.doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(Disposable disposable) throws Exception {
}
}).doOnTerminate(new Action() {
@Override
public void run() throws Exception {
Activity context;
if ((context = activityWeakReference.get()) != null
&& !context.isFinishing()) {
dialogUtils.dismissProgress();
}
}
}).doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(Disposable disposable) throws Exception {
/*Activity context;
if ((context = activityWeakReference.get()) != null
&& !context.isFinishing()) {
dialogUtils.dismissProgress();
}*/
}
});
}
};
}
public static <T> ObservableTransformer<T, T> applyProgressBar(
@NonNull final Activity activity) {
return applyProgressBar(activity, "");
}
}
至此關於RxJava和Retrofit的二次封裝已經基本完成。但是我們不能忽略了很重要的一點,就是網路請求的生命週期。我們將在下一節中詳細講解。
六、管理Retrofit生命週期
當activity被銷燬時,網路請求也應該隨之終止的。要不然就可能造成記憶體洩漏。會嚴重影到響App的效能!因此Retrofit生命週期的管理也是比較重要的一點內容。在這裡我們使用 來對Retrofit進行生命週期管理。其使用流程如下:
1.在gradel中新增依賴如下:
compile 'com.trello.rxlifecycle2:rxlifecycle:2.1.0'
compile 'com.trello.rxlifecycle2:rxlifecycle-components:2.1.0'
2.讓我們的BaseActivity繼承RxAppCompatActivity。
具體程式碼如下:
public abstract class BaseActivity extends RxAppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
init(savedInstanceState);
}
protected void showToast(String msg) {
ToastUtils.show(msg);
}
protected abstract @LayoutRes int getLayoutId();
protected abstract void init(Bundle savedInstanceState);
}
同樣我們專案的BaseFragment繼承RxFragment(注意使用繼承V4包下的RxFragment),如下:
public abstract class BaseFragment extends RxFragment {
public View rootView;
public LayoutInflater inflater;
@Nullable
@Override
public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
this.inflater = inflater;
if (rootView == null) {
rootView = inflater.inflate(this.getLayoutId(), container, false);
init(savedInstanceState);
}
ViewGroup parent = (ViewGroup) rootView.getParent();
if (parent != null) {
parent.removeView(rootView);
}
return rootView;
}
protected abstract int getLayoutId();
protected abstract void init(Bundle savedInstanceState);
protected void showToast(String msg) {
ToastUtils.show(msg);
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onDestroyView() {
super.onDestroyView();
}
}
3.使用compose操作符管理Retrofit生命週期了:
myObservable
.compose(bindToLifecycle())
.subscribe();
或者
myObservable
.compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
.subscribe();
關於RxLifecycle的詳細使用方法可以參考
七.如何使用封裝
前面幾節內容講解了如何RxJava進行二次封裝,封裝部分的程式碼可以放在我們專案的Library模組中。那麼封裝好之後我們應該如何在app模組中使用呢?
1.定義一個介面來存放我們專案的API
public interface IdeaApiService {
/**
* 此介面伺服器響應資料BasicResponse的泛型T應該是List<MeiZi>
* 即BasicResponse<List<MeiZi>>
* @return BasicResponse<List<MeiZi>>
*/
@Headers("Cache-Control: public, max-age=10")//設定快取 快取時間為100s
@GET("福利/10/1")
Observable<List<MeiZi>> getMezi();
/**
* 登入 介面為假介面 並不能返回資料
* @return
*/
@POST("login.do")
Observable<LoginResponse> login(@Body LoginRequest request);
/**
* 重新整理token 介面為假介面 並不能返回資料
* @return
*/
@POST("refresh_token.do")
Observable<RefreshTokenResponseBean> refreshToken(@Body RefreshTokenRequest request);
@Multipart
@POST("upload/uploadFile.do")
Observable<BasicResponse> uploadFiles(@Part List<MultipartBody.Part> partList);
}
2.定義一個RetrofitHelper 類,通過IdeaApi來獲取IdeaApiService的例項。
public class RetrofitHelper {
private static IdeaApiService mIdeaApiService;
public static IdeaApiService getApiService(){
return mIdeaApiService;
}
static {
mIdeaApiService= IdeaApi.getApiService(IdeaApiService.class, Constants.API_SERVER_URL);
}
}
3.在Activity或者Fragment中發起網路請求
/**
* Get請求
* @param view
*/
public void getData(View view) {
RetrofitHelper.getApiService()
.getMezi()
.compose(this.<List<MeiZi>>bindToLifecycle())
.compose(ProgressUtils.<List<MeiZi>>applyProgressBar(this))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DefaultObserver<List<MeiZi>>() {
@Override
public void onSuccess(List<MeiZi> response) {
showToast("請求成功,妹子個數為" + response.size());
}
});
}
八.小結
本篇文章主要講解了Rxjava和Retrofit的二次封裝。以上內容也是筆者參考多方面的資料經過長時間的改動優化而來。但鑑於本人能力有限,其中也避免不了出現不當之處。還請大家多多包涵。另外,在投稿郭神公眾號時文章可能還存在很多處理不優雅的地方,比如對響應資料的處理以及對Loading的處理。在投稿被推送後收到了很多小夥伴的建議,因此筆者也參考了大家的意見並做了優化,在此感謝大家。最後如果有疑問歡迎在文章留言評論。
相關推薦
(一)Rxjava2+Retrofit完美封裝
* 本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出 要說2016年最火的Android技術是什麼,毫無疑問肯定是RxJava+Retrofit+Mvp。現如今2017年也已經過了快一半了。相信做android開發的小夥伴對RxJava和Re
算法(一)冒泡排序與封裝接構
冒泡排序冒泡排序冒泡排序是相近的兩個數字兩兩比較,然後按照從小到大或者從大到小的順序排列比如有兩個數字A,B, A = 3 , B = 2,A 比 B 大,則A 和 B 位置發生交換,順序變成了 B,A,詳細過程看輸出結果就很清晰了。升序排序的代碼如下:lst1 = [[1, 9, 8, 5, 6, 7, 4
(一)解析第三方介面封裝為物件
最近做了一個還算可以上手的外包專案,就是解析第三方介面的資料然後展示在頁面中,整個專案做下來還是有很多收穫的,學到的有很多,包括xml的解析,echarts曲線的應用,video標籤的應用及新增定時任務器定時重新整理資料庫等,耗時將近兩個月,在這裡先做一個簡單的
Retrofit+Rxjava 網路請求的簡單封裝(一)(觀察者模式)
我通過公司一個上線App介面來進行Retrofit+Rxjava 的簡單學習 http://x-phone.cn/api/v1.0/DeletePrivateContact 當然你直接訪問上面的url是無法獲取伺服器Json資料的,因為是需要封裝請求體,
面向對象(一)封裝 命名空間 訪問修飾符等
制度 color return 重用 等級 部分 cnblogs 控制 turn 一、封裝 封裝,即隱藏對象的屬性和實現細節,僅對外公開接口,控制在程序中屬性的讀和修改的訪問級別;將抽象得到的數據和行為(或功能)相結合,形成一個有機的整體,也就是將數據與操作數據的源代碼進行
C# SQL封裝(一)
c# sql封裝(一)感謝軟謀徐老師的SQLHelper.as , 代碼如下(本人有所改動):using System; using System.Collections; using System.Collections.Generic; using System.Configuration; using
Javascript 面向對象(一):封裝
一模一樣 http rip nbsp 所有 == 編程 func color Javascript 面向對象編程(一):封裝 Javascript是一種基於對象(object-based)的語言,你遇到的所有東西幾乎都是對象。但是,它又不是一種真正的面向對象編程(OOP)語
前端就業班筆記框架封裝(一)
js框架整體介紹-> 框架和庫是什麽庫: 倉庫, 表示的是很多為了實現特殊功能而封裝的, 便於使用開發的 函數, 對象 和 常量等.例如: // 在 dom 元素下, 獲得 復合 tagName 的元素, // 如果不提供 dom 參數, 那麽就是默認
axios封裝(一)基礎配置
html ces nco urlencode axios port htm RM url axios 是目前流行的Promise網絡請求庫,在瀏覽器端他通過 xhr方式創建ajax請求。在node環境下,通過 http 庫創建網絡請求。 axios 提供了豐富的配置,這裏講
JavaScript 封裝插件學習筆記(一)
undefined error 精確 屬性。 根據 rip nbsp 類型 alt 此篇只是筆記,在借鑒、參考、模仿的過程,可能不完整,請多指教! 定義插件名稱要註意命名沖突,防止全局汙染。 1.第一種Javascript對象命名:(Javascript語言是“先解析,後運
前後端分離業務邏輯常用封裝函式(一)
1.陣列去空: function clear_arr_trim(array) { for(var i = 0 ;i<array.length;i++){ if(array[i] == "" || (array[i] == null) || typeo
websocket(一)封裝使用
介紹 WebSocket用於在Web瀏覽器和伺服器之間進行任意的雙向資料傳輸的一種技術。WebSocket協議基於TCP協議實現,包含初始的握手過程,以及後續的多次資料幀雙向傳輸過程。其目的是在WebSocket應用和WebSocket伺服器進行頻繁雙向通訊時,可以使伺服器避免開啟多
RxJava2(一)完整生命週期
RxJava2 完整生命週期 簡介: 1.RxJava用於通過使用被觀察者Obverserable/Flowable序列來構建非同步和基於事件的庫。 2.RxJava支援Java5之後版本,還支援跑在JVM上的各種語言。 3.RxJava可以用在Android開發的任何地方,包括App所
appium常用封裝(一)
一、Dos下執行adb devices獲取udid封裝 1 #coding=utf-8 2 import os 3 class DosCmd: 4 def excute_cmd_result(self,command): 5 result_list = []
【Android架構】基於MVP模式的Retrofit2+RXjava封裝(一)
#最近有個新專案要做,搭建框架的時候,順便梳理了下MVP模式,特此記錄,歡迎大家指正。 專案地址GitHub 一 、首先是依賴 compile 'com.google.code.gson:gson:2.8.0' compile 'com.squareup.ok
C語言面向物件程式設計(一):封裝與繼承
最近在用 C 做專案,之前用慣了 C++ ,轉回頭來用C 還真有點不適應。 C++ 語言中自帶面向物件支援,如封裝、繼承、多型等面向物件的基本特徵。 C 原本是面向過程的語言,自身沒有內建這些特性,但我們還是可以利用 C 語言本身已有的特性來實現面向物件的一些基本特徵。接下來
Java面向物件與多執行緒綜合實驗(一)之封裝、繼承與多型
編寫一個程式,實現檔案管理系統中的使用者管理模組。要求模組中實現使用者的模擬登入過程。通過使用者輸入,獲取使用者名稱和口令;與事先記錄在程式中的使用者資訊進行對比,通過口令驗證後才能使用系統。使用者分為系統管理人員、檔案錄入人員,檔案瀏覽人員三類,相關類圖如下所示。 (1)要求在使用者類中
Javascript 面向物件程式設計(一):封裝
Javascript是一種基於物件(object-based)的語言,你遇到的所有東西幾乎都是物件。但是,它又不是一種真正的面向物件程式設計(OOP)語言,因為它的語法中沒有class(類)。 那麼,如果我們要把"屬性"(property)和"方法"(method),封裝成
Rxjava2.x 原始碼分析,以及手動實現Rxjava(一)
這兩年Rxjava火的一塌糊塗,不會點Rxjava+Okhttp+Retrofit+MVP+Dagger2架構都不好意思說自己混Android的。Rxjava 到底是什麼和Rxjava到底怎麼用,這裡就不講了,網上太多了,具體可以參考 這位大佬 和扔物線的。 Rxjava
js元件封裝(一)
使用者提供各項資料 var obj = { id: "container",/*容器id(必填)*/ width: 500,/*容器寬度*/ height: 300,/*容器高度*/ title: "GDP分佈",/*標題