Best Practices
Gradle configuration
General structure. Follow Google's guide on Gradle for Android
gradle.properties
KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789
signingConfigs {
release {
try {
storeFile file("myapp.keystore")
storePassword KEYSTORE_PASSWORD
keyAlias "thekey"
keyPassword KEY_PASSWORD
}
catch (ex) {
throw new InvalidUserDataException("You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties.")
}
}
}
Retrolambda
dependencies {
classpath 'me.tatarka:gradle-retrolambda:2.4.+'
}
apply plugin: 'retrolambda'
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
retrolambda {
jdk System.getenv("JAVA8_HOME")
oldJdk System.getenv("JAVA7_HOME")
javaVersion JavaVersion.VERSION_1_7
}
Activities and Fragments
Square even has a library for building architectures mostly with Views,
- Avoid using nested fragments extensively,because matryoshka bugs can occur;
- Avoid putting too much code in activities.
Java packages architecture
com.futurice.project
├─ network
├─ models
├─ managers
├─ utils
├─ fragments
└─ views
├─ adapters
├─ actionbar
├─ widgets
└─ notifications
Resources
Naming:
fragment_contact_details.xml
view_primary_button.xml
activity_main.xml
Organizing layout XMLs.
-
android:id
as the first attribute always -
android:layout_**
attribute at the top -
style
attribute at the bottom - Tag closer
/>
on its own line. - Rather then hard coding
android:text
,using Designtime attributes
Designtime attributes
These are attributes which are used when the layout is rendered in the tool,but have no impact on the runtime.
How to use it
- Add
xmlns:tools="http://schemas.android.com/tools"
to root layout - Use the
tools:
namespace rather than theandroid:
namespace: - In general, you can set any Android framework attribute as a designtime attribute;
Tools Attributes
tools:layout
<fragment android:name="com.example.master.ItemListFragment" tools:layout="@android:layout/list_content" />
tools:listitem / listheader / listfooter
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@android:layout/simple_list_item_2"
/>
tools:showIn Can look the effect in the parent activity_main
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:text="@string/hello_world"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:showIn="@layout/activity_main" />
tools:menu
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:menu="menu1,menu2" />
Use styles
- Note to accumulate common use Style
- Split a large style file into other files
styles_home.xml
styles_item_details.xml
styles_forms.xml
Colors.xml
colors.xml than just a mapping from a color name to an RGBA value.Names such as "brand_primary", "brand_secondary", "brand_negative" are totally acceptable as well. Formatting colors as such will make it easy to change or refactor colors
- Bad
<resources>
<color name="button_foreground">#FFFFFF</color>
<color name="button_background">#2A91BD</color>
<color name="comment_background_inactive">#5F5F5F</color>
<color name="comment_background_active">#939393</color>
- Good
<!-- basic colors -->
<color name="green">#27D34D</color>
<color name="blue">#2A91BD</color>
<color name="orange">#FF9D2F</color>
<color name="red">#FF432F</color>
Treat dimens.xml like colors.xml.
<resources>
<!-- font sizes -->
<dimen name="font_larger">22sp</dimen>
<dimen name="font_large">18sp</dimen>
<dimen name="font_normal">15sp</dimen>
<dimen name="font_small">12sp</dimen>
<!-- typical spacing between two views -->
<dimen name="spacing_huge">40dp</dimen>
<dimen name="spacing_large">24dp</dimen>
<dimen name="spacing_normal">14dp</dimen>
<dimen name="spacing_small">10dp</dimen>
<dimen name="spacing_tiny">4dp</dimen>
<!-- typical sizes of views -->
<dimen name="button_height_tall">60dp</dimen>
<dimen name="button_height_normal">40dp</dimen>
<dimen name="button_height_short">32dp</dimen>
</resources>
Strings.xml
- Name your strings with keys that
resemble namespaces
, and don't be afraid of repeating a value for two or more keys. Languages are complex, so namespaces are necessary to bring context and break ambiguity.
Bad
<string name="network_error">Network error</string>
<string name="call_failed">Call failed</string>
<string name="map_failed">Map loading failed</string>
**Good **
<string name="error.message.network">Network error</string>
<string name="error.message.call">Call failed</string>
<string name="error.message.map">Map loading failed</string>
- Don't write string values in all uppercase.
Bad
<string name="error.message.call">CALL FAILED</string>
Good
<string name="error.message.call">Call failed</string>
Use <merge>
Tag
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="@+id/imgLoader"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerInParent="true"
android:src="@drawable/ico_loader" />
</merge>
Beware of problems related to WebViews.
WebViews can also leak memory
public class TestActivity extends Activity {
private FrameLayout mWebContainer;
private WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
mWebContainer = (FrameLayout) findViewById(R.id.web_container);
mWebView = new WebView(getApplicationContext());
mWebContainer.addView(mWebView);
}
@Override
protected void onDestroy() {
super.onDestroy();
mWebContainer.removeAllViews();
mWebView.destroy();
}
}
Test frameworks
- JUnit tests connectedAndroidTest ,using extension of JUnit with helpers for Android. Follow the official guide [1] [2] for testing.
- Robolectric only for unit tests, not for views.
- Robotium makes writing UI tests easy.
Proguard configuration
ProGuard can cause application crash when thd build command is succeeded :
ClassNotFoundException
, NoSuchFieldException
, similar etc.
**What's you can do **
- See the ProGuard remove
app/build/outputs/proguard/release/usage.txt
- See the ProGuard obfuscated:
app/build/outputs/proguard/release/usage.txt
Common configure
To prevent ProGuard from
- stripping classes or class members,
-keep class com.futurice.project.MyClass { *; }
- obfuscating classes or class members,
-keepnames class com.futurice.project.MyClass { *; }
Tips.
- Save the mapping.txt ==> when app crash you can see the obuscated stack trace.
Data storage
SharedPreferences
There are two reasons why you might not want to use ShareP
- Data is complex, or a lot of
- Multiple processes accessing the data:
ContentProviders
ContentProviders are fast and process safe. to generate the ContentProvider by using a library such as schematic .
Using an ORM
- Not recommend, unless you have unusually complex data and you have a dire need.
- They tend to be complex and require time to learn.
- whether or not it is process safe if your application requires it
网友评论