12/08/2018, 17:11

Sử dụng AsyncDisplayKit để phát triển ứng dụng iOS

Năm 2011 có một anh chàng tên Mike Matas đã giới thiệu một cách mới để tương tác với giao diện người dùng. Nó mang tới một trải nghiệm thật đáng kinh ngạc, bạn sẽ không thể tưởng tượng được nó mượt tới mức nào. Năm ngoái mình có một dự án với giao diện thực sự rất phức tạp, logic phức tạp ...

  • Năm 2011 có một anh chàng tên Mike Matas đã giới thiệu một cách mới để tương tác với giao diện người dùng. Nó mang tới một trải nghiệm thật đáng kinh ngạc, bạn sẽ không thể tưởng tượng được nó mượt tới mức nào.

  • Năm ngoái mình có một dự án với giao diện thực sự rất phức tạp, logic phức tạp và thực sự có rất nhiều control trên một màn hình. Đó chính là nguyên nhân dẫn tới sự trải nghiệm người dùng rất tệ. Và thực sự ban đầu mình không muốn dùng. Vì vấn đề đó mình có tìm hiểu và biết tới AsyncDisplayKit.

  • Hôm nay mình muốn giới thiệu các bạn về AsyncDisplayKit, các nguyên nhân dẫn tới trải nghiệm người dùng kém, các vấn đề tưởng chừng đơn giản nhưng lại rất dễ làm giảm performance khi viết code native mà không sử dụng framework AsyncDisplayKit. Điều đó có nghĩa là các vấn đề đó được giải quyết bằng AsyncDisplayKit.

  • AsyncDisplayKit được viết bởi công ty Push Pop Press và cuối năm 2011, nó chính thức được mua lại bởi Facebook. Đó là lý do vì sao bạn trải nghiệm Facebook thấy rất mượt.

  • Với những lý do trên và vì mục đích cải thiện ứng dụng của công ty, lần đó mình đã quyết tâm tìm hiểu AsyncDisplayKit. Cuối cùng mình luôn cảm thấy nó thực sự hữu ích cho những ai thực sự quan tâm tới performance của ứng dụng.

1. AsyncDisplayKit - nó là gì? 2. Đơn vị cơ bản cuả AsyncDisplayKit 3. Cơ chế Intelligent Preloading 4. Cách sắp xếp layout cho các thành phần giao diện 5. Hit Test Slop 6. Optimizations

1. AsyncDisplayKit - nó là gì?

  • AsyncDisplayKit nó là một framework, nó còn có một tên gọi nữa là Texture (tên gọi sau này khi nó được đầu tư hơn 70% bởi Pinterest). Là framework trên hệ điều hành iOS với mục đích tối ưu hoá ứng dụng bằng cách tạo ra một giao diện người dùng "an toàn". Hơi lạ phải không, bạn có thể hiểu đơn giản thế này, để hiển thị giao diện người dùng của một màn hình login, trước khi hiển thị thì bạn sẽ mất chi phí về thời gian cho việc tính toán toạ độ, ràng buộc các control... Và chi phí đó nó phần lớn được cấp bởi main thread. Tóm lại là việc tính toán, tạo ràng buộc... là do thằng main thread nó làm. Thằng main thread nó làm quá nhiều việc dẫn tới nó sẽ mất nhiều thời gian để làm, trong khi đó các thread khác chơi bời lêu lổng. Thì ở đây, thằng AsyncDisplayKit nó có khả năng chuyển hoá các công việc đó xuống background thread. Tức là thằng background thread nó chuẩn bị đầy đủ cho main thread và công việc của main thread chỉ là hiển thị. Chính vì vậy ứng dụng mới mượt, trơn tru.
  • Các bạn có thể truy cập vào link https://texturegroup.org/ để tìm hiểu về nó

2. Đơn vị cơ bản cuả AsyncDisplayKit

  • Trong framework này thì đơn vị cơ bản của nó có tên gọi là Node (ASDisplayNode)
  • Để giao diện ứng dụng mượt và phản hồi tương tác nhanh thì ứng dụng của bạn nên hiển thị 60 frame trong 1s. Vâng đó chỉ là cái chuẩn vàng trong iOS thôi, chứ không được thế đâu.
  • Vậy làm sao để làm cho tốc độ hiển thị như vậy. Câu trả lời là hãy để main thread chỉ làm việc liên quan tới hiển thị thôi, chứ đừng ép nó làm việc khác không liên quan tới giao diện.
  • Ở đây thì AsyncDisplayKit cho phép di chuyển các tác vụ với chi phí cao và tác vụ liên quan giao diện tốn kém về chi phí sang thằng background thread. Với nhiều tip trick, AsyncDisplayKit đã làm được điều đó, chúng ta cùng tìm hiểu bên dưới nhé.

3. Cơ chế Intelligent Preloading

  • Hãy nhìn vào bức ảnh

Intelligent Preloading

  • Hãy suy nghĩ trước khi đọc tiếp, bạn sẽ thấy một điểm khác rất hay. Với loại giao diện có thể scroll thì dải giao diện đó được chia ra làm 3 trạng thái.

    • Preload: Nơi xa nhất trước khi hiển thị, đây chính là nơi (là lúc) chúng ta thu thập những dữ liệu từ server hoặc thao tác xử lý dữ liệu.
    • Display: Thực hiện các tác vụ như xử lý giải mã hình ảnh, text...
    • Visible: Node được hiển thị trên màn hình.
  • Điểm hay ở đây chính là 2 trạng thái Preload và Display bạn có thể custom độ dài của nó, có nghĩa là bạn có thể thay đổi cho phần preload lên thành 2 màn hình hay Display lên thành 3 màn hình, tuỳ thuộc vào mong muốn, tuy nhiên khi tăng lên như vậy có nghĩa là dung lượng của 2 phần này cũng tăng lên. Chính vì vậy bạn cần cân nhắc nó bao nhiêu là hợp lý.

  • Vậy vì sao lại cần phải chia 3 state như vậy? Đó là để cho phép bạn custom, cho phép lượng bạn muốn chuẩn bị trước khi hiển thị lên màn hình là bao nhiêu, bạn chuẩn bị được nhiều thì trải nghiệm người dùng càng mượt. Hay nói cách khác, ví dụ bạn phải thuyết trình, tới giờ bạn phải đi chuẩn bị nào là máy chiếu, nào là bàn ghế... rất nhiều thứ. Và tất nhiên có thể tới giờ thuyết trình bạn vẫn chưa bắt đầu được. Vậy thay vì vậy, bạn kêu gọi hỗ trợ từ người khác, kêu 2 hay 3 ông tới làm việc đó, tới giờ thì bạn chỉ phải thuyết trình thôi. Như đã nói ở trên về dung lượng khi bạn tăng preload và display, ở đây là bạn thêm người. Đây là điểm hạn chế.

  • Ví dụ cho phần này

tableNode.view.leadingScreensForBatching = 3.0;  // overriding default of 2.0

4. Cách sắp xếp layout cho các thành phần giao diện

  • Phần này sẽ rất đơn giản nếu bạn nào đã từng code android. Nó được xây dựng cơ bản trên horizontal và vertical layout.

  • Ngoài ra extend cho phần này nó có một vài loại layout đặc biệt:

    • ASStackLayoutSpec
    • ASInsetLayoutSpec
    • ASOverlayLayoutSpec
    • ASAbsoluteLayoutSpec ...
  • Toàn bộ các loại layout trên đều base trên hai loại Layout Specs và Layout Elements

5. Hit Test Slop

  • Trên iOS khi bạn muốn phạm vi tương tác ví dụ của một button lớn hơn vùng hiển thị của button đó thì bạn phải custom func hitTest.
  • Với AsyncDisplayKit bạn chỉ cần chỉ định khoảng bạn muốn rộng ra là bao nhiêu thôi. Rất đơn giản và nhanh.
ASTextNode *textNode = [[ASTextNode alloc] init];

CGFloat padding = (44.0 - button.bounds.size.height)/2.0;
textNode.hitTestSlop = UIEdgeInsetsMake(-padding, 0, -padding, 0);

6. Optimizations

6.1 Layer Backing

  • Rất nhiều trường hợp bạn đã từng sử dụng layer thay vì view. Đã bao giờ bạn cảm nhận được là performance của nó tốt hơn view. Nếu có thì bạn cảm nhận đúng đó.
  • Trong iOS để có thể tương tác với một layer thì bạn sẽ phải có 1 view nằm trên nó để nhận tương tác của bạn. Nếu bạn nào hay debug UI thì sẽ thấy điều này. Mỗi một layer sẽ có một view nằm bên trên.
  • Vậy với những control không nhận tương tác thì đâu cần view đó làm gì đúng không? AsyncDisplayKit cho phép enable layer backing và bạn nên sử dụng nó khi không cần tương tác với control.
rootNode.isLayerBacked = YES;

6.2 Subtree Rasterization

  • Mục này có nghĩa là chuyển đổi tất cả các layer về một layer duy nhất (làm phẳng)
  • Khi debug UI thì bạn sẽ thấy với một một view nó sẽ được draw thành 1 layer. Với màn hình rất nhiều view thì có nghĩa là số lượng layer sẽ tăng lên nhiều.
  • Tuy chuyển đổi về thành 1 layer có thể làm tăng hiệu suất nhưng nó lại là một hạn chế khi bạn cần debug UI xem nó sai ở view nào. Vì tất cả đều quy về một layer

Note:

  • Còn rất nhiều tip xử lý liên quan tới việc cải thiện performance nữa. Các bạn có thể tham khảo tại https://texturegroup.org/docs/getting-started.html để hiểu về lý do làm giảm, giải pháp làm tăng performance nhé.

Cuối cùng, nếu bạn cảm thấy hứng thú với nó, hãy thử code một app code không sử dụng framework này và một app sử dụng nó. Mình nghĩ bạn sẽ thấy khác nhau hẳn đấy.

0