編輯:Android資訊
我們先假設一個場景需求:剛有孩子的爸爸媽媽對用照片、視頻記錄寶寶成長有強烈的意願,但苦於目前沒有一款專門的手機APP做這件事。A公司洞察到市場需求,要求開發團隊盡快完成Android客戶端的開發。以下模擬團隊和工作開展。
先給出服務端的架構圖。

由於服務端開發有Java、PHP背景,為了快速完成開發任務,我們選擇PHP作為服務端開發語言,順便也把數據庫定為MySQL。考慮後期擴展和數據庫訪問性能,擬引入Redis非關系型數據庫。同時為了提高數據讀的性能,在雲服務器和數據庫之間用上緩存,並為數據庫主從備份、讀寫分離。服務器就不搭建在本地了,管理是一大問題。現在雲服務器一大把,七牛、阿裡雲、騰訊雲、百度雲、金山雲等等,技術成熟,而且價格還算公道。在此我們選擇阿裡雲。為了應對可能面臨的並發問題,雲服務器要考慮負載均衡。項目中可能存在大量的需要上傳和下載照片和視頻,我們選擇阿裡雲的開放存儲服務,同時為了提升各個地區的下載體驗,我們引入CDN。客戶端通過API Service和服務端交換數據,圖片和視頻的下載直接通過CDN。
根據需求和原型設計,可能的模塊劃分如下:
服務端與客戶端使用JSON交換數據,使用自定義JSON格式,約定返回code、message,實體封裝在result中,支持單個實體、實體列表、多個實體列表,定義如下:
{
"code":500,
"message":"系統異常,請稍後重試",
"result":""
}
{
"code":200,
"message":"登錄成功",
"result":{
"user":{
"userId":1,
"nickName":"Leo",
"email":"Leo@xxx.com",
"gender":0
}
}
}
{
"code":200,
"message":"SUCCESS",
"result":{
"album":{
"kid":{
"kidId":1,
"nickName":"LEE",
"gender":1,
"birthday":"2014-3-6",
......
},
"media":{
"mediaId":123,
"mediaType":1,
"createdTime":193743546746,
"mediaDescription":"這是小孩第一次出去春游",
......
}
}
}
}
主要API接口設計如下:
http://api.xxx.com/service/v1.0/user/login http://api.xxx.com/service/v1.0/user/third-login http://api.xxx.com/service/v1.0/user/register http://api.xxx.com/service/v1.0/user/logout http://api.xxx.com/service/v1.0/user/info/update http://api.xxx.com/service/v1.0/album/upload http://api.xxx.com/service/v1.0/album/update http://api.xxx.com/service/v1.0/album/delete ......
也許你看到了,API做了二級域名映射,同時為了服務端後期API版本的升級管理,在URL中加上了版本標識V1.0。命名方面我盡量做到restful的風格。對了,此處沒有使用Https。為了解決數據傳輸的安全,我做了點特別的處理:對請求體和響應結果進行RSA加密(如果服務端返回的數據稍稍過大,這個RSA嚴重影響客戶端解密,後來我換成了AES),所有請求為POST請求,所以API URL後面沒有帶參數,你也看不到任何請求相關的信息。
根據需求和原型設計,數據庫的設計大概需要兩周時間。其實一周基本搞定了,但為了考慮充分,留出一周時間來檢驗和調整。數據庫E-R圖略。
Android本身就是MVC,所以我不打算引入MVP或MVVM。我的理念是職責分層,快速推出Android 1.0。主要的包結構如下:

工程的搭建和包的劃分有各種各樣的,適合自己的就行了。想討論或想看別人怎麼做的,點擊這裡:App工程結構搭建:幾種常見Android代碼架構分析
注冊登錄,個人信息,我的小孩,相冊管理,消息通知,系統設置等等。
重復發明輪子是不可取的。有些模塊根本沒必要自己寫。以下是引入的第三方庫,以及優勢說明。
public interface DataCallback {
void onSuccess(Object result);
void onFailure(Object result);
}
先看一下登錄的序列圖:

HttpManager類負責調用AsyncHttpWrapper中的post方法,和對服務端返回的數據解密、JSON轉對象、回調上層;AsyncHttpWrapper則負責請求體的封裝加密和其它的校驗參數封裝。看一下HttpManager類的post方法:
public void post(Context context, String url, RequestParams params, final String modelName,
final DataCallback callback) {
AsyncHttpWrapper.post(context, url, params, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
try {
if (modelName != null) {
handleResponse(responseBody, callback, modelName);
} else {
String response = new String(responseBody);
// 解密
response = AES128.getInstance().decrypt(AppUtil.decodeReplace(response));
// JSON轉對象
BaseMessage message = AppUtil.getMessage(response);
if (callback != null) {
// 如果自定義code是200
if (Coder.CODE_200.equals(message.getCode())) {
callback.onSuccess(message.getMessage());
} else {
callback.onFailure(new ServerError(message.getCode()));
}
}
}
} catch (JSONException e) {
LogUtil.e(e);
callback.onFailure("服務端返回的數據不能解析成JSON");
} catch (Exception e) {
LogUtil.e(e);
callback.onFailure(e);
}
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
if (callback != null) {
callback.onFailure(error);
if (responseBody != null) {
String s = new String(responseBody);
LogUtil.e(s);
}
}
}
});
}
AsyncHttpWrapper中的post方法
public static void post(Context context, String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
// 設置請求頭部信息
generateHeader(context);
// 加密請求參數
String encryParams = AES128.getInstance().encrypt(params.toString());
RequestParams requestParams = new RequestParams();
requestParams.put("param", AppUtil.encodeReplace(encryParams));
client.post(context, url, requestParams, responseHandler);
}
private static AsyncHttpClient client = new AsyncHttpClient();
為了加快開發速度,重用代碼,Adapter的使用有技巧。每次在getView中查找控件id、利用ViewHolder、賦值,最後返回convertView,看著都是差不多的代碼。是時候脫離這個苦海了。先看怎麼解決共用的ViewHolder問題。
public static <T extends View> T get(View view, int id) {
SparseArrayCompat<View> viewHolder = (SparseArrayCompat<View>) view.getTag();
if (viewHolder == null) {
viewHolder = new SparseArrayCompat<>();
view.setTag(viewHolder);
}
View childView = viewHolder.get(id);
if (childView == null) {
childView = view.findViewById(id);
viewHolder.put(id, childView);
}
return (T) childView;
}
ViewHolder的作用,就是通過convertView.setTag與convertView進行綁定。當convertView復用時,直接從與之對應的ViewHolder(getTag)中拿到convertView布局中的控件,省去了findViewById的時間。上面的代碼就是這樣的原理。
然後就是CommonAdapter了。
public abstract class CommonAdapter<T> extends BaseAdapter {
protected LayoutInflater inflater;
protected Context context;
protected List<T> datas;
protected final int itemLayoutId;
public CommonAdapter(Context context, List<T> datas, int itemLayoutId) {
this.context = context;
this.inflater = LayoutInflater.from(context);
this.datas = datas;
this.itemLayoutId = itemLayoutId;
}
@Override
public int getCount() {
return datas != null ? datas.size() : 0;
}
@Override
public T getItem(int position) {
return datas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final CommonViewHolder viewHolder = getViewHolder(position, convertView, parent);
convert(viewHolder, getItem(position), position);
return viewHolder.getConvertView();
}
public abstract void convert(CommonViewHolder viewHolder, T item, int position);
}
好了,不貼代碼了。看不明白了請點擊這裡:Android 快速開發系列 打造萬能的ListView GridView 適配器
如AES128加密類、BitmapUtils、SecurePreferences、StringUtil、ToastUtil、IOUtil等等。
為了保證數據交換、加解密正常,首先對某一個接口進行測試,以驗證API Service能正常跑通。比如可以先對登錄進行模擬測試,看是否成功,同時包括異常的測試,服務端是不是處理了邊界異常,返回給客戶端的都是封裝過的異常信息,而不是拋一個敏感信息給客戶端。提前進行接口測試有助於我們的基礎組件運行沒問題,方便後期其它模塊的快速集成。
基礎組件封裝好後,除了少量的從網絡獲取數據邏輯和本地數據庫的增刪改查,客戶端基本上就是界面的布局工作了。界面開發基本看熟練程度和自定義View的重用。
好了,基本就這些。兩個Android開發人員兩個月內完成肯定是可以的,前提是至少有一個熟手。後面再談談MVP,畢竟這個客戶端設計沒法進行單元測試,如果業務邏輯越來越復雜,Activity的職責會越來越重,問題多多,不利於後期維護。
Android項目的依賴關系解析
在Android SDK Tools和Eclipse ADT 插件的第17版本 (revision 17)中,我們對Android項目的依賴關系管理做了很多改變。
Android下如何把屏幕錄制演示生成GIF動態圖
最近想錄制一段視頻用來演示自己的作品 XBrowser 的網址補全及搜索提示功能 , 通過屏幕錄制生成的.mp4文件把視頻放到”某酷” 視
iOS 和 Android 哪個更利於賺錢呢?
一直有一個幾乎所有機友都認同的說法:Android 是全球用戶數最多的操作系統,市場份額十分驚人。無論是問你的表哥表姐,還是弟弟妹妹,即便他們不是很關注移動領域或
檢測和解決Android應用的性能問題
前言 無論你的應用多麼有創新性、有用,如果它卡得要命,或者非常消耗內存,那麼每人將會願意使用它。 因此,性能變得尤為重要。當你忙碌於構建精美的用戶界面或者完成新的