Những kinh nghiệm với lập trình Android - Phần I

Tóm tắt

[Có một vài topic như thế này][1] khiến mình rất bối rối không biết phải trả lời như thế nào cho hợp lí. Vậy nên lập thêm một topic mới chia sẻ kinh nghiệm chung nhất của mình về lập trình Android. Đây là những kinh nghiệm khi mình thực hiện maintain lại một dự án trong hai năm và làm một vài dự án nhỏ nữa nên không chắc là những kinh nghiệm tốt nhất. Tuy nhiên vẫn hi vọng chúng có ích
[1]: Cần tài liệu phần giao diện Android

Cấu trúc thư mục cho code Android

Mục này mình có tham khảo ở nhiều nguồn để ghép vào phần dự án mình đang làm. Theo mình thấy thì dự án chia cây thư mục như sau sẽ rõ ràng hơn các cách chia khác.

Android_structure
├─ gdg.lbs
│  ├─ activities
│  ├─ adapters
│  ├─ fragments
│  ├─ example
│  ├─ interfaces
│  ├─ models
│  ├─ navigates
│  ├─ networks
│  ├─ notifications
│  ├─ utils
│  └─ views

Trong đó mỗi một thành phần sẽ có một ý nghĩa riêng như sau:

gdg: Tên công ty (đồng thời nằm trong root package name)
lbs: Viết tắt của Lương Sơn Bạc là tên của team
activities: Các activity sẽ được đưa vào đây.
adapters: Dành cho các custom adapter.
fragments: Toàn bộ các fragment
example: Tên của project. Trong này sẽ chứa tất cả những gì liên quan tới project bao gồm file Config.java, Application.java…
interfaces: Khai báo các interface được dùng trong dự án
models: Làm việc với preference, làm việc với SQLlite
navigates: Các phương thức điều khiển vào ra của fragment sẽ được đưa vào đây.
networks: Picaso. Volley, OKHttp sẽ được đưa vào đây.
notifications: Tất cả mọi hoạt động liên quan đến notification, GCM.
utils: Các lớp hỗ trợ trọng quá trình sử dụng như StorageUtil.java, ImageUtil.java…
views: Khai báo các custom view

Ngoài những thư mục (package) kể trên thì mình còn sử dụng thêm một vài package nữa tuy nhiên không phổ biến lắm nên mình không có liệt kê tại đây.

Sử dụng DebugLog.java thay vì Log.java

Thông thường khi muốn viêt lại log để hỗ trợ việc debug android được tốt hơn, các bạn sẽ sử dụng lớp được cũng cấp sẵn là Log.i(String, String);. Tuy nhiên vấn đề bạn đang gặp phải là những đoạn log này chỉ hữu ích với việc debug mà thôi. Khi sản phẩm được tung ra thị trường thì những đoạn log này vô tình lại đang làm hại chính các bạn. Vậy trong trường hợp này ta phải làm sao? Chẳng lẽ lại tìm từng chỗ viết log rồi xóa tay trước khi build?

Một gợi ý nhỏ đó là chúng ta sẽ sử dụng một biến static có tên là IS_DEBUG như sau:

if (Config.IS_DEBUG) {
    Log.i(String, String);
}

Cũng khá ổn rồi nhưng mà bây giờ mỗi lần sử dụng chẳng lẽ lại viết đi viết lại mất công quá. Chưa kể là thành viên mới vào team có khi còn quên việc phải sử dụng code như vậy nữa. Vậy nên chăng ta nên sử dụng một lớp là DebugLog.java như sau:

public class DebugLog {
    public static void i(String tag, String msg) {
        if (Config.IS_DEBUG) {
            Log.i(tag, msg);
        }
    }
}

Tốt rồi, code đã trở nên sáng sủa hơn nhiều.Thế nhưng có một vấn đề nhỏ với DebugLog.

DebugLog.i(TAG, "String: " + numberOfString);

Đoạn code trên tưởng chừng như vô hai nhưng lại khiến cho ta tốn thêm 4 bước thuật toán cộng chuỗi trong khi chuỗi mới chẳng được sử dụng nếu cờ IS_DEBUG được đặt bằng false. Vậy cách giải quyết ở đây là gì nhỉ?
Chẳng có cách nào để khắc phục việc này đâu. Hãy coi đây là một cái giá phải trả cho việc quản lí dễ dàng hơn.

Lưu mật khẩu signing key vào file gradle.properties

Nếu có bạn nào tìm hiểu sâu đến grade thì đều biết đây là một nền tảng hỗ trợ việc build tự động theo kịch bản. Thông thường khi release, bạn sẽ tạo ra một kịch bản build như sau:

signingConfigs {
    release {
        storeFile file("release.keystore")
        storePassword "storepassword"
        keyAlias "myproject"
        keyPassword "keypassword"
    }
}

Sau đó bạn đẩy file này lên version control như GIT hoặc SVN. Tình cờ thế nào ai đó có quyền truy cập vào version control của bạn. Và bạn mất đi signing key, một trong những chìa khóa cốt lõi cho file apk của bạn trên GooglePlay.
Một giải pháp cho vấn đề này là chúng ta sẽ sử dụng file gradle.properties như sau:

KEYSTORE_PASSWORD=storepassword
KEY_PASSWORD=keypassword

Và thay vì viết một kịch bản như trên, bạn có cách viết lại khác hơn nhiều:

signingConfigs {
    release {
        try {
            storeFile file("release.keystore")
            storePassword KEYSTORE_PASSWORD
            keyAlias "myproject"
            keyPassword KEY_PASSWORD
        }
        catch (ex) {
            throw new InvalidUserDataException("You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties.")
        }
    }
}

Lúc này, chúng ta chỉ cần pull kịch bản lên version control và giữ lại password cho riêng mình.

16 Likes

Mình thực sự chưa hiểu rõ về navigatesutils trong phần cấu mục cho code Android. Bạn có thể giải thích rõ hơn được không? Cụ thể là các class trong đó để làm gì? Nếu ví dụ cụ thể thì quá tốt. :smiley:

Mình cũng đã không ít lần thắc mắc về cấu trúc thư mục trong Android sao cho phù hợp với MVC. Cũng có người bạn trên facebook chia sẽ cho mình 1 cấu trúc thư mục. Mình sẽ dán nó ở đây để các bạn cùng tham khảo.

2 Likes

Hi GDG :smiley:

Mình có góp ý phần Log thay vì đặt biến như vậy bạn có thể dùng proguard để unuse hết các method use Log object trước khi submit app.

Mở file proguard-rules.pro và thêm vào :

Log

-assumenosideeffects class android.util.Log {
public static *** v(...);
public static *** d(...);
public static *** i(...);
public static *** w(...);
public static *** e(...);
}

Sau đó save lại và nhớ cấu hình run proguard khi build app ở Build Variant release nhé.

Link hướng dẫn tại đây : http://stackoverflow.com/questions/13218772/removing-log-call-using-proguard

Và cho mình hỏi là mất storgekey và keyPassword như thế nào ? 2 key này mình nhớ đâu có chỉnh sửa đc, ví dụ có người biết 2 key này thì tác hại như thế nào bạn ?

6 Likes

Òa cách này của bạn hay quá. Nó giải quyết được luôn vấn đề phát sinh từ phương pháp của mình.

Còn cái key thì là mấy bác bên Google bảo thế mình cũng chẳng biết.

3 Likes

Util là class chứa các phương thức dùng chung. Ví dụ như hàm để cắt chuỗi hay đơn cử nhất là TextUtils trong Android.
Navigate là phương thức xử lí việc điều hướng ví dụ như update screen name hay các tác vụ liên quan.

3 Likes

Hay nhá bạn. Mình cảm ơn

1 Like

Em chưa bao giờ dùng Log, em toàn dùng Toast để debug thôi :frowning:

2 Likes

Toast là những cái show ra với người dùng. Những không tin như id, facebook id hay kiểu kiểu thế chẳng lẽ cũng show ra cho họ biết sao?

Chắc là bạn ấy viết Toast để test xong rồi xoá

1 Like

Thực ra là toast hiện lên rồi biến mất, thời gian khá nhanh lại không copy được vào máy tính hoặc tính lưu triwx không cao.

Anh cố gắng viết thêm nhiều bài thế này nữa. Rất hữu ích

Đợt này anh bận dự án quá vẫn chưa viết được thêm. Hic hic.

Em dùng xong xóa, hoặc thay nội dung Toast sau khi đã test thành một nội dung nào đó dành cho user

Khi cần liệt ra nhiều giá trị thì toast không ổn một tí nào :smile:

Một câu trả lời trên Stackoverflow khá hay
https://davidng94.files.wordpress.com/2016/01/2016-01-28_00-55-13.jpg?w=640

Tại sao trong cấu trúc thư mục cho code Android lại không có thư mục controllers vậy GDGHN_AndroidTeam?

Mình chưa hiểu chỗ thư mục example lắm, tại sao tên project lại đặt ngang hàng với những thư mực khác vậy.

Không biết mọi người thấy thế nào chứ mình phản đối cách dựng skeleton tùy ý. Ngồi debug mấy dự án này siêu khoai vì logic nhảy lung tung, viết function cũng ngẫu nhiên, thích là đặt chẳng qui chuẩn gì cả.

Nên dùng các tool làm scaff folding, vừa tiện, vừa gõ command tạo skeleton nhanh.

Vậy bạn có thể cho biết 1 vàu cái tool được không?

bài viết rất bổ ích, có 1 chức năng mà trước giờ em hok tài nào hiểu nổi là debug bằng breakpoint trong android studio,dubug breakpoint giống như là mình chỉ chạy 1 đoạn nhỏ code trong android nhưng nó hoạt động rất lạ và khó nắm bắt. Trước giờ toàn debug bằng log.java, logcat là chính

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?