Code standard trong Android

  1. Project guidelines.

    1.1. Cấu trúc project

Project mới có cấu trúc dựa theo Android Gradle project được định nghĩa ở  Android Gradle plugin user guide. Project ribot Boilerplate là 1 ví dụ khá tốt, các bạn có thể clone về và xem cách tổ chức source của project này.

1.2 Đặt tên file

        1.2.1 Tên class.

Ngoài các chuẩn khi đặt tên class trong ngôn ngữ java như:

  • Viết hoa chữ cái đầu.
  •  Viết  hoa các chữ bắt đầu 1 từ
  •  Tên phải có nghĩa với các method function của class.

ví dụ như: NetWorkUtils.java thì với các class extends từ các Android component, tên nên kết thúc là tên của component đó, ví dụ như:

LogInActivity, SignInFragment, ImageUploaderService, ChangePasswordDialog

1.2.2. Tên cho resources.

Tên file resources nên viết dưới dạng: lowercase_underscore.

1.2.2.1.Tên cho các Drawable files:

Tên cho các drawable file

8

file ảnh có đuôi .9.png là ảnh 9-patch khi dùng trong trên các màn hình có kích thước và độ phân giải khác nhau, ảnh sẽ tự resize, không bị vỡ giao diện, Search google để tìm hiểu thêm nhé 😀

Đặt tên cho các icon resources.

9.png

Tên  các files selector states:

10.png

1.2.2.2 Layout files.

Tên file layout nên match với tên Android components, tên component sẽ là tiền tố file layout ví dụ: Với activity SignInActivity thì file layout nên tên là activity_sign_in.xml.

11.png

Nếu tạo layout cho các item của Adapter listView hay RecycleVIew,ta nên đặt tiền tố cho file layout là _item.

Lưu ý có 1 số trường hợp các cách đặt tên trên không hoàn toàn được áp dụng. Ví dụ như khi tạo 1 layout file mà nó là 1 phần của file layout khác thì nên đặt tên file đó với tiền tố là partial_

1.2.2.3 Menu files.

Tương tự như các layout files, các menu files nên đặt tên match với component. Ví dụ,

Nếu chúng ta định nghĩa 1 menu file được sử dụng trong UserActivity, thì tên của nó nên là

activity_user.xml

Chúng ta không nên thêm từ menu vào tên file vì những file này đã được đặt trong thư mục menu trong cấu trúc ứng dụng rồi.

1.2.2.3 Value files

Resource files trong thư mục values nên ở dạng số nhiều tiếng anh: ex: strings.xml, styles.xml, colors.xml, dimens.xml, attrs.xml.

2.Code guidlines.

2.1  Code standard trong Java

2.1.1 không nên bỏ qua exceptions

Chúng ta không nên làm như sau:


void setServerPort(String value) {
 try {
 serverPort = Integer.parseInt(value);
 } catch (NumberFormatException e) { }
}

Chúng ta có thể nghĩ rằng đoạn code trên sẽ không bao giờ gây lỗi, hoặc không quan trọng để xứ lý nó, bỏ qua các exceptions loại khác như đoạn kết trên có thể tiềm ẩn khả năng gây lỗi và khó hiểu cho ng khác . Chúng ta nên xử lý tất cả các ngoại lệ theo các quy tác chuẩn tùy theo trường hợp của b.

Chúng ta có viết lại các đoạn code trên như sau:

    • ém ra 1 ngoại lệ tới nơi gọi method này:
      void setServerPort(String value) throws NumberFormatException {
       serverPort = Integer.parseInt(value);
      }
      

     

  • Ném ra 1 ngoại lệ tự định nghĩa

void setServerPort(String value) throws ConfigurationException {
 try {
     serverPort = Integer.parseInt(value);
 } catch (NumberFormatException e) {
         throw new ConfigurationException("Port " + value + " is not valid.");
 }
}

  • 1 cách xử lý khá tốt, ngay trong khối catch

/** Set port. If value is not a valid number, 80 is substituted. */

void setServerPort(String value) {
 try {
 serverPort = Integer.parseInt(value);
 } catch (NumberFormatException e) {
 serverPort = 80; // default port for server
 }
}

 

 

  • Bắt ngoại lệ và ném ra 1 RuntimeExeption. Khá nguy hiểm, nó chỉ nên dùng khi bạn chắc chắn rằng lỗi này  xẩy ra sẽ gây crash ứng dụng của bạn.

/** Set port. If value is not a valid number, die. */

void setServerPort(String value) {
 try {
 serverPort = Integer.parseInt(value);
 } catch (NumberFormatException e) {
 throw new RuntimeException("port " + value " is invalid, ", e);
 }
}

Lưu ý: với cách xử lý này compile với java 1.3 trở lên.

 

  • Nếu bạn chắc chắn rằng không xử lý exceptions này không ảnh hưởng tới ứng dụng, thì bạn cũng nên comment vào khối catch, để dễ dàng maintain sau này:
    /** If value is not a valid number, original port number is used. */
    void setServerPort(String value) {
     try {
     serverPort = Integer.parseInt(value);
     } catch (NumberFormatException e) {
     // Method is documented to just ignore invalid user input.
     // serverPort will just be unchanged.
     }
    }
    
    

    2.1.2 Không bắt lỗi 1 cách chung chung

Chúng ta không nên làm thế này:

try {
 someComplicatedIOFunction(); // may throw IOException
 someComplicatedParsingFunction(); // may throw ParsingException
 someComplicatedSecurityFunction(); // may throw SecurityException
 // phew, made it all the way
} catch (Exception e) { // I'll just catch all exceptions
 handleError(); // with one generic handler!
}

Chúng ta rất hay dính phốt này vì lười, nhưng nếu xẩy ra bug rất khó để chúng ta tìm

ra nguyên nhân để fix nhanh chóng.
Hãy bắt lỗi càng chi tiết càng tốt 🙂

2.1.3 Không sử dụng finalizers

Finalize là phương thức sẽ được gọi ngay trước khi hủy 1 đối tượng bởi Garbage Collector. Nó được sử dụng để chắc chắn rằng 1 đối tượng đã hoàn toàn kết thúc.

Ví dụ: sử dụng finalize() để đảm bảo rằng 1 file được mở mà sở hửu bởi 1 đối tượng nào đó đã được đóng.

Trong Android, chúng ta không nên sử dụng finalizers. Không có gì đảm bảo khi 1 finalizer sẽ được gọi, hay thậm chí nó sẽ được gọi mọi lúc. Trong hầu hết các trường hợp, bạn có thể làm những gì bạn cần từ finalizers với 1 xử lý ngoại lệ tốt. Nếu bạn vẫn cần nó, hãy đinh nghĩa phương thức close() và xác định chính xác khi nào phương thức này được gọi.

2.1.4 Vấn đề Import

  • không nên: import foo.*;
  • nên: import foo.Bar;

Chỉ import những class cần thiết, để hạn chế kích thước file class.

2.2 Styles code Java

2.2.1 Định nghĩa các biến và đặt tên

Các biến (trường) nên đặt ở đầu trong file, và nên được đặt tên theo 1 sốquy tắc sau đây:

  • các trường private, non-static nên được đăt tên bắt đầu bởi tiền tốm.
  • các trường private, static nên được đặt tên bắt đầu bởi tiền tố s.
  • các trường khác nên bắt đầu bởi chữ cái thường, và tên nên có ý nghĩa sử dụng
  • các trường static constants nên đặt tên gồm các chữ cái hoa và dấu gạch dưới

Ví dụ:


public class MyClass{

        private static final int SOME_CONSTANT = 42;

        public int numCout;

       private static MyClass sSingleton;

       private int mPrivate;

       protected int mProtected;
}

2.2.3 Một số quy tắc khác:
12.png

2.2.4 Sử dụng dấu cách cho thụt đầu dòng

Sử dụng “4 dấu cách liên tiếp” để thụt đầu dòng trong 1 khối code:

13.png

Sử dụng “8 dấu cách liên tiếp” để thụt đầu dòng khi 1 dòng lệnh quá dài.

14.png

2.2.5 Quy tắc code chuẩn trong các câu lệnh điều kiện if:

15.png

Nếu điều kiện và khối lệnh ở trên 1 dòng, nên không cần phải bọc trong cặp ký tự {}

Và nên:

16.png

Thay vì:
17.png

2.2.6 Annotations trong Java(Android)

2.2.6.1 Thực hành annotations

Theo như Android code style guidle, các annotations đã được định nghĩa sẵn gồm

  @override: được sử dụng khi 1 phương thức overrides 1 phương thức từ class cha.

@SuppressWarnings: nên được sử dụng trong những  tình huống không thể loại bỏ  các cảnh báo. Nếu các cảnh báo là không thể loại bỏ, annotations này phải được sử dụng để đảm bảo rằng tất cả các các cảnh báo phản ánh đúng vấn đề thực sự trong code.

2.2.6.2 quy tắc của annotations

Class, method và Constructors

Khi áp dụng annotations trong class, method hay constructor, chúng được liệt kê vào đoạn chú thích mô tả và mỗi annotations trên 1 dòng:

18.png

Biến, trường

Annotations áp dụng vào các biến (trường) nên được đặt trên 1 dòng, trừ khi nó vượt quá giới hạn 1 kí tự trên 1 dòng code.

19.png

2.2.7 Phạm vi của biến

Phạm vi của các biến cục bộ nên giữ nhỏ nhất có thể. Điều này làm code của bạn đễ đọc và dễ bảo trì hơn, giảm thiểu khả năng gây lỗi. Biến nên được khai báo trong đoạn mã  code mà biến này được sử dụng.

Các biến cục bộ nên được khai báo lúc nó được sử dụng. Hầu hết mỗi biến được khai báo đều cần khởi tạo. Do đó, nếu chưa đủ các thông tin để khởi tạo biến, tốt nhất là chưa nên khái báo biến cho tới khi thực sự cần.

2.2.8 Vấn đề về import trong Android:

Nếu sử dụng IDE Android studio, chúng ta không cần lo lắng điều này vì AS tự động import và tuân theo các quy tắc viết code đã nói ở trên.

Android studio cung cấp các Import gồm:

  • Android imports
  • Import từ lib bên thứ 3 (com, junit, net, org)
  • java và javax
  • Import project mẫu

để phù hợp với setting của As, các import nên:
– theo thứ tự bảng chứ cái trong mỗi nhóm, với chữ viết hoa trước chữ thường.

– nên có 1 dòng trống (cách dòng) trước mỗi nhóm chính (android, com, junit, net, org, java, javax)

2.2.9 hướng dẫn hiển thị Log

Sử dụng phương thức loggin được cung cấp bởi class Log để in ra các thông báo lỗi hoặc các  thông tin hữu ích cho việc phát hiện vấn đề khi coding.

20.png

Ta nên sử dụng tên class như là tag và ta định nghĩa 1 biến static final ở đầu file. ví dụ:

21.png

Verbosedebug logs phải loại bỏ khi ta sẵn sàng tạo bản build phát hành. Ta cũng nên loại bỏ cả các log khác nhưng cũng có thể cho phép nếu nó hữu ích trong việc xác định các vấn đề gây lỗi trong bản phát hành. Nếu bạn để các loại log này được hiển thị, bạn phải chắc chắn rằng chúng không rò rỉ các thông tin nhạy cảm của người dùng như email, địa chỉ….

Chỉ nên show log ở chế độ debug:

22.png

2.2.10 Tổ chức Class.

Không có 1 solution chính thức nào, nhưng tổ chức cách thành phần trong class thống nhất và ít thay đổi rất tốt cho khả năng maintain code sau này. Sau đây là cấu trúc được khuyến nghị.

  1. Các giá trị constants (hằng số).
  2. Các biến (trường)
  3. Các phương thức khởi dựng (constructors)
  4. Các phương thức override và callback (public hoặc private).
  5. Các phương thức public.
  6. Các phương thức private.
  7. Các class nội và Interfaces.

Ví dụ:


public class MainActivity extends Activity {
private static final int CONSTANT_VAL = 12;
private String mTitle;
private TextView mTvTitle;
public String status;

@Override
public void onCreate() {
...
}
public void setTitle(String title) {
mTitle = title;
}

private void setUpView() {
...
}

static class AnInnerClass {

}

}

Nếu class của chúng ta extends từ một Android component như Activity hay Fragment, 

Chúng ta nên override các phương thức phù hợp với vòng đời của component đó. Ví dụ, chúng ta có 1 Activity, ta implements onCreate(), onResume(), onPause(), onDestroy(), đoạn code chi tiết như sau:


public class MainActivity extends Activity {

//Order matches Activity lifecycle
@Override
public void onCreate() {}

@Override
public void onResume() {}

@Override
public void onPause() {}

@Override
public void onDestroy() {}

}

2.2.11 Truyền tham số trong method

Trong lập trình Android, thường rất hay có các phương thức truyền vào biến Context. Nếu bạn có các phương thức kiểu này, biến Context nên là biến đầu tiên.

Ngược lại các interfaces callback nên là tham số cuối cùng.

24.png

2.2.12. Chuỗi không đổi, tên và giá trị.

Nhiều yếu tố cỉa Android SDK như SharePreferences, Bundle, hay Intent sử dụng 1 cặp key-value, các yếu tố này có trong hầu hết các loại ứng dụng, và chúng ta phải viết rất nhiều các chuỗi không đổi.

Khi sử dụng các yếu tố này, ta nên định nghĩa giá trị key như 1 static final và nên đặt các tiền tố như bảng sau:

25.png

Lưu ý rằng: tham số của Fragment – Fragment.getArguments() cũng là 1 bundle, nhưng do nó rất hay được sử dụng nên ta định nghĩa 1 tiền tố khác cho nó.

Ví dụ:


// Note the value of the field is the same as the name to avoid duplication issues
static final String PREF_EMAIL = "PREF_EMAIL";
static final String BUNDLE_AGE = "BUNDLE_AGE";
static final String ARGUMENT_USER_ID = "ARGUMENT_USER_ID";

// Intent-related items use full package name as value
static final String EXTRA_SURNAME = "com.myapp.extras.EXTRA_SURNAME";
static final String ACTION_OPEN_USER = "com.myapp.action.ACTION_OPEN_USER";

2.2.13 Tham số trong Fragment và Activities.

Khi passed Data tới Activity hay Fragment thông qua Intent hay Bundle, key và values nên tuân theo các quy tắc đã định nghĩa trên.

Khi 1 Activiy hay Fragment mong đợi các tham số, nó nên cung cấp 1 phương thức public static, để tạo các intent hay fragment thích hợp.

Ví dụ: với trường hợp là Activity, phương thức này nên gọi là getStartIntent():


public static Intent getStartIntent(Context context, User user) {
 Intent intent = new Intent(context, ThisActivity.class);
 intent.putParcelableExtra(EXTRA_USER, user);
 return intent;
}

Với Fragment tên nên là newInstance(), phương thức xử lý việc khởi tạo Fragment với các tham số chính xác.


public static UserFragment newInstance(User user) {
 UserFragment fragment = new UserFragment();
 Bundle args = new Bundle();
 args.putParcelable(ARGUMENT_USER, user);
 fragment.setArguments(args)
 return fragment;
}

Lưu ý 1: những phương thức này nên đặt ở trước onCreate().

Lưu ý 2: Nếu ta định nghĩa các method trên, key của  tham số nên để là private để hạn chế truy cập từ bên ngoài class.

2.2.14 Giới hạn độ dài dòng code.

1 dòng code không nên quá 100 kí tự. Nếu vượt quá, có 2 cách để giảm độ dài như sau:

– rút gọn tên biến hay phương thức (hay dùng)

–  Tách 1 dòng viết trên nhiều dòng

Có 1 số ngoại lệ có thể vượt quá 100 kí tự như:

  •   1 đường link nào đấy, không thể tách được
  • tên package hay import…

2.2.14.1

Không có 1 cách thức nào để xuống tách 1 dòng code quá dài. thường khá có nhiều cách. Tuy nhiên có 1 vài quy tắc chung được áp dụng ở hầu hết các trường hợp.

Tách dòng trước toán tử.


int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
 + theFinalOne;

Ngoại lệ của toán tử gán:
1 ngoại lê của quy tắc xuống dòng ở trên là khi dùng với toán tử gán “=”, Dòng code nên được tách sau toán tử này.


int longName =
 anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;

Trường hợp gọi chuỗi các phương thức.

Khi gọi 1 chuỗi các phương thức liên tiếp trên 1 dòng, chẳng hạn như khi sửa dụng Builders, mỗi lần gọi, 1 phương thức nên ở trên 1 dòng và được tách bởi dấu “.

26.png

Trường hợp tham số quá dài.

Khi gọi 1 phương thức có nhiều tham số hoặc tham số quá dài, ta nên tách dòng sau mỗi dấu “,“.

27.png

2.2.16 Quy tắc của chuỗi RxJava

Chuỗi Rx của toán tử yêu cầu tách dòng. Mỗi toán tử phải bắt đầu trên 1 hàng và hàng nên được tách dòng bởi dấu “.

28

2.3 Các quy tắc viết XML

2.3.1 Nên dùng dấu tự  đóng tag.

Khi sử dụng 1 phần tử XML mà không có nội dung gì, nên dùng dấu tự đóng tag.

Nên:

29.png

Không nên:

30.png

2.3.2 Tên các resources

Resouce Id và tên phải được viết gồm các chữ thường và dấu gạch dưới.

31.png

ví dụ: menu

32.png

2.3.2.2 Chuỗi (strings)

tên chuỗi nên được đặt với tiền tố xác định thành phần mà chuỗi này thuộc về.Ví dụ:

registration_email_hint hay registration_name_hint . Nếu các chuỗi không thuộc bất cứ thành phần nào thì nên theo 1 số quy tắc dưới đây:
33.png

2.3.2.3 Styles và Themes

Ngoại trừ cá thành phần của resouces, tên của style nên được đặt giống như tên class trong Java (ví dụ tên của 1 styles: UpperCamelCase.)

2.3.3 Thứ tự các thuộc tính trong xml

Như 1 quy tắc chung, chúng ta nên đặt các thuộc tính cùng nhau trong 1 file XML, các thuộc tính này nên được sắp xếp theo thứ tự sau:

  1. View Id
  2. Style
  3. layout with và layout height
  4. Các thuộc tính layout khác(sắp xếp theo abc)
  5. Các thuộc tính còn lại (sắp xếp theo abc)

2.4 Quy tắc viết Test trong Android

2.4.1 Unit tests

Class test nên khớp với tên của class cần test và kết thúc bởi hâu tố Test. Ví dụ, để tạo class để test cho class DatabaseHelper, ta nên đặt tên cho class này là DatabaseHelperTest.

Phương thức test nên đặt annotations là @Test và nên bắt đầu với tên của phương thức cần test, theo sau bởi 1 điều kiện tiên quyết hay 1 kết quả mong đợi.

  • Mẫu: @Test void methodNamePreconditionExpectedBehavior();
  • Ví dụ: @Test void signInWithEmptyEmailFails();

Điều kiện tiên quết hay kết quả mong đợi có thể không cần xuất hiện trong đặt tên nếu code trong phương thức test rõ ràng, dễ hiểu.

Trong một số trường hợp, class có thể chứa rất nhiều các phương thức, trong cùng 1 thời gian đòi hỏi nhiều test case cho mỗi phương thức. Trường hợp này, ta nên tách class test này thành nhiều class test liên quan. Ví dụ, class DataManager có nhiều phương thức, chúng ta có thể phân tách thành các class để test như DataManagerSignInTest, DataManagerLoadUserTest….

2.4.2 Espresso Test

Mỗi class Espresso test thường sửa dụng cho một Activity nào đấy, do đó tên của nó nên khớp với Activty, kết thúc bởi hậu tố Test, ví dụ như: SignInActivityTest

Khi sửa dụng Espresso API, thông thường ta sẽ gọi một chuỗi các phương thức, các phương thức trên 1 dòng và được tách dòng bởi dấu “.“.


onView(withId(R.id.view))
.perform(scrollTo())
.check(matches(isDisplayed()))

Advertisements

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Đăng xuất / Thay đổi )

Connecting to %s