重新规划Android项目的目录结构,分两步走:
建立AndroidLab类库,将与业务无关的逻辑转移到AndroidLab,AndroidLab至少包括五大部分:包名+ acticity,cache,net,ui,utils 。
将主项目的类分门别类的进行划分,放置在各种包中。
可以把onCreate方法拆成三个子方法
只要在一个团队内部达成了协议,决定使用某种事件编程方式,所有开发人员就要按照同样的方式编写代码。
一些开发人员不使用实体化编程,在获取MobileAPI网络请求返回的JSON数据时,使用JSONObject或者JSONArray来承载数据,然后把返回的数据当作一个字典,根据键取出响应的值。介绍fastJSON和GSON这种实体化编程的方式.
WeatherEntity weatherEntity = JSON.parseObject(content,
WeatherEntity.class);
WeatherInfo weatherInfo = weatherEntity.getWeatherInfo();
if (weatherInfo != null) {
tvCity.setText(weatherInfo.getCity());
tvCityId.setText(weatherInfo.getCityid());
}
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. 调用MobileAPI获取JSON数据
2. 从上一个页面传递过来
Activity之间的数据传递一个偷懒的办法是设置一个全局变量,在来源页设置全局变量,在目标页接收全局变量。以下是来源页MainActivity的代码:
Intent intent = new Intent(MainActivity.this,
LoginActivity.class);
intent.putExtra(AppConstants.Email, "[email protected]");
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, "[email protected]");
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;
}
}
不对Adapter的写法进行规范,就会写出以下的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个方法:
此外,还要内置一个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
}
});
}
在每天统计线上崩溃的时候,我们发现因为类型转换不正确导致的崩溃占了很大的比例。于是去检查程序中的所有类型转换,发现主要集中在两个地方: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返回的数据