Header Ads

Kinh nghiệm với lập trình 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

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.

Sử dụng thư viện Volley hoặc OkHttp thay vì tự viết một HTTP client của riêng bạn

ResponseData responseData = new ResponseData();
String data = generalRequest.toString();
if (!Utility.isNetworkConnected(mContext)) {
    responseData.setStatus(Response.CLIENT_ERROR_NO_CONNECTION);
    return responseReceiver.parseResponse(idLoader, responseData, requestType);
}

String url;
if (Method.POST == generalRequest.getMethod()) {
    url = Config.SERVER_URL;
} else {
    url = Config.IMAGE_SERVER_URL + data;
}

ContentResponse outData = sendRequest(url, generalRequest.getMethod(), data, timeoutConnect, timeoutRead);
if (outData.status == Response.CLIENT_SUCCESS) {
    responseData.setStatus(Response.CLIENT_SUCCESS);
    responseData.setText(outData.content);
    if (requestType == RequestType.JSON && responseData.getText() != null) {
        try {
            responseData.makeJSONObject();
            int code = getResponseCode(responseData.getJSONObject());
            if (code == Response.SERVER_BLOCKED_USER || code == Response.SERVER_USER_NOT_EXIST) {
                sendBroadcastHasBlockAndDeactive(code);
            }
        } catch (JSONException e) {
            e.printStackTrace();
            outData.status = Response.CLIENT_ERROR_PARSE_JSON;
            responseData.setStatus(outData.status);
        }
    }
} else {
    responseData.setStatus(outData.status);
}
if (responseReceiver == null)
    return null;

return responseReceiver.parseResponse(idLoader, responseData, requestType);
Quá nhiều dòng code cho một chức năng. Chưa kể bạn còn phải quan tâm rất nhiều thứ khác nữa để tạo ra được một thư viện http tuyệt vời cho riêng bạn. Mình đã từng nếm trái đắng, siêu đắng khi tự viết một framework liên kết với raspberry pi. Tất cả mọi chuyện kết thúc khi mình chuyển qua dùng okhttp với vài ba dòng lệnh đơn giản:
public static final MediaType JSON  = MediaType.parse("application/json; charset=utf-8");

OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
    RequestBody body = RequestBody.create(JSON, json);
    Request request = new Request.Builder().url(url).post(body).build();
    Response response = client.newCall(request).execute();
    return response.body().string();
}

Giữ cho file colors.xml của bạn hữu ích hơn bằng cách sử dụng bảng màu

<!-- Palette -->
<color name="primary">#FFC0392B</color>
<color name="primary_dark">#FFD32F2F</color>
<color name="primary_light">#FFFFCDD2</color>
<color name="secondary">#FF449D44</color>
<color name="secondary_light">#FF6EC02B</color>
<color name="divider">#FFB6B6B6</color>
<color name="divider_highlight">#FFFFFFFF</color>

 <!-- Text view -->
<color name="icons">#FFFFFFFF</color>
<color name="secondary_text">#FFB0ADAD</color>
<color name="secondary_text_highlight">#FFDADADA</color>
<color name="primary_text">#FF595959</color>
<color name="primary_text_highlight">#FF929292</color>
Theo đó thì từng màu được định nghĩa sẽ có những ý nghĩa khác nhau như sau:
  • primary: Màu chủ đạo của ứng dụng. Thông thường màu này sẽ nằm trên navigation bar, các text view làtitle của màn hình.
    • primary_dark: Ít khi được dùng nhưng thường có để làm nổi bật màu primary
    • primary_light: Ít khi được dùng nhưng thường có để làm nổi bật màu primary
  • secondary: Màu thứ hai được dùng nhiều trong ứng dụng. Màu này sẽ nằm ở các đoạn text được highlight hoặc những vị trí thấp hơn tone màu primary
    • secondary_light: Ít khi được dùng nhưng thường có để làm nổi bật màu secondary
  • divider: Dành cho các vách ngăn, đường viền của các view.
    • divider_highlight: Dành cho những cách ngăn nhạt màu hơn. Thường được dùng khi màu nền không phải là màu đơn sắc.
  • icons: Màu chủ đạo dành cho icon, chữ nằm trên nền có màu. Thông thường toàn bộ nút trong ứng dụngsẽ sử dụng màu này.
  • primary_text: Các phần chữ nội dung có tone màu chính.
    • primary_text_highlight: Nhấn mạnh màu primary_text
  • secondary_text: Các phần chữ nội dung có tone màu yếu hơn.
    • secondary_text_highlight: Nhấn mạnh màu secondary_text

Các trường hợp tuyệt đối tránh trong colors.xml

1.Định nghĩa màu
Làm thế nào để bạn phân biệt được giữa màu xám, màu xám nhạt, màu xám nhạt hơn chút nữa?
<color name="gray">#FFDADADA</color>
<color name="gray_light">#FFDBDBDB</color>
<color name="gray_more_light">#FFDCDCDC</color>
2.Khai báo màu đã được định nghĩa trong Android
Chắc chắn là chúng ta sẽ không bao giờ định nghĩa là màu transparent rồi.

Hãy cho dimens.xml học theo colors.xml

Một ví dụ cho cách định nghĩa dimen:
<!-- 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>
Với cách định nghĩa này, bạn sẽ không cần phải định nghĩa quá nhiều những thông số dimen với nhau. Thông thường thì một view sẽ có kích thước tương quan với các view bên cạnh nó. Nhất là khi bạn phải thực hiện code trên nhiều kích thước thiết bị. Định nghĩa như thế này sẽ giúp bạn thu nhỏ hoặc phóng to các thành phần trong màn hình của bạn được dễ dàng hơn.

theo : http://daynhauhoc.com/

No comments:

Powered by Blogger.