当前位置: 移动技术网 > 移动技术>移动开发>Android > Android开发之规划项目结构的实例

Android开发之规划项目结构的实例

2018年10月27日  | 移动技术网移动技术  | 我要评论

规划android项目结构

1.1建立androidlab类库,将与业务无关的逻辑转移到androidlab。androidlab至少包括五大部分:包名+ acticity,cache,net,ui,utils 。

activity包里面存放的是与业务无关的activity基类。net包里存放的是网络底层封装。cacahe包里面存放的是缓存数据的图片和图片的相关处理。ui包中存放的是自定义控件utils包中存放的是各种与类无关的功用方法。

将主项目的类分门别类的进行划分,放置在各种包中。

activity:我们按照模块继续划分,将不同模块的activity划分到不同的包中。adapter:所有的适配器都放在一起entity:所有的实体都放在一起db:sqlite相关逻辑的封装engine:将业务相关的类都放在一起ui:将自定义控件都放在这个包里utils:将所有的公用方法都放在这里interfaces:真正意义上的接口,命名以i作为开头listener:基于listener的接口,命名以on作为开头

1.2 为activity定义新的生命周期

可以把oncreate方法拆成三个子方法

initvariables:初始化变量,包括intent带的数据和activity内的变量initviews:加载layout布局文件,初始化控件,为控件挂上事件方法loaddata:调用mobileapi获取数据

1.3 统一事件模型

只要在一个团队内部达成了协议,决定使用某种事件编程方式,所有开发人员就要按照同样的方式编写代码。

1.4 实体化编程

1.4.1在网络请求中使用实体

一些开发人员不使用实体化编程,在获取mobileapi网络请求返回的json数据时,使用jsonobject或者jsonarray来承载数据,然后把返回的数据当作一个字典,根据键取出响应的值。介绍fastjson和gson这种实体化编程的方式.

使用fastjson如下

weatherentity weatherentity = json.parseobject(content,
						weatherentity.class);
				weatherinfo weatherinfo = weatherentity.getweatherinfo();
				if (weatherinfo != null) {
					tvcity.settext(weatherinfo.getcity());
					tvcityid.settext(weatherinfo.getcityid());
				}

使用gson如下:

gson gson = new gson();
				weatherentity weatherentity = gson.fromjson(content,
						weatherentity.class);
				weatherinfo weatherinfo = weatherentity.getweatherinfo();
				if (weatherinfo != null) {
					tvcity.settext(weatherinfo.getcity());
					tvcityid.settext(weatherinfo.getcityid());
				}

1.4.3 在页面跳转中实现实体

在一个页面中,数据的来源有两种:

调用mobileapi获取json数据从上一个页面传递过来

activity之间的数据传递一个偷懒的办法是设置一个全局变量,在来源页设置全局变量,在目标页接收全局变量。以下是来源页mainactivity的代码:

intent intent = new intent(mainactivity.this,
						loginactivity.class);
				intent.putextra(appconstants.email, "jianqiang.bao@qq.com");
								
				cinemabean cinema = new cinemabean();
				cinema.setcinemaid("1");
				cinema.setcinemaname("星美");

				//使用全局变量的方式传递参数
				globalvariables.cinema = cinema;
				startactivity(intent);

以下是目标页loginactivity的代码:

// 使用全局变量的方式传值
		cinemabean cinema = globalvariables.cinema;
		if (cinema != null) {
			cinemaname = cinema.getcinemaname();
		} else {
			cinemaname = "";
		}

不建议使用全局变量。app一旦被切换到后台,当手机内存不足的时候,就会回收这些全局变量,从而当app再次切换回前台时,再继续使用全局变量,就会因为它们为空而崩溃。如果必须使用全局变量,就一定要把它们序列化到本地。这样即使全局变量为空,也能从本地文件中恢复。

我们使用intent在页面间来传递数据实体的机制。

首先,在mainactivity中:

intent intent = new intent(mainactivity.this,
						loginnewactivity.class);
				intent.putextra(appconstants.email, "jianqiang.bao@qq.com");
				
				cinemabean cinema = new cinemabean();
				cinema.setcinemaid("1");
				cinema.setcinemaname("星美");
				
				//使用intent上挂可序列化实体的方式传递参数
				intent.putextra(appconstants.cinema, cinema);

				startactivity(intent);

其次,目标页loginactivity要这样写:

cinemabean cinema = (cinemabean)getintent()
				.getserializableextra(appconstants.cinema);
		if (cinema != null) {
			cinemaname = cinema.getcinemaname();
		} else {
			cinemaname = "";
		}

这里的cinemabean要实现serializable接口,支持序列化:

public class cinemabean implements serializable {
	private static final long serialversionuid = 1l;

	private string cinemaid;
	private string cinemaname;

	public cinemabean() {

	}

	public string getcinemaid() {
		return cinemaid;
	}

	public void setcinemaid(string cinemaid) {
		this.cinemaid = cinemaid;
	}

	public string getcinemaname() {
		return cinemaname;
	}

	public void setcinemaname(string cinemaname) {
		this.cinemaname = cinemaname;
	}

}

1.5 adapter模版不对adapter的写法进行规范,就会写出以下的adapter

很多开发人员都喜欢将adapter内嵌在activity中,一般会使用simpleadapter

由于没有使用实体,所以一般会把一个字典作为构造函数的参数注入到adapter中

希望adapter只有一个编码风格,这样发现了问题也很容易排查。于是要求所有的adapter都继承自baseadapter,从构造函数注入list<自定义实体>这样的数据集合,从而完成listview的填充工作:

public class cinemaadapter extends baseadapter {
	private final arraylist cinemalist;
	private final appbaseactivity context;

	public cinemaadapter(arraylist cinemalist,
			appbaseactivity context) {
		this.cinemalist = cinemalist;
		this.context = context;
	}

	public int getcount() {
		return cinemalist.size();
	}

	public cinemabean getitem(final int position) {
		return cinemalist.get(position);
	}

	public long getitemid(final int position) {

		return position;
	}

对于每个自定义的adapter,都要实现以下4个方法:

getcount()getitem()getitemid()getview()

此外,还要内置一个holder嵌套类,用于存放listview中每一行中的控件。viewholder的存在,可以避免频繁创建同一个列表项,从而极大的节省内存,如下:

class holder {
		textview tvcinemaname;
		textview tvcinemaid;
	}

当有很多列表数据时,快速滑动列表会变的很卡,其实就是因为没有viewholder机制导致的,正确的写法如下:

public view getview(final int position, view convertview,
			final viewgroup parent) {
		final holder holder;
		if (convertview == null) {
			holder = new holder();
			convertview = context.getlayoutinflater().inflate(
					r.layout.item_cinemalist, null);
			holder.tvcinemaname = (textview) convertview
					.findviewbyid(r.id.tvcinemaname);
			holder.tvcinemaid = (textview) convertview
					.findviewbyid(r.id.tvcinemaid);
			convertview.settag(holder);
		} else {
			holder = (holder) convertview.gettag();
		}

		cinemabean cinema = cinemalist.get(position);
		holder.tvcinemaname.settext(cinema.getcinemaname());
		holder.tvcinemaid.settext(cinema.getcinemaid());
		return convertview;
	}

在activity中,在使用adapter的地方,我们按照下面的方式把列表数据传递过去:

@override
	protected void initviews(bundle savedinstancestate) {
		setcontentview(r.layout.activity_listdemo);		

		lvcinemalist = (listview) findviewbyid(r.id.lvcinemalist);
		
		cinemaadapter adapter = new cinemaadapter(
				cinemalist, listdemoactivity.this);
		lvcinemalist.setadapter(adapter);
		lvcinemalist.setonitemclicklistener(
				new adapterview.onitemclicklistener() {
					@override
					public void onitemclick(adapterview parent, view view,
							int position, long id) {
						//do something
					}
				});
	}

1.6 类型安全转换函数

在每天统计线上崩溃的时候,我们发现因为类型转换不正确导致的崩溃占了很大的比例。于是去检查程序中的所有类型转换,发现主要集中在两个地方:object类型的对象,substring函数。

对于一个object类型的对象,我们对其使用字符串操作函数tostring,当其为null时就会崩溃。

int result = integer.valueof(obj.tostring());

一旦obj这个对象为空,那么上面这行代码就会直接崩溃,这里的obj,一般是从json数据中取出来的,对于mobileapi返回的json数据,我们无法保证其永远不为空。

比较好的做法是编写一个类型安全转换函数converttoint,实现如下:

public final static int converttoint(object value, int defaultvalue) {
 	if (value == null || "".equals(value.tostring().trim())) {
 		return defaultvalue;
 	}
 	try {
 		return integer.valueof(value.tostring());
 	} catch (exception e) {
 		try {
 			return double.valueof(value.tostring()).intvalue();
 		} catch (exception e1) {
 			return defaultvalue;
 		}
 	}
 }

将这个方法放到utils类下面,每当要把一个object对象转化成整型时,都使用该方法,就不会崩溃

int result = utils.converttoint(obj, 0);

如果长度不够,执行substring函数就会崩溃。java的substring有两个参数:start和end。

string cityname = "t";
 string firstletter = cityname.substring(1, 2);

对于第一个参数设为0一般没有问题,设置为大于0如上代码就会崩溃,使用substring函数的时候,都要判断start和end两个参数是否越界了,应该这样写:

string cityname = "t";
string firstletter = "";
if(cityname.length() > 1)
{
	cityname.substring(1, 2);
}

对于mobileapi返回的数据

首先,不能让app直接崩溃,应该在解析json数据的外面包一层try...catch...语句,将截获到的异常在catch中进行处理,比如发送错误日志给服务器。

其次,需要分级对待,例如:

对于那些不需要加工就能直接展示的数据,我们不担心,即使为空页面也就是不显示而已,不会引起逻辑的问题。对于那些很重要的数据,比如涉及支付的金额不能为空的逻辑,这时候就应该弹出提示框提示用户当前服务不可用,并停止接下来的操作。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网