編輯:關於Android編程
Android" target="_blank">RxAndroid是RxJava的擴展, 可以優雅地處理異步請求. 以前的文章講述過一些, 這次再補充些內容, 熟悉RxAndroid的使用方法.
本文源碼的GitHub下載地址
要點包含:
(1) 鏈式表達式的使用方式.
(2) Lambda的應用.
(3) Rx處理網絡請求.
(4) 線程自動管理, 防止內存洩露.
(5) RxBinding綁定控件的異步事件.
當然, 從一個嶄新的HelloWorld項目開始.
添加Gradle配置.
compile 'com.jakewharton:butterknife:7.0.1'
compile 'io.reactivex:rxandroid:1.1.0' // RxAndroid
compile 'io.reactivex:rxjava:1.1.0' // 推薦同時加載RxJava
RxAndroid是本文的核心依賴, 同時添加RxJava. 還有ButterKnife注解庫.
Lambda表達式, 是寫出優雅代碼的關鍵, 參考.
plugins {
id "me.tatarka.retrolambda" version "3.2.4"
}
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
Gradle 2.1+
以上, 配置非常簡單, 添加一個plugin和一個Java1.8兼容即可.
從主MainActivity
跳轉至SimpleActivity
.
/**
* 主Activity, 用於跳轉各個模塊.
*
* @author wangchenlong
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// 跳轉簡單的頁面
public void gotoSimpleModule(View view) {
startActivity(new Intent(this, SimpleActivity.class));
}
// 跳轉復雜的頁面
public void gotoMoreModule(View view) {
startActivity(new Intent(this, MoreActivity.class));
}
// 跳轉Lambda的頁面
public void gotoLambdaModule(View view) {
startActivity(new Intent(this, LambdaActivity.class));
}
// 跳轉網絡的頁面
public void gotoNetworkModule(View view) {
startActivity(new Intent(this, NetworkActivity.class));
}
// 跳轉線程安全的頁面
public void gotoSafeModule(View view) {
startActivity(new Intent(this, SafeActivity.class));
}
}
在SimpleActivity
中, 創建一個觀察者, 收到字符串的返回.
// 觀察事件發生
Observable.OnSubscribe mObservableAction = new Observable.OnSubscribe() {
@Override public void call(Subscriber subscriber) {
subscriber.onNext(sayMyName()); // 發送事件
subscriber.onCompleted(); // 完成事件
}
};
...
// 創建字符串
private String sayMyName() {
return "Hello, I am your friend, Spike!";
}
創建兩個訂閱者, 使用字符串輸出信息.
// 訂閱者, 接收字符串, 修改控件
Subscriber mTextSubscriber = new Subscriber() {
@Override public void onCompleted() {
}
@Override public void onError(Throwable e) {
}
@Override public void onNext(String s) {
mTvText.setText(s); // 設置文字
}
};
// 訂閱者, 接收字符串, 提示信息
Subscriber mToastSubscriber = new Subscriber() {
@Override public void onCompleted() {
}
@Override public void onError(Throwable e) {
}
@Override public void onNext(String s) {
Toast.makeText(SimpleActivity.this, s, Toast.LENGTH_SHORT).show();
}
};
在頁面中, 觀察者接收信息, 發送至主線程AndroidSchedulers.mainThread()
, 再傳遞給訂閱者, 由訂閱者最終處理消息. 接收信息可以是同步, 也可以是異步.
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
ButterKnife.bind(this);
// 注冊觀察活動
@SuppressWarnings("unchecked")
Observable observable = Observable.create(mObservableAction);
// 分發訂閱信息
observable.observeOn(AndroidSchedulers.mainThread());
observable.subscribe(mTextSubscriber);
observable.subscribe(mToastSubscriber);
}
最基礎的RxAndroid使用.
我們已經熟悉了初步的使用方式, 在接著學習一些其他方法, 如
just
: 獲取輸入數據, 直接分發, 更加簡潔, 省略其他回調.
from
: 獲取輸入數組, 轉變單個元素分發.
map
: 映射, 對輸入數據進行轉換, 如大寫.
flatMap
: 增大, 本意就是增肥, 把輸入數組映射多個值, 依次分發.
reduce
: 簡化, 正好相反, 把多個數組的值, 組合成一個數據.
來看看這個示例, 設置兩個不同類型數組, 作為輸入源, 根據不同情況分發數據.
/**
* 更多的RxAndroid的使用方法.
*
* Created by wangchenlong on 15/12/30. */ public class MoreActivity extends Activity { @Bind(R.id.simple_tv_text) TextView mTvText; final String[] mManyWords = {"Hello", "I", "am", "your", "friend", "Spike"}; final List
, Observable
, Observable
這次簡化調用代碼, 因為有時候我們對異常並不是很關心,
只要能catch
異常即可, 因此流僅僅關注真正需要的部分.
輸入字符串, 變換大寫, 輸出至控件中顯示.
// 添加字符串, 省略Action的其他方法, 只使用一個onNext.
Observable obShow = Observable.just(sayMyName());
// 先映射, 再設置TextView
obShow.observeOn(AndroidSchedulers.mainThread())
.map(mUpperLetterFunc).subscribe(mTextViewAction);
just
可以非常簡單的獲取任何數據, 分發時, 選擇使用的線程.
map
是對輸入數據加工, 轉換類型, 輸入Func1
, 准換大寫字母.
Func1
代表使用一個參數的函數, 前面是參數, 後面是返回值.
Action1
代表最終動作, 因而不需要返回值, 並且一個參數.
輸入數組, 單獨分發數組中每一個元素, 轉換大寫, 輸入Toast連續顯示.
// 單獨顯示數組中的每個元素
Observable obMap = Observable.from(mManyWords);
// 映射之後分發
obMap.observeOn(AndroidSchedulers.mainThread())
.map(mUpperLetterFunc).subscribe(mToastAction);
from
是讀取數組中的值, 每次單獨分發, 並分發多次, 其余類似.
輸入數組, 映射為單獨分發, 並組合到一起, 集中顯示.
// 優化過的代碼, 直接獲取數組, 再分發, 再合並, 再顯示toast, Toast順次執行.
Observable.just(mManyWordList)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(mOneLetterFunc)
.reduce(mMergeStringFunc)
.subscribe(mToastAction);
這次是使用
just
分發數組, 則分發數據就是數組, 並不是數組中的元素.
flatMap
把數組轉換為單獨分發,Func1
內部使用from
拆分數組.
reduce
把單獨分發數據集中到一起, 再統一分發, 使用Func2
.
最終使用Action1
顯示獲得數據. 本次代碼也更加簡潔.
由此我們可以觀察到, Rx的寫法可以是多種多樣, 合理的寫法會更加優雅.
效果
Lambda表達式和Rx非常契合, 可以省略大量的內部類, 如Func和Action.
我們把上個示例, 用Lambda再寫一次, 功能相同.
/**
* Lambda表達式寫法
*
* Created by wangchenlong on 15/12/31. */ public class LambdaActivity extends Activity { @Bind(R.id.simple_tv_text) TextView mTvText; final String[] mManyWords = {"Hello", "I", "am", "your", "friend", "Spike"}; final List
這次沒有使用常規的Lambda表達式, 而是更簡單的
方法引用(Method References)
.
方法引用: 方法參數和返回值與Lambda表達式相同時, 使用方法名代替.
Retrofit是網絡請求庫, 剛推出2.0版本. Rx的一個核心應用就是處理異步網絡請求, 結合Retrofit, 會更加方便和簡潔. 參考.
引入庫
compile 'com.android.support:recyclerview-v7:23.1.1' // RecyclerView
compile 'com.squareup.retrofit:retrofit:2.0.0-beta2' // Retrofit網絡處理
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2' // Retrofit的rx解析庫
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2' // Retrofit的gson庫
compile 'com.squareup.picasso:picasso:2.5.2' // Picasso網絡圖片加載
recyclerview
和picasso
為了顯示.retrofit
系列是網絡請求.
主頁使用一個簡單的列表視圖, 展示Github的用戶信息.
/**
* Rx的網絡請求方式
*
* Created by wangchenlong on 15/12/31. */ public class NetworkActivity extends Activity { @Bind(R.id.network_rv_list) RecyclerView mRvList; // 列表 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_network); ButterKnife.bind(this); // 設置Layout管理器 LinearLayoutManager layoutManager = new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); mRvList.setLayoutManager(layoutManager); // 設置適配器 UserListAdapter adapter = new UserListAdapter(this::gotoDetailPage); NetworkWrapper.getUsersInto(adapter); mRvList.setAdapter(adapter); } // 點擊的回調 public interface UserClickCallback { void onItemClicked(String name); } // 跳轉到庫詳情頁面 private void gotoDetailPage(String name) { startActivity(NetworkDetailActivity.from(NetworkActivity.this, name)); } }
在列表中提供點擊用戶信息跳轉至用戶詳情.
NetworkWrapper.getUsersInto(adapter)
請求網絡, 設置適配器信息.
關鍵部分, 適配器, 其中包含ViewHolder類和數據類.
/**
* 顯示列表
*
* Created by wangchenlong on 15/12/31. */ public class UserListAdapter extends RecyclerView.Adapter
添加數據
addUser
, 其中notifyItemInserted
通知更新.
可以自動生成Json解析類的網站.
首先創建`Retrofit``服務, 通過服務獲取數據, 再依次分發給適配器.
/**
* 用戶獲取類
*
* Created by wangchenlong on 15/12/31. */ public class NetworkWrapper { private static final String[] mFamousUsers = {"SpikeKing", "JakeWharton", "rock3r", "Takhion", "dextorer", "Mariuxtheone"}; // 獲取用戶信息 public static void getUsersInto(final UserListAdapter adapter) { GitHubService gitHubService = ServiceFactory.createServiceFrom(GitHubService.class, GitHubService.ENDPOINT); Observable.from(mFamousUsers) .flatMap(gitHubService::getUserData) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(adapter::addUser); } // 獲取庫信息 public static void getReposInfo(final String username, final RepoListAdapter adapter) { GitHubService gitHubService = ServiceFactory.createServiceFrom(GitHubService.class, GitHubService.ENDPOINT); gitHubService.getRepoData(username) .flatMap(Observable::from) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(adapter::addRepo); } }
網絡請求無法在主線程上執行, 需要啟動異步線程, 如
Schedulers.newThread()
.
使用工廠模式ServiceFactory
創建服務, 也可以單獨創建服務.
創建Retrofit
服務的工廠類.
/**
* 工廠模式
*
* Created by wangchenlong on 15/12/31. */ public class ServiceFactory { public static
這是Retrofit 2.0的寫法, 注意需要添加Rx和Gson的解析.
設置網絡請求的Url.
/**
* GitHub的服務
*
* Created by wangchenlong on 15/12/31. */ public interface GitHubService { String ENDPOINT = "https://api.github.com"; // 獲取個人信息 @GET("/users/{user}") Observable
顯示用戶
詳情頁面與主頁類似, 參考代碼, 不做細說.
Rx的好處之一就是可以防止內存洩露, 即根據頁面生命周期, 處理異步線程的結束. 可以使用RxLifecycle庫處理生命周期.
Activity
類繼承RxAppCompatActivity
, 替換AppCompatActivity
.
啟動一個循環線程.
/**
* Rx的線程安全
*
* Created by wangchenlong on 15/12/31. */ public class SafeActivity extends RxAppCompatActivity { private static final String TAG = "DEBUG-WCL: " + SafeActivity.class.getSimpleName(); @Bind(R.id.simple_tv_text) TextView mTvText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_simple); ButterKnife.bind(this); Observable.interval(1, TimeUnit.SECONDS) .observeOn(AndroidSchedulers.mainThread()) .compose(bindToLifecycle()) // 管理生命周期, 防止內存洩露 .subscribe(this::showTime); } private void showTime(Long time) { mTvText.setText(String.valueOf("時間計數: " + time)); Log.d(TAG, "時間計數器: " + time); } @Override protected void onPause() { super.onPause(); Log.w(TAG, "頁面關閉!"); } }
繼承RxAppCompatActivity
, 添加bindToLifecycle
方法管理生命周期. 當頁面onPause
時, 會自動結束循環線程. 如果注釋這句代碼, 則會導致內存洩露.
RxBinding是Rx中處理控件異步調用的方式, 也是由Square公司開發, Jake負責編寫. 通過綁定組件, 異步獲取事件, 並進行處理. 編碼風格非常優雅.
除了RxJava, 再添加RxBinding的依賴.
// RxBinding
compile 'com.jakewharton.rxbinding:rxbinding:0.3.0'
compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.3.0'
compile 'com.jakewharton.rxbinding:rxbinding-design:0.3.0'
Toolbar和Fab, 兩個較新的控件.
兩個EditText控件, 對比傳統方法和RxBinding方法.
使用ButterKnife注入控件, 使用RxBinding方式綁定控件, 異步監聽事件.
/**
* Rx綁定
*
* Created by wangchenlong on 16/1/25. */ public class BindingActivity extends AppCompatActivity { @Bind(R.id.rxbinding_t_toolbar) Toolbar mTToolbar; @Bind(R.id.rxbinding_et_usual_approach) EditText mEtUsualApproach; @Bind(R.id.rxbinding_et_reactive_approach) EditText mEtReactiveApproach; @Bind(R.id.rxbinding_tv_show) TextView mTvShow; @Bind(R.id.rxbinding_fab_fab) FloatingActionButton mFabFab; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_binding); ButterKnife.bind(this); initToolbar(); // 初始化Toolbar initFabButton(); // 初始化Fab按鈕 initEditText(); // 初始化編輯文本 } // 初始化Toolbar private void initToolbar() { // 添加菜單按鈕 setSupportActionBar(mTToolbar); ActionBar actionBar = getSupportActionBar(); // 添加浏覽按鈕 if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } RxToolbar.itemClicks(mTToolbar).subscribe(this::onToolbarItemClicked); RxToolbar.navigationClicks(mTToolbar).subscribe(this::onToolbarNavigationClicked); } // 點擊Toolbar的項 private void onToolbarItemClicked(MenuItem menuItem) { String m = "點擊\"" + menuItem.getTitle() + "\""; Toast.makeText(this, m, Toast.LENGTH_SHORT).show(); } // 浏覽點擊 private void onToolbarNavigationClicked(Void v) { Toast.makeText(this, "浏覽點擊", Toast.LENGTH_SHORT).show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_rxbinding, menu); return super.onCreateOptionsMenu(menu); } // 初始化Fab按鈕 private void initFabButton() { RxView.clicks(mFabFab).subscribe(this::onFabClicked); } // 點擊Fab按鈕 private void onFabClicked(Void v) { Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "點擊Snackbar", Snackbar.LENGTH_SHORT); snackbar.show(); RxSnackbar.dismisses(snackbar).subscribe(this::onSnackbarDismissed); } // 銷毀Snackbar, event參考{Snackbar} private void onSnackbarDismissed(int event) { String text = "Snackbar消失代碼:" + event; Toast.makeText(this, text, Toast.LENGTH_SHORT).show(); } // 初始化編輯文本 private void initEditText() { // 正常方式 mEtUsualApproach.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { mTvShow.setText(s); } @Override public void afterTextChanged(Editable s) { } }); // Rx方式 RxTextView.textChanges(mEtReactiveApproach).subscribe(mTvShow::setText); } }
Toolbar使用RxToolbar監聽點擊事件; Snackbar使用RxSnackbar監聽;
EditText使用RxTextView監聽; 其余使用RxView監聽.
說到圖像處理,第一件事就是要從手機相冊選擇圖片,然後才是處理。其實,用代碼實現從手機相冊選擇一張圖片其實非常簡單:添加一個Button,id設為btnOpen;對該But
數據庫 SQLiteOracle SQLServer mySql SQLite 關系型數據SQLite 數據庫Android系統中集成了輕量級的數據SQLite一, 特點
網上文章雖多,但是這種效果少之又少,我真誠的獻上以供大家參考實現原理:自定義ImageView對此控件進行相應的layout(動態布局).這裡你要明白幾個方法執行的流程:
1.概述回到正題,這次帶來的效果,是一個Android 的3D立體旋轉的效果。當然靈感的來源,來自早些時間微博上看到的效果圖。非常酷有木有!作為程序猿我當然要把它加入我的