編輯:關於Android編程
高層不應該知道低層的細節,應該是面向抽象的編程。業務的實現交給實現的接口的類。高層只負責調用。
首先,要介紹一下一個項目中好架構的好處:好的軟件設計必須能夠幫助開發者發展和擴充解決方案,保持代碼清晰健壯,並且可擴展,易於維護,而不必每件事都重寫代碼。面對軟件存在的問題,必須遵守SOLID原則(面向對象五大原則),不要過度工程化,盡可能降低框架中模塊的依賴性。
之前的一段時間,學習了一些新的技術,並把自己關注的技術整合了一下,是的,相似的技術有很多,自己擇優選擇,將它們的思想和技術應用到了自己的搭建的項目框架中.
限於自己能力水平有限,自己搭建的項目可能還有些不足,歡迎大家指正批評,讓自己的想法和設計思想走向正軌.O(∩_∩)O謝謝~
1.項目整體框架: 利用google-clean-architecture的思想 來負責項目的整體MVP架構.
MVP是模型(Model)、視圖(View)、主持人(Presenter)的縮寫,分別代表項目中3個不同的模塊。我以登錄為例子,進行說明.
這裡每個業務首先要有一個管理接口Contract,在這裡面有三個接口來面向接口編程, (Model),(View),(Presenter). 將三個接口放在一起便於管理.
/**
* 登錄關聯接口類
*
* Created by ccj on 2016/7/7.
*/
public interface LoginContract {
interface View extends BaseView {
void showProgress();
void hideProgress();
void showError(String error);
void navigateToMain();
void navigateToRegister();
}
interface Presenter extends BasePresenter {
void login(String username, String password);
void onDestroy();
}
interface Model{
void saveUserInfo(User user);
void saveLoginState(Boolean isLogin);
void saveRememberPass(User user);
}
}
模型(Model):實現 implements LoginContract.Model 負責處理數據的加載或者存儲,比如從網絡或本地數據庫獲取數據等;這裡的login 涉及到的業務邏輯比較少請求網絡 采用了rxjava +retroft+gsons 相當於 model層. 如果處理的出具多,就采用此model ,就像圖片保存顯示等等.
視圖(View):采用接口的方式,讓activity實現該接口,接口中有關於視圖的方法,例如”initVIew()”,”showDialog()”,”hideDialog()”等等, 負責界面數據的展示,與用戶進行交互;
public class LoginActivity extends BaseActivity implements LoginContract.View {
//省略bufferknife 注解
private LoginPresenter presenter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.bind(this);
presenter=new LoginPresenter(this);
presenter.start();//初始化控制層
}
//實現於view的方法
@Override
public void navigateToMain() {
Intent intent =new Intent(getBaseContext(),MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
主持人(Presenter):持有 view和model的對象,操作兩者的方法.相當於協調者,是模型與視圖之間的橋梁,將模型與視圖分離開來,對view 和model 進行調度操作。
/**
* login的presenter層 進行對view 和 model 的控制,
* Created by ccj on 2016/7/7.
*/
public class LoginPresenter implements LoginContract.Presenter {
private LoginContract.View loginView;
public LoginPresenter(LoginContract.View loginView) {
this.loginView = loginView;
}
@Override
public void login(String username, String password) {
loginView.showProgress();
Observable userObservable = APIService.userLogin(username, password);
userObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
loginView.hideProgress();
}
@Override
public void onError(Throwable e) {
TLog.log(e.getMessage().toString());
loginView.hideProgress();
loginView.showError(e.getMessage().toString());
}
@Override
public void onNext(User getIpInfoResponse) {
TLog.log(getIpInfoResponse.toString());
loginView.navigateToMain();
}
});
}
@Override
public void start() {
}
2.網絡訪問: 采用rxjava+retrofit+gson進行網絡訪問,並輕松的將json轉為對象,結構清晰,使用方便.
在APIService中初始化retrofit
/**
* 調用後台的接口,架構網絡層采用Retroft+Rxjava+gson
* Created by ccj on 2016/7/1.
*
*/
public class APIService {
private static final String TAG = "APIService";
public static final String URL_HOST ="http://123.234.82.23" ;//服務器端口
/**
* 基礎地址
* 初始化 retroft
*/
private static final Retrofit sRetrofit = new Retrofit.Builder()
.baseUrl(URL_HOST)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 使用RxJava作為回調適配器
.build();
private static final RetrofitRequest apiManager = sRetrofit.create(RetrofitRequest.class);
/**
* 登錄,返回,我這邊用的是json格式的post,大家可以進行選擇
* @param city
* @return
*/
public static Observable userLogin(String format, String city) {
HashMap hashMap =new HashMap<>();
hashMap.put("UserPhone", format);
hashMap.put("UserPassWord", city);
TLog.log(hashMap.toString());
Observable ss = apiManager.userLogin(hashMap);
return ss;
}
/**********************仿照上面的方法,進行請求數據****************************/
用retrofit訪問 返回observable的對象
public interface RetrofitRequest {
boolean isTest=true; //是否在測試環境下
//發布之前更改
String BASE_URL_TEST = "/flyapptest/";//測試服務器
String BASE_URL_OFFICAL = "/flyapp/";//正式服務器
String BASE_URL = isTest?BASE_URL_TEST:BASE_URL_OFFICAL;//發布服務器
/**
* 登錄返回(json post)
* @param body
* @return
*/
@Headers( "Content-Type: application/json" )
@POST(BASE_URL+"Login.ashx/")
Observable userLogin(@Body HashMap body);
3.異步處理: 采用rxjava響應式框架進行優雅的異步處理,簡化代碼邏輯,並且很好的解決內存洩漏 問題.(相關模塊在TakePhoto業務中)
/**
* rxjava 進行異步操作 eventBus進行時間傳遞
* @param data
*/
@Override
public void savePhoto(final Intent data) {
TLog.log("savePhoto", "data-->" + data.getData().toString());
Log.e("Tlog-->", "data-->" + data.getData().toString());
saveObservable = Observable.fromCallable(new Callable() {
@Override
public String call() throws Exception {//通知調用 並返回string
return savePic(data);//此方法在io線程中調用 並返回
}
});
saveSubscription = saveObservable
.subscribeOn(Schedulers.io())//observable在調度中的IO線程中進行調度進行
.observeOn(AndroidSchedulers.mainThread())//在主線程中進行觀察
.subscribe(new Observer() {//訂閱觀察者
@Override
public void onCompleted() {
Log.e("Tlog-->", "onCompleted-->");
}
@Override
public void onError(Throwable e) {
Log.e("Tlog-->", "Throwable-->" + e.getMessage().toString());
EventBus.getDefault().post(new EventUtils.ObjectEvent(e.getMessage().toString()));
}
@Override
public void onNext(String s) {//帶參數的下一步,在此就是當
Log.e("Tlog-->", "s-->" + s);
EventBus.getDefault().post(new EventUtils.ObjectEvent(bitmap));
}
});
}
4.事件訂閱: 采用EventBus作為事件總線,進行線程間,組件之間的通信.
/**
* 事件總線 用於組件或線程通信,可替代回調,廣播等
* Created by ccj on 2016/4/14.
*/
public class EventUtils {
/**
* object類型(即傳統的所有類型,都可以強轉進行傳遞事件)
*/
public static class ObjectEvent{
private Object object;
public ObjectEvent(Object object) {
// TODO Auto-generated constructor stub
this.object = object;
}
public Object getMsg(){
return object;
}
}
}
5.代碼分包: 根據業務區分進行分包,便於對代碼進行管理 .
6. 工具類: TDeviceUtils設備狀態的工具類,,SeriliazebleUtils 序列化工具類,SharepreferenceUtils保存工具類,
相關請參考代碼
7.app棧管理: 基於baseActivity,很好的釋放內存,管理內存.
相關請參考代碼
異常捕獲(待完善)
測試框架Espresso/JUnit/Mockito/Robolectric (待完善)
gradle相關構建編譯
1.層次分明,各層級之間都不管對方如何實現,只關注結果;
2.在視圖層(Presentation Layer)使用MVP架構,使原本臃腫的Activity(或Fragment)變得簡單,其處理方法都交給了Presenter。
3.易於做測試,只要基於每個模塊單獨做好單元測試就能確保整體的穩定性。
4.易於快速迭代,基於代碼的低耦合,只需在業務邏輯上增加接口,然後在相應的層級分別實現即可,絲毫不影響其他功能。
1. 功能介紹1.1 Android Universal Image LoaderAndroid Universal Image Loader 是一個強大的、可高度定制的
效果圖:這種效果的實現這裡是采用自定義ExpandableListView,給它設置一個指示布局,在滑動過程中監聽當前是否應該懸浮顯示分類來實現的。今天抽時間,整理了下代
實現功能:已存在歌曲歌詞下載後續將博文,將實現已下載音樂掃描功能。因為,沒有自己的服務器,所以網絡音樂所有相關功能(包含搜索音樂、下載音樂、下載歌詞)均無法保證時效性,建
In recent years,mobile platform become more and more popular!At present,the flourishi