The first chapter

1.1 Replanning the Android project structure

Replanning the Android project directory structure in two steps:

1. Establish AndroidLab class libraries, shift has nothing to do with the business logic to AndroidLab AndroidLab include at least five most: the package name + acticity, cache,.net, UI, utils.

  • The activity package contains the activity base class that is irrelevant to the business.
  • Net package is stored in the network layer encapsulation.
  • Cacahe package is stored in the cache data picture and picture related processing.
  • The UI package holds custom controls
  • The utils package holds a variety of class-independent utility methods.

2. Divide the main project into different categories and place them in various packages. Activity: We continue by module, dividing activities from different modules into different packages.

  • Adapter: All adapters are placed together
  • Entity: All entities are grouped together
  • Db: Encapsulation of SQLite logic
  • Engine: Puts business-related classes together
  • UI: Put all your custom controls in this package
  • Utils: Put all the public methods here
  • Interfaces: Real interfaces, named beginning with I
  • Listener: A listener-based interface whose name starts with On

1.2 Define a new lifecycle for the Activity

You can split the onCreate method into three child methods

  • InitVariables: Initialize variables, including data in the Intent and variables in the Activity
  • InitViews: loads the layout file, initializes the controller, and hangs the event method for the control
  • LoadData: Call MobileAPI to get data

1.3 Unified event programming model

Once an agreement is reached within a team to use a certain event programming style, all developers code the same way.

1.4 Materialized programming

1.4.1 Using entities in network requests

Some developers, instead of using materialized programming, use JSONObject or JSONArray to host the JSON data returned by a MobileAPI network request, and then treat the returned data as a dictionary, retrieving the value of the response based on the key. This paper introduces fastJSON and GSON methods of materialized programming.

Using fastJSON is as follows

WeatherEntity weatherEntity = JSON.parseObject(content, WeatherEntity.class); WeatherInfo weatherInfo = weatherEntity.getWeatherInfo(); if (weatherInfo ! = null) { tvCity.setText(weatherInfo.getCity()); tvCityId.setText(weatherInfo.getCityid()); }Copy the code

Use GSON as follows:

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()); }Copy the code

1.4.3 Implement entities in page hopping

On a page, there are two sources of data:

1. Call MobileAPI to obtain JSON data

2. Pass from the previous page

A lazy way to pass data between activities is to set a global variable that is set on the source page and received on the target page. Here is the code for the source page MainActivity:

Intent intent = new Intent(MainActivity.this, LoginActivity.class); intent.putExtra(AppConstants.Email, "[email protected]"); CinemaBean cinema = new CinemaBean(); cinema.setCinemaId("1"); Cinema. SetCinemaName (" stellar "); // pass GlobalVariables as GlobalVariables.Cinema = Cinema; startActivity(intent);Copy the code

Here is the code for the target page LoginActivity:

// use GlobalVariables to pass values CinemaBean cinema = GlobalVariables.Cinema; if (cinema ! = null) { cinemaName = cinema.getCinemaName(); } else { cinemaName = ""; }Copy the code


Global variables are not recommended. Once the App is switched to the background, these global variables will be reclaimed when the phone runs out of memory, so that when the App is switched back to the foreground again and continues to use global variables, they will crash because they are empty. If you must use global variables, be sure to serialize them locally. This allows recovery from local files even if the global variable is empty.

We use intEnts to pass data entities between pages.

First, in MainActivity:

Intent intent = new Intent(MainActivity.this, LoginNewActivity.class); intent.putExtra(AppConstants.Email, "[email protected]"); CinemaBean cinema = new CinemaBean(); cinema.setCinemaId("1"); Cinema. SetCinemaName (" stellar "); // Pass intent.putextra (AppConstants.Cinema, Cinema) with a serializable entity attached to the intent; startActivity(intent);Copy the code

Second, the target page LoginActivity should say:

CinemaBean cinema = (CinemaBean)getIntent() .getSerializableExtra(AppConstants.Cinema); if (cinema ! = null) { cinemaName = cinema.getCinemaName(); } else { cinemaName = ""; }Copy the code

The CinemaBean implements Serializable interface and supports serialization:

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; }}Copy the code

1.5 Adapter template

Without the specification of Adapter writing, the following Adapter would be written

  • Many developers prefer to embed Adapter in their activities, using SimpleAdapter
  • Since no entities are used, a dictionary is typically injected into the Adapter as an argument to the constructor

The Adapter is expected to have only one coding style so that problems can be easily detected. We then require that all adapters inherit from BaseAdapter, injecting a List< custom entity > data set from the constructor to complete the 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;
}
Copy the code

For each custom Adapter, the following four methods are implemented:

  • getCount()

  • getItem()

  • getItemId()

  • getView()

In addition, there is a built-in Holder nested class that holds the controls in each row of the ListView. The existence of a ViewHolder can greatly save memory by avoiding the need to create the same list item frequently:

class Holder {

TextView tvCinemaName;

TextView tvCinemaId;

}
Copy the code

When there is a lot of list data, the quick slide list becomes very slow. This is because there is no 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;

}
Copy the code

In the Activity, where Adapter is used, we pass the list data as follows:

@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

}

});

}
Copy the code

1.6 Type-safe conversion functions

On a daily basis, we see a high percentage of crashes due to incorrect casts. So I went to check all the conversions in the program and found that they were mainly concentrated in two places: objects of type Object and substring function.

  • For an Object of type Object, we use the string manipulation function toString on it, which crashes when null.


int result = Integer.valueOf(obj.toString());
Copy the code

This line of code will crash if obj is null. Obj is usually retrieved from JSON data, and there is no guarantee that JSON data returned by MobileAPI will never be null.

It is better to write a type-safe conversion function, convertToInt, as follows:

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; }}}Copy the code

Put this method under the Utils class and use it whenever you want to convert an Object to an integer without crashing

int result = Utils.convertToInt(obj, 0);
Copy the code

If the length is insufficient, the substring function will crash. Java substring takes two arguments: start and end.

String cityName = "T";

String firstLetter = cityName.substring(1, 2);
Copy the code

If the first parameter is set to 0, the code will crash if the first parameter is set to greater than 0. If the first parameter is set to 0, the code will crash if the first parameter is set to greater than 0.

String cityName = "T";

String firstLetter = "";

if(cityName.length() > 1)

{

cityName.substring(1, 2);

}
Copy the code

For data returned by the MobileAPI

  • First, don’t let your App crash directly. Instead, try… catch… Statement that captures exceptions for processing in a catch, such as sending an error log to the server.
  • Secondly, hierarchical treatment is needed, for example:
  • We don’t worry about data that doesn’t need to be processed and can be displayed directly, even if it’s blank pages that don’t show, and it doesn’t cause logic problems.
  • For important data, such as the logic that the amount of money paid cannot be empty, a prompt should pop up telling the user that the service is not currently available and stop the next operation.