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