Android網絡應用開發從零開始做起


動機

首先自己不是做應用的,一直以來從事的都是網絡游戲客戶端的開發。一是想了解一下應用開發的流程與技術點,二是看到園子里有人做的app,賺錢了,看的有點手癢癢,呵呵,三是Android應用開發,工資待遇比游戲開發要好,自己也好好學習下,為自己找條后路吧。

 

說明

因為是從零做起,所以好多問題都是第一次遇到。主要寫一些碰到的一些設計和技術難題,主要以客戶端為主,因為是首次接觸。可能對高手來說,是小菜了。 但都有個過程吧。慢慢也就懂了。希望園子里面的朋友多多指教,多拍磚,找到最優的方案。希望自己能夠堅持寫完,自己的app能夠早日上架。

 

主要內容

  1. 服務端客戶端框架的設計及通訊方式
  2. 網絡請求數據,動態加載listview中的數據項
  3. 服務端客戶端關於會話session的狀態保存
  4. ActivityGroup 對子Activity的管理 還有 子Activity之間的切換
  5. 內存管理方式的選擇
  6. ListView 中ArrayAdapter中刪除Item
  7. 持續更新中……目前已完成60%

 

--------------------------------------------------------------------------------------------

--------------------------服務端客戶端框架的設計及通訊方式------------------

--------------------------------------------------------------------------------------------

服務端:

服務端使用MVC的框架,dao,manager,servlet 三層。感覺不像嚴格意義上的MVC,有個型就好了。能夠分層次,處理不同水平方向上的東西。有一個handler處理接口。根據請求使用不同的handler來進行處理。相當於一個算法簇。用了策略模式。 Servlet分兩種,一種是處理服務端后台的管理,一種是處理客戶端請求的。 客戶端請求的servlet只需要一個就可以了。根據不同的請求,分別使用不同的handler來進行處理。

客戶端:

客戶端由activity,adapter,bean,handler,net,util這幾部分進行組成。 Adapter相當於activity和請求數據之間的橋梁,使用了適配器模式,handler和服務端的功能差不多。在這一片文章中也分析過。記一電子商城android客戶端初探解析Net包為網絡包,網絡分為兩部分,一部分是當進入一個界面時,頁面的請求,頁面的異步加載,第二部分是當有圖片的時候,圖片的異步加載。

 

 

--------------------------------------------------------------------------------------------

--------------------------網絡請求數據,動態加載listview中的數據項-------

--------------------------------------------------------------------------------------------

關於這個網上的做法有很多。但都是本地數據定死的。相應都是及時的。當有網絡請求數據的時候,就需要多處理一些東西。例如網絡請求期間,onScroll方法可能會持續的執行等等。

這里需要幾個變量參數來進行協作。

    private int curPage;

private static int maxCount;

  private boolean havaFooterView;//是否有腳view

 

在剛開始的時候,需要

        // 添加到腳頁顯示

list.addFooterView(loadingLayout);

havaFooterView = true;



然后系統會執行onScroll方法

    @Override

public void onScroll(AbsListView view, int firstVisibleItem,

int visibleItemCount, int totalItemCount)

{



//當加載的時候,不在請求網絡

if (isLoading())

{

return;

}



if (firstVisibleItem + visibleItemCount == totalItemCount

&& havaFooterView)

{

Log.i("test", "Scroll>>>first: " + firstVisibleItem + ", visible: "

+ visibleItemCount + ", total: " + totalItemCount);

lastItem = firstVisibleItem + visibleItemCount - 1;

Log.i("test", "Scroll>>>lastItem:" + lastItem);

Log.i("test", "Scroll>>>adapter count:" + adapter.getCount());

requestNet();

}



}



執行查詢的條件是,不在加載中,並且拉到了底部,而且還有腳view的情況下。一定要切記了。條件判定不充分,可能會有多余的報文產生。

那么怎么解析數據呢?看下面的代碼

    @Override

protected void processNetData(Object obj)

{

HashMap data = (HashMap)obj;

maxCount = Integer.parseInt(data.get("maxCount").toString());

ArrayList<CaipuDetail> tempCaipuDetails = (ArrayList) data.get("caipuList");

caipuDetails.addAll(tempCaipuDetails);

//如果適配器里面的數據數少於最大數就增加適配器數量,

//當這一次增加的數量是最大數量的時候,就刪除腳view

//同時設置havaFooterView為假

adapter.setCount(caipuDetails.size());

if (adapter.getCount() == maxCount)

{

list.removeFooterView(loadingLayout);

havaFooterView = false;

}

//重新刷新Listview的adapter里面數據

adapter.notifyDataSetChanged();

setLoading(false);

}



注意什么時候去除腳view,設置havaFooterView為false,讓進度條的狀態為false。

 

--------------------------------------------------------------------------------------------

--------------------------服務端客戶端關於會話session的狀態保存-----------

--------------------------------------------------------------------------------------------

一般的做法是這樣的:當客戶端登錄的時候,服務端生成一個session回話,並保存登錄Account賬號信息,並且返回一個sessionId給客戶端,客戶端解析后保存這個sessionId,並且每次發送報文的時候,附帶着sessionId在頭域中。這樣就可以保持回話狀態了。如果一段時間不請求服務器,回話可能會失效。這時候就需要重新登陸了。

我遇到的問題是,服務端的我是用session是否為null來判斷,回話是否過期的。但是會有這種情況,當session不為null,但是里面的值取不到,為空值,也就是上面保存的Account賬號信息,有一種解釋是session保存在散列表中,回話過期,應該是session中的屬性發生變化,但是並不為空。所以,我使用一個折中的辦法來判斷。就是也取得賬號信息,如果為空,就判斷過期。具體做法如下:

服務端:

  

  public JSONObject prepareData(HttpServletRequest request) {

HttpSession session = request.getSession(false);

Object accountObj =null;

if(session!=null){

accountObj= session.getAttribute("account");

}



if(null == session || null == accountObj){

//這里就轉向為登錄提示

return formatJSONObject();

}

Account account =(Account)accountObj;

System.out.println("最大不活動時間:"+session.getMaxInactiveInterval());

System.out.println("sessionId:"+session.getId());

int page = Integer.parseInt(request.getParameter("page"));

Favorites t = new Favorites();

t.setAccountId(account.getId());

List<Favorites> favorites = fm.selectByAccountId(t,page);

int maxCount = favorites.size();

return formatJSONObject(favorites,maxCount);

}

 

客戶端:

httpGet.setHeader("Cookie", "JSESSIONID=" + Logic.instance().getSessionId());


--------------------------------------------------------------------------------------------

--- ActivityGroup 對子Activity的管理 還有 子Activity之間的切換-----

--------------------------------------------------------------------------------------------

ActivityGroup這個控件經常被使用。 如果學過html的同學,應該知道iframe框架,我覺得他們兩個挺像的。個人理解。呵呵。 其實剛開始我對這個ActivityGroup是很陌生的,都沒用過,當時反編譯了一個不錯的應用的xml。發覺里面使用到了這個控件。 覺得不錯。不錯的原因是 如果不用這個控件,我們的做法一般是寫一個baseActivity,在它中進行處理,想想就覺得不合理吧。 基類中點擊某一個搜索,首頁或收藏的選項,在基類中調用。 覺得這樣的設計很不好。覺得違反了設計的原則。 這個控件就解決了這個問題。 呵呵。好像扯遠了。。。

 

ActivityGroup對子Activity有啟動,還可以切換自己的內容view來包含子activity中的view。但是發現子Activity不能finish。如果執行了finish就推出應用了。ActivityGroup執行了finish就是推出應用。 不知道什么原因。 還請高手指教。 子Activity之間的切換,也分兩種 ,一種是啟動原有的activity,一種是刪除原有的,啟動一個新的。

可以使用下面通用的方法來進行啟動子Activity,代碼如下:

 

   /**

* 加載子界面

*
@param id ActivityId

*
@param class1 類

*/

public void loadSubActivity(String id, Class class1,Bundle bundle,int flags)

{

contextView.removeAllViews();

Class subActivityClass = class1;

if (subActivityClass != null)

{

Intent in = new Intent(this,

subActivityClass);

//FLAG_ACTIVITY_SINGLE_TOP 如果沒有,添加一個。如果有顯示以前的

//Intent.FLAG_ACTIVITY_CLEAR_TOP 如果沒有添加一個,如果有刪除以前的,重新添加一個

in.addFlags(flags);

// in.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

// in.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

if(bundle!=null){

in.putExtras(bundle);

}

Window mWindow = getLocalActivityManager().startActivity(id, in);

contextView.addView(mWindow.getDecorView());

}

}

 

 

 

--------------------------------------------------------------------------------------------

--------------------------內存管理方式的選擇---------------------------------------

--------------------------------------------------------------------------------------------

據我所知的內存管理方式有兩種:一種是軟引用,一種是記錄資源所用的次數,當為0時,及時釋放掉。

在應用中一般使用軟引用。

private HashMap<String, SoftReference<Drawable>> imageCache;



這個網上有很多例子,可以查找下。做法就是在當加進來一張圖片時,根據它的鏈接地址作為鍵值,如果這張圖片在這個哈希表中,就返回它的軟引用,然后產生對對象,如果不存在,然后看本地緩存中有沒有,有的話就返回對象,沒有的話 ,就保存圖片到本地。為以后再用。

在游戲中一般使用后一種做法,及時的消除資源。

原理就是,當增加一個界面時,計算界面中有多少圖片,然后有個圖片管理計數器,計算相同的圖片被加進去多少次,當關閉這個界面的時候,圖片被使用的數目減1,當為0的時候,就釋放掉這種圖片。

 

--------------------------------------------------------------------------------------------

-------------------------- ListView 中ArrayAdapter中刪除Item-----------------

--------------------------------------------------------------------------------------------

看到網上最簡單的回答是這樣的:

//                                adapter.remove(favorite);

// adapter.notifyDataSetChanged();



我信以為真。結果。你懂得。出錯了。呵呵。

問題出在哪里。一般ArrayAdapter中有下面兩個變量:

private List<Favorites> favorites;

private int count;



是這兩個變量出了問題?是的。 只更改上面的還不夠, 還需要更改適配器的管理數據的數量和數據。代碼如下:

 

           maxCount = Integer.parseInt(data.get("maxCount").toString());

ArrayList<Favorites> tempFavorites = (ArrayList) data.get("favoritesList");

favoritesList = tempFavorites;

//如果適配器里面的數據數少於最大數就增加適配器數量,

//當這一次增加的數量是最大數量的時候,就刪除腳view

//同時設置havaFooterView為假

adapter.setCount(favoritesList.size());

adapter.setFavorites(favoritesList);

if (adapter.getCount() == maxCount && havaFooterView)

{

list.removeFooterView(loadingLayout);

havaFooterView = false;

}

//重新刷新Listview的adapter里面數據

adapter.notifyDataSetChanged();

 

當我們這樣寫的時候。特別要注意adapter.setFavorites(favoritesList);這句話。剛開始我的做法是這樣的,favoritesList = tempFavorites,后面沒有其他操作了。這樣就是兩個引用了。 數據還是沒有更新。 當時很煩躁,沒太注意這個東西。如果單獨的改變里面的值倒是可以的。還是一個引用。如果直接指到另一個引用。就有問題了。 呵呵。 感覺各位應該都不會犯這種低級問題吧。呵呵。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM