Sunday, June 29, 2014

What is a compiler?

Máy tính chỉ có thể hiểu được một thứ ngôn ngữ duy nhất và ngôn ngữ này bao gồm các tập hợp các số 1 và 0, được gọi là machine language (mã máy).

Một đoạn mã máy đơn giản:
0000010011110

Đoạn mã máy cho phép người dùng nhập vào 2 số, và xuất ra tổng của 2 số đó
0000010011110
0000111110100
0001010011110
0001111010100
0010010111111
0010100000000

Bạn có thể hình dung rằng, lập trình cho máy tính trực tiếp bằng mã máy chỉ sử dụng 1 và 0 rất khó khăn và dễ bị lỗi. Để khiến việc lập trình dễ dàng hơn, các ngôn ngữ bậc cao đã được phát triển.

Dưới đây là đoạn mã được viết bằng ngôn ngữ C++, đoạn code này có chung mục đích với đoạn mã máy phía trên:
1
2
3
4
5
6
7
int a, b, sum;
     
cin >> a;
cin >> b;
             
sum = a + b;
cout << sum << endl;
Thậm nếu bạn không thực sự hiểu đoạn mã trên thì bạn cũng thấy rằng sẽ dễ dàng hơn khi lập trình bằng C++ thay vì bằng mã máy.

Máy tính chỉ có thể hiểu được mã máy, và con người thì lại muốn dùng ngôn ngữ bậc cao để lập trình do đó ngôn ngữ bậc cao phải được dịch thành mã máy. Do đó một chương trình đặc biệt gọi là compiler, interpreter hoặc assembler đã được tạo ra.

C++ được thiết kế để trở thành ngôn ngữ đã biên dịch. Hiểu chung chung là nó được dịch sang mã máy và khiến cho việc tạo ra các chương trình hiểu quả hơn. Để làm điều này, một bộ các công cụ được cung cấp, được biết đến như development toolchain, mà thành phần chính là compiler và linker. 

Android các nguyên tắc thiết kế

Các nguyên tắc này được phát triển bởi Android User Experience Team giúp người dùng cảm thấy thú vị khi trải nghiệm.

Enchant Me

Delight me in surprising ways


Vẻ ngoài đẹp, animation đặt đúng chỗ, sound effect đúng thời điểm. Một loạt các effect tinh tế sẽ khiến người dùng cảm thấy có một lực mạnh mẽ ở bàn tay của họ.

Real objects are more fun than buttons and menus


Cho phép người dùng chạm và thao tác trực tiếp lên các đối tượng trong app của bạn. Khiến cho người dùng có nhiều cảm xúc hơn.

 Let me make it mine


Mọi người thích thêm những thứ của riêng mình vào app, bởi vì nó giúp họ cảm thấy như ở nhà và có toàn quyền quết định. Bạn nên có một setting mặc định thật hợp lý, nhưng bên cạnh đó cũng cần có các sự lựa chọn và tùy chỉnh trong chương trình.

Get to know me


Hãy tìm hiểu và ghi nhớ sở thích của người dùng. Hơn là bắt họ đưa ra lựa chọn giống nhau hết lần này tới lần khác.


Simplify My Life

Keep it brief


Đưa ra các cụm từ đơn giản. Mọi người có xu hướng bỏ qua các câu nếu thấy chúng dài.

Pictures are faster than words


Hãy nghĩ đến việc sử dụng hình ảnh để trình bày ý tưởng của bạn. Hình ảnh khiến mọi người chú ý và mang lại nhiều hiệu quả hơn là dùng từ ngữ

Decide for me but let me have final say


Đưa ra các lời khuyên tốt nhất cho người dùng trước khi hỏi họ. Nếu có quá nhiều sự chọn lựa sẽ khiến người dùng cảm thấy khó chịu. Trong trường hợp họ làm sai điều gì đó hãy cho phép họ làm lại

Only show what i need when i need it


Người dùng bị quá tải nếu họ thấy quá nhiều thứ tại cùng một thời điểm. Hãy chia nhỏ các tác vụ, thông tin khiến chúng "dễ tiêu hóa". Hãy giấu đi những tùy chọn không cần thiết tại một thời điểm, và chỉ cho người dùng cách tìm ra nó.

I should always know where i am


Hãy cho người dùng biết họ đang ở đâu trong chương trình của bạn. Làm cho các phần trong app của bạn trông riêng biệt. Nến có một dấu hiệu nào đó để chỉ ra rằng app đang chuyển trạng thái qua trạng thái khác hoặc chuyển từ phần này qua phần khác. Phải có sự phản hồi nào đó của các task đang được xử lý.

Never lose my stuff


Cái này dịch ra thì hơi khó hiểu. Đại loại là nếu bạn có nhiều ứng dụng, mà các ứng dụng này đều lưu một số thông tin giống nhau của người dùng, thì thay vì bắt họ tạo lại các thông tin này bạn hãy đồng bộ hóa thông tin giữa các ứng dụng với nhau. Cho phép người dùng có thế lấy ra thông tin này ở bất cứ app nào.

If it looks the same, it should act the same


Cái này hiểu như là nếu bạn có hai cái button giống nhau thì hai cái button này phải thực hiện cùng một hành động. Tranh trường hợp hai button giống nhau thực hiện hai hành đông khác nhau.

Only interupt me if it's important


Tưởng tượng app của bạn như một người trợ lý. Hãy xử lý những điều vụn vặt không quan trọng cho người dùng sau đó thông báo lại cho họ thay vì đưa nó trực tiếp cho họ xử lý.


Make Me Amazing

Give me tricks that work everywhere


Người dùng sẽ nhanh chóng nắm bắt được cách sử dụng ứng dụng của bạn nếu nó tận dụng cách thiết kế và cách sử dụng của các ứng dụng Android khác.

It's not my fault


Hãy nhẹ nhàng khi hướng dẫn người dùng thực hiện các thao tác. Nếu họ làm điều gì đó không đúng, hãy đưa ra một hướng dẫn khôi phục rõ ràng kèm theo các thông tin kỹ thuật.

Sprinkle encouragement


Chia nhỏ các tác vụ khiến chúng dễ thực hiện hơn. Đưa ra các phản hồi trên các thao tác.

Do the heavy lifting for me


Khiến cho những người mới sử dụng cảm thấy mình chuyên nghiệp bằng cách cho phép họ làm những việc mà họ không bao giờ nghĩ mình làm được. Ví dụ chỉnh sửa hình ảnh với nhiều effect tuyệt vời qua một vài phím tắt sẽ khiến cho một photographer nghiệp dư trông thật cool :).

Make important things fast


Không phải thao tác nào cũng giống nhau. Chỉ ra cái nào là cái quan trọng nhất trong ứng dụng của bạn, làm cho nó dễ dàng được tìm thấy và nhanh chóng khi sử dụng. Như nút chụp hình của một cái camera hay là nút tạm dừng của máy nghe nhạc

Friday, June 27, 2014

Process và Thread

Khi một application component khởi chạy và application không có bất kỳ component nào khác đang chạy, Android system khởi động một Linux process mới cho application với một thread thực thi. Mặc định tất cả các component trong cùng một ứng dụng chạy trên cùng một process và thread(main thread). 


Process

Mặc định tất cả các component của cùng một Activity chạy trong cùng một process. Nếu bạn muốn kiểm soát process nào dành cho component nào thì bạn có thể làm việc đó tại file manifest.

Mỗi loại component <activity>, <service>, <receiver>, <provider> hỗ trợ một thuộc tính android:process mà chỉ ra một process mà component chạy trên. Có thể có các component của các ứng dụng khác nhau chạy trên cùng một process.

<application> hỗ trợ thuộc tính android:process để thiết lập process mặc định cho tất cả components

Android có thể kill một process tại một thời điểm bất kỳ khi bộ nhớ không đủ hoặc được yêu cầu bởi các process khác đang trực tiếp phục vụ người dùng. Process được khởi động lại cho các component đó khi chúng làm việc trở lại

Khi quyết định process nào sẽ bị kill, Android sẽ so sánh mức độ quan trọng của process đó với người dùng. Ví dụ android sẽ kill process đang host một activity trong trạng thái paused thay vì process đang host activity trong trạng thái resumed.

Process Lifecycle
Android system cố gắng duy trì một process của ứng dụng càng lâu càng tốt, nhưng khi có một process mới quan trọng nó sẽ xóa bỏ process cũ để lấy lại bộ nhớ. Để chỉ ra process nào sẽ bị kill, hệ thống sẽ gán cho mỗi process một "importance hierarchy" dựa trên các component  chạy trên process và trạng thái của các component  đó. Các process có mức độ importance thấp sẽ bị loại bỏ trước...

Có 5 cấp độ importance hierarchy như sau:

1. Foreground process:
Là một process được yêu cầu cho các hành động hiện hành của user. Một process được coi là foreground nếu bất kỳ điều kiện nào sau đây là đúng:
  • Nếu nó host một Activity mà user đang tương tác với (trạng thái activity là resumed)
  • Nếu host một Service mà rằng buộc với activity mà user tương tác
  • Nếu host một Service đang chạy ở foreground - service đã gọi phương thức startForeground()
  • Nếu nó host một Service mà đang thực thi một trong các lifecycle callback của nó (onCreate(), onStart() hoặc onDestroy()).
  • Nếu nó host một BroadcastReceiver mà đang thực thi onReceiver()
Thông thường chỉ có một vài foreground process tồn tại ở một thời điểm, và chúng chỉ bị kill khi bộ nhớ không đủ để chạy toàn bộ foreground process cùng lúc.

2. Visible process:
Là một process không có bất kỳ foregound component nào, nhưng vẫn có thể tác động tới nhưng gì user nhìn thấy trên màn hình. Một process visible nếu bất kì điều kiện nào sau đây đúng:

  • Nếu nó host cho một Activity mà không ở trạng thái foreground, nhưng vẫn visible cho user (khi hàm onPause() được gọi). Ví dụ: khi một foreground Activity khỏi động một Dialog, Activity lúc này sẽ hiện ỏ phía sau dialog
  • Nó host cho một Service mà ràng buộc với một visible Activity (foreground Activity)
Visible process rất quan trọng và sẽ không bị kill trừ khi việc kill chúng là để giữ cho foreground process chạy.

3. Serivce process
Một process chạy một service khi hàm startService() được gọi và không rơi vào một trong hai category cao hơn. Mặc dù service process không trực tiếp gắn với bất cứ thứ gì mà user có thể nhìn thấy nhưng chúng làm những việc mà user quan tâm (như là play music in background, hoặc là download dữ liệu từ trên mạng), hệ thống sẽ duy trì chúng đến khi không còn đủ bộ nhớ cho foreground và visible process chạy

4. Background process
Một process dữ một Activity mà không còn visible cho user (hàm onStop() của Activity được gọi). Process không còn tác động tới user experience, và hệ thống có thể kill chúng bất kỳ lúc nào để duy trì bộ nhớ cho 3 loại process bên trên. Thông thường có rất nhiều background process chạy, chúng được lưu trong một danh sách LRU(least recently used) để đảm bảo rằng process và activity nào vừa mới được user sử dụng sẽ bị kill sau cùng.  Nếu một activity implement các method của lifecycle một cách đúng đắn thì việc kill process đó sẽ không ảnh hưởng tới user experience, bởi vì khi user quay trở lại activty đó nó sẽ khôi phục lại toàn bộ trạng thái.

5. Empty process
Một process không dữ bất kỳ một active application component nào, lý do duy nhất để duy trì process này là để caching, cải thiện thời gian startup. Hệ thống thường xuyên kill các process này theo thứ tự để duy trì sự cân bằng tài nguyên của toàn bộ hệ thống giữa các process cache và các kernal cache cơ bản.


Lưu ý: Android xếp hạng process ở cấp độ cao nhất mà nó có thể đạt được, dựa trên mức độ quan trọng của các component đang được active trong process. Ví dụ, nếu một proess host cho một service và một visible activiy thì process được xếp hạng là một visible process chứ không phải là service process.

Cấp độ của process có thể tăng lên bởi vì có các process khác phụ thuộc vào nó - process mà đang phục vụ cho các process khác thì cấp độ sẽ không bao giờ thấp hơn process mà nó đang phục vụ. Ví du: nếu một content provider trong process A đang phục vụ một client trong process B hoặc một service trong process A rằng buộc với một component trong process B thì cấp độ process A luôn bằng hoặc lớn hơn B.


Threads

Khi ứng dụng được khởi động, hệ thống sẽ tạo ra một thread cho ứng dụng gọi là "main". Thread rất quan trọng bởi vì nó chịu trách nhiệm điều phối các Event tới các user interface widget thích hợp, bao gồm cả các draw events. Nó cũng là thread mà chương trình của bạn sử dụng để tương tác với các component từ Android UI toolkit (các component từ android.widget và android.view package). Main thread còn được gọi là UI thread.

Hệ thông không tạo ra thread riêng cho từng component. Tất cả các thành phần chạy trên cùng process đều được khởi tạo trên UI thread, và hệ thống sẽ gọi cho từng component được đưa ra từ thread đó. Vì thế các method mà phản hồi cho system callback(như onKeyDown() hoặc một lifecycle callback) lúc nào cũng chạy trên UI thread của process.

Ví dụ, khi user touch vào một button trên màn hình, UI thread của bạn sẽ gửi touch event cho widget để chuyển trạng thái của nó sang pressed và post một invalidate request vào event queue. UI thread sẽ lấy các request từ trong queue và notify cho widget mà cần phải redraw.

Khi ứng dụng của bạn thực hiện một công việc chuyên sâu để đáp ứng sự tương tác của người dùng. Mô hình sử dụng một thread có thể mạng lại hiệu suất kém. Đặc biệt nếu bạn làm mọi thứ trên UI thread, thực hiện các tình toán cần nhiều thời gian như là truy cập network hoặc truy vấn dữ liệu sẽ block UI. Khi UI bị block, không event nào được gửi đi bao gồm cả drawing event. Và theo góc nhìn của người dùng thì chương trinh đang bị treo. Tệ hơn nữa nếu chương trình treo lâu hơn 5s cửa sổ "Application not responding" sẽ hiện ra, người dùng có thể sẽ đóng chương trình lại và uninstall nó nếu họ cảm thấy bực bội.

Android UI toolket is not thread-safe. Vì vậy bạn không thể thao tác với UI từ một worker thread, bạn phải làm mọi thao tác cho UI thread trên UI thread. Có 2 nguyên tắc cơ bản bạn cần phải lưu ý:

  1. Không được block UI thread
  2. Không được truy cập Android UI toolkit từ bên ngoài UI thread.

Worker threads

Nếu bạn phải thực hiện các thao tác không tức thời, bạn phải đảm bảo rằng các thao tác đó được thực hiện trên các thread tách biệt với UI thread ("backgound" hoặc "worker" thread)

Ví dụ bên dưới, một đoạn code đơn giản thự hiện việc lắng nghe sự kiện click của user download một tấm hình sau đó hiển thị lên trong một thread riêng:


public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
        }
    }).start();
}

Trước hết, ta thấy đoạng code này có vẻ ổn, bởi vì nó tạo ra một thread mới để xử lý nework operation. Tuy nhiên nó đã vi phạm nguyên tắc thứ 2 nêu trên. Ví dụ này đã modify ImageView từ một worker thread thay vì làm nó trong UI thread.

Để giải quết vấn đề này, Android đã cung cấp một vài cách để truy cập UI thread từ các thread khác:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)
Ví dụ, chúng ta có thể fix đoạn code bên trên như sau:

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}
 Đoạn code trên là thread-safe: network operation được thực hiện trong thread riêng trong khi ImageView được xử lý trong UI thread.

Tuy nhiên nếu bạn code theo cách này thi chương trình của bạn sẽ rất phức tạp và khó phát triển lên. Để giải quyết những thao tác phức tạp hơn với một worker thread, bạn nên sử dụng Handler trong worker thread để gửi message cho UI thread. Nhưng giải pháp tốt nhất là extend AsyncTask class, điều này đơn giản hóa việc hoạt động của worker thread.

Sử dụng AsyncTask

AsyncTask cho phép bạn thực hiện các việc bất đồng bộ trên user interface. Nó thực hiện các công việc tốn nhiều thời gian trên worker thread sau đó thông báo kết quả về cho UI thread.

Để sử dụng bạn vào subclass AsynTask và implement phương thức callback doInBackground(), cái mà chạy trên backgroud thread. Để cập nhật UI, bạn phải implement phương thức onPostExecute(), phương thức này nhận kết quả từ doInBackground và chạy trên UI thread. Bạn có thể chạy task bằng cách gọi excute() trên UI thread.

Ví dụ:
public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    /** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }
    
    /** The system c
om doInBackground() */
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}

Chú ý: Một vấn đề khác có thể xuất hiện khi sử dụng một worker thread đó là Activity bị khởi động lại do runtime configuration thay đổi (như khi user xoay màn hình) có thế kill worker thread của bạn.