Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 重構之Android

重構之Android

編輯:關於Android編程

一:重構 1、重新規劃Android項目結構 第一步:建立AndroidLib類庫,將與業務無關的邏輯轉移到AndroidLib。應至少包括五大部分:
activity基類:public abstract class BaseActivity extends Activity-------AppBaseActivity----具體的一個Activity
public abstract class AppBaseActivity extends BaseActivity {

}
net:網絡底層封裝 cache:緩存數據和圖片的相關處理 ui:存放自定義控件 utils:存放的是各種與業務無關的公共方法 第二步:將主項目中的類分門別類地進行劃分 activity adapter:放適配器 entity:將所有的實體放在一起 db:SQLLite相關邏輯的封裝 engine:存放業務相關的類 ui:存放自定義的控件 utils:存放所有的共用方法 interfaces:真正意義上的接口,一I命名 listener:基於Listener的接口,命名以On作為開頭   2、為Activity定義新的生命周期 設計模式中有一條原則是:單一責任原則。單一責任的定義是:一個類或方法,只做一件事情。 用這條原則來觀察Activity中的onCreate方法,通常要干好多事:通過繼承實現接口的方式,重寫onCreate方法---
public abstract class BaseActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

initVariables();
initViews(savedInstanceState);
loadData();
}

//定義為抽象的方法,用於子類繼承用的
protected abstract void initVariables();
protected abstract void initViews(Bundle savedInstanceState);
protected abstract void loadData();
}

 

引用:
public class LoginNewActivity extends AppBaseActivity implements View.OnClickListener {
private int loginTimes;
private String strEmail;

private EditText etPassword;
private EditText etEmail;
private Button btnLogin;

@Override
protected void initVariables() {
loginTimes = -1;

Bundle bundle = getIntent().getExtras();
strEmail = bundle.getString(AppConstants.Email);
}

@Override
protected void initViews(Bundle savedInstanceState) {
setContentView(R.layout.activity_login);

etEmail = (EditText)findViewById(R.id.email);
etEmail.setText(strEmail);
etPassword = (EditText)findViewById(R.id.password);

//登錄事件
btnLogin = (Button)findViewById(R.id.sign_in_button);
btnLogin.setOnClickListener(this);
}

@Override
protected void loadData() {
//獲取2個MobileAPI,獲取天氣數據,獲取城市數據
loadWeatherData();
loadCityData();
}

@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.sign_in_button:
gotoLoginActivity();
}
}

private void gotoLoginActivity() {
Intent intent = new Intent(LoginNewActivity.this,
PersonCenterActivity.class);
startActivity(intent);
}

private void loadWeatherData() {
//發起網絡請求,代碼從略
}

private void loadCityData() {
//發起網絡請求,代碼從略
}
}

 

對Activity生命周期重新定義是借鑒了JavaScript的做法。JavaScript因為是腳本語言,所以必須要細化每個方法,才能保證結構清晰,不 至於寫錯變量和語法。   3、統一事件編程模型 常見做法是實現事件接口,重寫相應的事件方法如onClick,再在switch......case中R.id......篩選實現。根據面向對象編程的思想,就是initViews方法中實例化控件後,不希望再出現R.id...。就是在初始化控件的時候,就給控件添加相應的事件。
// 登錄事件
Button btnLogin = (Button) findViewById(
R.id.sign_in_button);
btnLogin.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
gotoLoginActivity();
}
});
有兩個優點: 1>直接在控件對象上增加點擊事件,是面向對象的寫法。 2>件onClick方面的實現,封裝成一個方法,減少代碼的臃腫度。   4、實體化編程 1>在網絡請求中使用實體 JSONObect和JSONArray都是不支持序列化的,在值傳遞的時候只好將這種對象封裝到一個全局變量中,在跳轉前設置,在跳轉後取出, 這並不是明智之舉。
// 第一種寫法,基於JSONObject
try {
JSONObject jsonResponse = new JSONObject(result);
JSONObject weatherinfo = jsonResponse
.getJSONObject("weatherinfo");
String city = weatherinfo.getString("city");
int cityId = weatherinfo.getInt("cityid");

tvCity.setText(city);
tvCityId.setText(String.valueOf(cityId));
} catch (JSONException e) {
e.printStackTrace();
}

 

如果通過傳統的字典鍵值取值法會存在問題: @1:根據key值取value,這是一個字典鍵值對,字典比實體更晦澀難懂,容易產生bug。 @2:每次都要手動從JSONObject或者JSONArray中取值,很繁瑣。 ====通過fastJSON和GSON實例化實體對象 首先得導入相應的.jar包。
public class WeatherInfo {
private String city;
private String cityid;
private String temp;
private String WD;
private String WS;
private String SD;
private String WSE;
private String time;
private String isRadar;
private String Radar;
private String njd;
private String qy;
........................................}
public class WeatherEntity {
private WeatherInfo weatherinfo;

public WeatherInfo getWeatherInfo() {
return weatherinfo;
}

public void setWeatherInfo(WeatherInfo weatherinfo) {
this.weatherinfo = weatherinfo;
}
}
fastJSON映射方式:
// 第2種寫法,基於fastJSON
WeatherEntity weatherEntity = JSON.parseObject(content,
WeatherEntity.class);
WeatherInfo weatherInfo = weatherEntity.getWeatherInfo();
if (weatherInfo != null) {
tvCity.setText(weatherInfo.getCity());
tvCityId.setText(weatherInfo.getCityid());
}
// 第3種寫法,基於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());
}

 

====特殊注意 這裡說一件非常狗血的事情,就是在我們使用fastJSON後,App四處起火,主要表現為: 1) 加了符號Annotation的實體屬性,已使用就崩潰。 2)當有泛型屬性時,一使用就崩潰。 在調試的時候沒事,可是每次打簽名混淆包,就會出現上述問題。解決這個問題需要在混淆文件中添加兩行代碼: -keepattributes Signature //避免混淆泛型 -keepattributes *Annotation* //不混淆注解 2>實體生成器 Json Class Generator。可以生成Android和IOS以及WindowsPhone的實體。 工具地址如:http://www.xamasoft.com/json-class-generator/ 項目地址: http://files.cnblogs.com/Jax/EntityGenerator.zip。 說明:工具代碼為C# .NET代碼。 3>在頁面跳轉中使用實體 Activity之間的數據應該如何傳遞。 一種偷懶的方法是,設置一個全局變量,在來源頁設置全局變量,在目標頁接收全局變量。
CinemaBean cinema = new CinemaBean();
cinema.setCinemaId("1");
cinema.setCinemaName("星美");

//使用全局變量的方式傳遞參數
GlobalVariables.Cinema = cinema;

 

--接收全局變量的值:
// 使用全局變量的方式傳值
CinemaBean cinema = GlobalVariables.Cinema;
if (cinema != null) {
cinemaName = cinema.getCinemaName();
} else {
cinemaName = "";
}
這裡的GlobalVariables類是一個全局變量,定義如下:
public classGlobalVariables{
public static CinemaBean Cinema;
}

 

注意:不建議使用全局變量。 App一旦切換到後台,當手機內存不足的時候,就會回收這些全局變量,從而當App再次切換回前台時,再繼續使用全局變量,就會因為它 們為空而崩潰。 而必須使用全局變量,就一定要把它們序列化到本地。這樣即使全局變量為空,也能從本地文件中恢復。 ==著重研究使用Intent在頁面間來傳遞數據實體的機制:
public class AppConstants {
public final static String Email = "Email";
public final static String Cinema = "Cinema";
}
//傳遞對象來源
public void onClick(View v) {
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);
}
//接收數據
Bundle bundle = getIntent().getExtras();
strEmail = bundle.getString(AppConstants.Email);

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;}
5>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;
}

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;
}

class Holder {
TextView tvCinemaName;
TextView tvCinemaId;
}
}

 

----對於每個自定義的Adapter,都要實現以下4個方法: getCount()、getItem()、getItemId()、getView() ----此外,還要內置一個Holder嵌套類,用於存放ListView中每一行中的控件。ViewHolder的存在,可以避免頻繁創建用一個列表項,從 而極大地節省內存。 ====那麼,在Activity中,在使用Adapter的地方,按照下面的方式把列表數據傳遞過去:
public class ListDemoActivity extends AppBaseActivity {
ListView lvCinemaList;
ArrayList cinemaList;

@Override
protected void initVariables() {
cinemaList = new ArrayList();
CinemaBean cinema1 = new CinemaBean();
cinema1.setCinemaId("1");
cinema1.setCinemaName("星美");
CinemaBean cinema2 = new CinemaBean();
cinema2.setCinemaId("2");
cinema2.setCinemaName("萬達");

cinemaList.add(cinema1);
cinemaList.add(cinema2);
}

@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(AdapterViewparent, View view,
int position, long id) {
//do something
}
});
}

@Override
protected void loadData() {

}}

 

6、類型安全轉換函數 統計線上崩潰問題時,發現因為類型轉換不正確導致的崩潰占了很大的比例。主要集中在兩個地方:Object類型的對象、substring函數。 1)對於一個Object類型的對象,直接使用字符串操作函數toString,當其為null時就會崩潰。 解決方法:
public class Utils {
/**
*
* @Title: convertToInt
* @Description: 對象轉化為整數數字類型
* @param value
* @param defaultValue
* @return integer
* @throws
*/
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;
}
}
}
}

 

再通過此種方式引用,就不會崩潰了: int result = Utils.converToInt(obj , 0);   2)如果長度不夠,那麼執行substring函數的時候,就會崩潰: Java的substring函數有2個參數:start和end。 --解決方法:
String cityName = "T";
String firstLetter = "";
if(cityName.length() > 1) {
firstLetter = cityName.substring(1, 2);
}

 

====總結: 以上兩類問題的根源,都來自MobileAPI返回的數據,由此而引出另一個很嚴肅的問題,對於從MobileAPI返回的數據,對待數據要分級別 對待: 1)對於那些不需要加工就能直接展示的數據,即使為空,只要在頁面不顯示就行,不會影響到邏輯。 2)對於那些很重要的數據,比如涉及到支付的金額不能為空時的邏輯,這是需要彈出提示框提示用戶當前服務不可用,並停止接下來的工 作。  
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved