Có rất nhiều ý kiến cho rằng JavaScript không phải là ngôn ngữ lập trình hướng đối tượng vì nó không có từ khóa class để tạo bản thiết kế đối tượng như các ngôn ngữ lập trình OOP khác (Java, Python, C#…). Tuy nhiên, trong JavaScript ta có thể làm điều tương tự với function
Về thừa kế (inheritance) trong JavaScript, tất cả các đối tượng đều thừa kế từ prototype. (protype là thuộc tính đặc biệt mà tất cả các đối tượng đều có. Nó tương tương như trong Java, tất cả các class đều thừa kế class Object. Phần prototype này mình sẽ viết một bài riêng để mọi người hiểu rõ hơn)
Tóm lại, JavaScript là một ngôn ngữ lập trình hàm nhưng đồng thời nó cũng là một ngôn ngữ lập trình hướng đối tượng vì nó có đầy đủ các tính chất của ngôn ngữ lập trình hướng đối tượng.
Một số ngôn ngữ wrapper lại JavaScript như TypeScript hay CoffeeScript cung cấp các từ khóa như class , extends sẽ giúp ta thấy rõ hơn tính chất hướng đối tượng của JavaScript. (Các ngôn ngữ này sau khi build chính là JavaScript).
Bài viết được sự cho phép của tác giả To Thi Van Anh
Nhắc lại cái vấn đề ở bài ngoại truyện đã đề cập đến đó là khi bạn thực hiện download một file là các định dạng như MS Excel, MS Word, Zip File, PDF, CSV, Text file… với Selenium Webdriver từ một ứng dụng web nào đó. Sau khi nhấn nút Download để tải file xuống, thì một popup window sẽ hiển thị ra, nó sẽ hỏi bạn là bạn muốn Mở file này, hay là bạn muốn Lưu nó lại xuống máy. Cửa sổ này không phải là HTML popup thông thường để chúng ta có thể inspect và lấy locator như các popup khác đâu nha, vì nó là window popup mà.
Như chúng ta đều biết, thì Selenium webdriver được sử dụng với các ứng dụng web mà thôi, tức là các ứng dụng chạy qua trình duyệt đó, và với kiểu popup trên ta sẽ không thể sử dụng trực tiếp Selenium webdriver rồi. Vậy thì bằng cách nào có thể xử lý được đây? Các bạn theo dõi tiếp bài viết này của mình nhé!
Có một cái ý nho nhỏ mà mình sẽ nói qua ở đoạn này đó là với trình duyệt Firefox và trình duyệt IE, bình thường nếu làm việc với IE thì nó sẽ luôn luôn hỏi là bạn muốn Mở file, hoặc Lưu, hoặc là Lưu ở một nơi khác (click vào rồi chọn đến thư mục bạn muốn lưu file ở đó). Còn với Firefox và Chrome thì bạn có thể vào phần Settings, để tùy chọn mặc định lưu file ở một nơi nào đó mà không cần hỏi gì hết, hoặc là chọn lựa chọn là mỗi khi download sẽ phải hỏi bạn xem bạn muốn lưu ở đâu rồi mới thực hiện lưu – lựa chọn này chính là hiển thị popup mà mình đã nói phía trên đó.
Và ở bài này mình sẽ nói đến trường hợp download file với Firefox thôi nhé, Chrome chắc cũng tương tự thôi, vì mình cũng chưa thử, nên không khẳng định chỗ này đâu ha. :D. Và trước khi bắt đầu chúng ta sẽ nói về một khái niệm hơi lạ nhưng lại hơi liên quan đó là MIME một chút nhé! :v
MIME là gì?
MIME viết tắt của Multi-purpose Internet Mail Extensions, nó là một chuẩn Internet về định dạng cho thư điện tử, hầu như mọi thư điện tử Internet được truyền qua giao thức SMTP theo định dạng MIME. Ví dụ như Web server và các trình duyệt đều có một danh sách các loại MIME, vì thế mà các browser và server có thể trao đổi các loại file khác nhau theo cùng phương thức. Một loại MIME có hai phần là type và subtype, ngăn cách nhau bởi 1 dấu /, ví dụ cho dễ hình dung nhé:
TextFile (.txt): text/plain
PDFFile (.pdf): application/pdf
CSVFile (.csv): text/csv
MS Excel File(.xlsx): application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
MS word File(.docx): application/vnd.openxmlformats-officedocument.wordprocessingml.document
Và liên quan trực tiếp đến mục đích lớn nhất của chúng ta là download được file, thì trước tiên phải truyền các MIME type nào vào một cái gọi là Firefox profile.
Firefox Profile Preferences
Để thực hiện download một file với Selenium Webdriver thì chúng ta sẽ cần phải thực hiện một vài setting cho trình duyệt thông qua Firefox profile preferences, không phải đi cài đặt bằng tay đâu nhá, mà là đưa nó vào trong code ấy :v. Dưới đây là một vài preference cần lưu ý:
setPreference(“browser.download.folderList”, 2);
0 – Default. Truyền vào là 0 nếu bạn muốn lưu file trên màn hình desktop.
1 – Với giá trị 1 thì lưu file mặc định vào thư mục Downloads
2 – Lựa chọn này khi bạn muốn lưu file vào một thư mục cụ thể nào đó trong máy tính của bạn.
Mặc định thì là một chuỗi trống – không có nội dung đó. Đoạn này bạn có thể truyền vào danh sách các loại MIME được lưu luôn xuống máy mà không cần hỏi bạn có muốn lưu file này hay không.
Tương tự như trên, giá trị mặc định của cái này cũng là một chuỗi trống, ở đây bạn sẽ truyền vào danh sách các loại MIME được mở luôn mà cũng không cần hỏi bạn nữa.
setPreference(“browser.download.dir”,path);
Giá trị của thằng này cũng là chuỗi trống, ở đây bạn có thể truyền vào đường dẫn mà bạn muốn lưu file ở một thư mục cụ thể nào đó trên máy bạn.
Về cơ bản thì những cái trên kia đã đủ đáp ứng nhu cầu cho việc download file của chúng ra rồi, tuy nhiên mình sẽ đưa thêm một vài preferences khác nữa để các bạn tham khảo nhé, và có thể bổ sung vào trong những trường hợp cần thiết khác mà không phải là download nữa!
Giá trị mặc định là True. Với True thì nó sẽ cảnh báo khi người dùng muốn mở một tập tin có khả năng thực thi, như kiểu các file exe, html, js gì đó đó. Nếu để False thì nó sẽ không hiển thị cảnh báo nữa mà sẽ luôn cho phép run.
Preference này có giá trị mặc định là False. Với giá trị True – Nó sẽ cho phép cửa sổ trình quản lý download được hiển thị khi quá trình download bắt đầu, ngược lại với False thì cửa sổ sẽ không được hiển thị, quá trình download diễn ra ngầm thôi.
Theo mình tìm hiểu thì có khá nhiều các preference khác nhau hỗ trợ các tác vụ khác nhau nữa, nhưng mà liên quan đến việc download file thì chủ yếu sẽ sử dụng những cái trên này, còn cả những cái khác nữa đấy :v nhưng nói đến đây đủ dùng rồi, nếu thiếu thì sau này bổ sung sau cũng được. hehe.
Bài viết mình có tham khảo từ nhiều nguồn thông qua tìm kiếm Google, trên stack overflow cũng nhiều người hỏi lắm, mình không tìm theo từ khóa tiếng việt, nên không rõ đã có ai nói chưa. Hehe. Hi vọng bài viết hữu ích cho tất cả các bạn, mình cũng rất mong nhận được các bình luận góp ý từ các bạn để hoàn thiện hơn mặt kiến thức cũng như trình bày.
Máy tính ảo là một trong những công cụ rất quan trọng đối với nhiều nhóm đối tượng sử dụng khác nhau, đặc biệt là những bạn đang theo học và làm việc trong các ngành như: mạng máy tính, máy chủ, lập trình web, phát triển phần mềm..
Mà để học những ngành trên thì Ubuntu nói riêng hay các hệ điều hành Linux nói chung là một trong những lựa chọn rất tốt, nếu không muốn nói là rất tuyệt vời.
Giống với hệ điều hành Windows 10, Linux cũng có một mớ các ứng dụng hỗ trợ cho việc tạo máy tính ảo, nhưng chỉ một vài trong số đó là miễn phí và đáng để sử dụng. Tiêu biểu là 2 cái tên VMWare Workstation - Player và Oracle VM VirtualBox.
Về công dụng thì 2 phần mềm này là như nhau, tùy vào nhu cầu và đặc thù công việc của bạn mà lựa chọn thôi.
Trên blog đã có bài hướng dẫn chi tiết về cách cài đặt VMWare Workstation trên Ubuntu rồi, nay mình hướng dẫn nốt cách cài phần mềm VM VirtualBox trên Ubuntu để các bạn có thêm sự lựa chọn, ok – giờ thì tiến hành thôi 🙂
I. Hướng dẫn cài đặt VirtualBox trên Ubuntu
Cách #1: Cài VirtualBox trên Ubuntu bằng Software Installer
Tương tự như trên Windows 10, có vài phần mềm trên Linux cho phép bạn có thể cài đặt chỉ với vài cú click chuột mà không cần động đến dòng lệnh nào, và may mắn là VirtualBox của chúng ta nằm trong số đó.
+ Bước 1: Trước hết, bạn cần truy cập vào trang Wiki của VirtualBox để tải về bộ cài của phần mềm này, phiên bản mới nhất tính tới thời điểm mình viết bài này là 6.1.18.
+ Bước 2: Bạn sẽ thấy giao diện của trang Wiki tương tự như hình bên dưới, có thể thấy VirtualBox hỗ trợ hầu hết các hệ điều hành phổ biến của máy tính.
Vì chúng ta đang muốn cài trên Ubuntu => nên bạn hãy click vào dòng Linux distributions để tiếp tục.
+ Bước 3: Sau đó bạn sẽ thấy danh sách các bản phân phối Linux có thể sử dụng được VirtualBox, phiên bản mới nhất của Ubuntu là Ubuntu 20.04 cũng đã được hỗ trợ luôn rồi.
Giờ hãy chọn hệ điều hành phù hợp với máy tính của bạn, ở bài viết này chúng ta sẽ chọn dòng Ubuntu 19.10 / 20.4 nhé.
Ngay lập tức một file cài đặt có đuôi .deb với dung lượng tầm 90MB sẽ được tải xuống máy tính. Tải xong bạn hãy click vào file này để bắt đầu quá trình cài đặt VirtualBox sử dụng Software Installer của Ubuntu.
+ Bước 4: Một cửa sổ virtualbox-6.1 hiện lên cung cấp cho một vài thông tin về ứng dụng VirtualBox mà bạn sắp sửa cài đặt, tất nhiên là chúng ta chẳng đọc bao giờ.
Giờ hãy bấm vào nút Install màu xanh như trong hình để cài đặt VirtualBox vào Ubuntu. Cách này na ná giống việc bạn Search và cài đặt một ứng dụng từ cửa hàng ứng dụng của macOS và Linux, rất dễ và ai cũng có thể làm được.
+ Bước 5: Tiếp theo, bạn cần nhập mật khẩu tài khoản Admin của mình để xác nhận việc cài đặt ứng dụng này, bao giờ cũng vậy.
Việc hỏi mật khẩu root nhằm giúp bảo vệ hệ điều hành khỏi những phần mềm độc hại, những phần mềm muốn có quyền can thiệp sâu vào hệ điều hành và nó gần giống với User Account Control của Windows, chỉ khác là không thể tắt được như UAC mà thôi.
Quá trình cài đặt sẽ diễn ra trong khoảng 30S sau đó và nếu không có lỗi gì xảy ra thì bạn đã cài thành công ứng dụng VirtualBox trên Ubuntu và nút Install lúc đầu sẽ chuyển thành nút Remove màu đỏ.
Giờ hãy bấm phím Windows trên bàn phím => gõ từ khóa Virtualbox vào ô tìm kiếm và bạn sẽ thấy duy nhất một kết quả là Oracle VM VirtualBox như hình bên dưới => Click vào đó để chạy ứng dụng VirtualBox.
+ Bước 6: Giao diện của VirtualBox trên Ubuntu và macOS sẽ hơi khác với giao diện của VirtualBox trên Windows 10 một chút, nhưng về cơ bản thì cách sử dụng thì không có gì khác biệt, nhưng mình vẫn sẽ có bài hướng dẫn chi tiết cách sử dụng sau.
Okay, vậy là việc cài đặt VirtualBox trên Ubuntu đã xong !
Cách #2: Cài đặt VirtualBox trên Ubuntu bằng Terminal
Terminal là một trong những công cụ cực kỳ mạnh mẽ trên các hệ điều hành nhân Linux và tạo sự khác biệt giữa Windows và macOS.
Bạn hoàn toàn có thể cài đặt bất cứ phần mềm nào chỉ với một vài dòng lệnh trong Terminal, mình sẽ lấy luôn ví dụ là ứng dụng Oracle VM VirtualBox này nhé !
Khi muốn cài bất cứ phần mềm nào trên Ubuntu nói riêng và Linux nói chung, việc cần làm đầu tiên phải là cập nhật hệ thống.
+ Bước 1: Bạn hãy bấm tổ hợp phím Ctrl + Alt + T trên bàn phím để mở cửa sổ Terminal lên => rồi cập nhật hệ thống Ubuntu bằng dòng lệnh sau:
Ngoài ra, lệnh này còn tự động loại bỏ những gói phần mềm bổ sung của các ứng dụng đã bị gỡ ra trước đó, giúp tối ưu và tăng tốc hệ thống.
Sau khi hệ thống đã được cập nhật xong, chúng ta cần phải cài đặt gói Linux Ubuntu Headers, nó chứa các tập tin Header và các tập lệnh để xây dựng module cho nhân Linux, và đây cũng cũng là một thành phần cần thiết để có thể sử dụng được Virtual Box trên Ubuntu.
+ Bước 2: Nhập vào cửa sổ Terminal dòng lệnh dưới đây => rồi bấm Enter để cài gói Linux Ubuntu Headers, thời gian cài đặt cũng khá lâu nên bạn cần kiên nhẫn chờ đợi nha:
sudo apt-get -y install gcc make linux-headers-$(uname -r) dkms
+ Bước 3: Các ứng dụng muốn cài được bằng dòng lệnh thì phải có kho lưu trữ gọi là các Repository trên Internet, đa số các ứng dụng phổ biến đều đã nằm trong kho lưu trữ mặc định của Linux hoặc kho lưu trữ của chúng đã được thêm vào Linux rồi, chỉ cần gõ lệnh nữa là xong.
Danh sách các kho lưu trữ của ứng dụng được lưu vào một tập tin có tên sources.list nằm trong thư mục etc/apt/ trên ổ đĩa hệ thống.
Đối với những ứng dụng còn lại, muốn cài được bằng dòng lệnh thì bạn phải thêm kho lưu trữ theo cách thủ công giống như cách mình làm với ứng dụng Virtual Box dưới đây.
Trước hết hãy tải về các khóa của Virtual Box về máy để có được quyền truy cập vào kho lưu trữ của ứng dụng này.
+ Bước 5: Giờ hãy cập nhật lại hệ thống một lần nữa để Ubuntu lấy dữ liệu các gói cài đặt từ kho lưu trữ của Virtual Box mà bạn vừa đã thêm vào ở trên, lệnh sau:
sudo apt-get update
Cuối cùng nhập vào Terminal lệnh bên dưới =>rồi bấm phím Enter và chờ cho Virtual Box được cài đặt vào Ubuntu.
sudo apt-get install virtualbox-6.1 -y
Lưu ý về phiên bản của VirtualBox khi cài đặt, ở đây phiên bản mới nhất của VirtualBox đang là 6.1.18 nên mình nhập virtualbox-6.1, bạn cần lưu ý để chọn đúng phiên bản muốn cài đặt.
Và đây là thành quả, nói chung là 2 cách cài này đều cài như nhau, bạn thích dùng cách nào thì tùy 😀
II. Làm thế nào để gỡ cài đặt VirtualBox trên Ubuntu?
Khi Virtual Box không đáp ứng được yêu cầu công việc của bạn, hoặc đơn giản là bạn thích dùng VMWare Workstation hơn thì việc gỡ cài đặt ứng dụng này cũng không có gì phức tạp.
+ Đối với những người cài bằng cách dùng Software Installer, bạn hãy chạy lại file .deb đã dùng lúc cài đặt lên, nếu lỡ xóa rồi thì vào trang download của VirtualBox để tải lại.
Cửa sổ Software Installer quen thuộc lại hiện lên nhưng thay vì nút Install như lúc đầu, bạn sẽ thấy một nút Remove màu đỏ => Bấm vào đó rồi nhập mật khẩu root để gỡ VirtualBox.
+ Còn với những người cài bằng Terminal, việc gỡ cài đặt thậm chí còn đơn giản hơn nữa. Chỉ việc mở Terminal lên => rồi gõ dòng lệnh sudo apt-get remove virtualbox* --purge -y và bấm phím Enter là xong rồi.
Khác với Windows, bạn có thể cài nhiều phiên bản của cùng một ứng dụng mà không gặp bất cứ lỗi gì.
Vậy nên nếu có lỡ cài nhầm và nhiều phiên bản khác của Virtual Box, hãy sử dụng lệnh tương tự dưới đây để gỡ từng phiên bản còn lệnh bên trên sẽ gỡ toàn bộ phiên bản đã cài :
sudo apt-get remove virtualbox virtualbox-5.0
sudo apt-get remove virtualbox virtualbox-5.2
sudo apt-get remove virtualbox virtualbox-6.1
………….
III. Lời kết
Vâng, như vậy là mình đã hướng dẫn xong cho các bạn 2 cách cài đặt ứng dụng VirtualBox trên Ubuntu rồi nhé. Hi vọng là qua bài viết này bạn sẽ có thêm những lựa chọn để tạo và sử dụng máy tính ảo trên Ubuntu, phục vụ cho công việc của bạn được tốt hơn.
Ở trong bài tiếp theo thì mình sẽ nói về cách tạo máy ảo bằng VirtualBox trên Ubuntu vì nó sẽ hơi khác Windows một tí, mời bạn cùng theo dõi nha. Nếu thấy thủ thuật này hay và có ích đừng quên chia sẻ để ủng hộ Blog. Chúc các bạn thành công !
Như bài 1 đã có giới thiệu, định dạng data trong API thường dùng 2 loại chính là JSON (JavaScript Object Notation) và XML (Extensible Markup Language). Hôm nay, mình sẽ nói kỹ hơn về từng loại định dạng.
Ngày nay, JSON được sử dụng nhiều trong Restful API. Nó được xây dựng từ Javascript, ngôn ngữ mà được dùng nhiều, tương thích với cả front-end và back-end của cả web app và web service. JSON là 1 định dạng đơn giản với 2 thành phần: keys và values.
– Key thể hiện thuộc tính của Object
– Value thể hiện giá trị của từng Key
Ví dụ:
Trong ví dụ trên, keys nằm bên trái, values nằm bên phải.
Có nhiều trường hợp, 1 Key sẽ có Value là 1 dãy key + value. Ví dụ như hình:
Trong hình trên Key có tên là Data có Value là 2 cặp Key + value.
XML
Trong JSON dùng { } và [ ] để dánh dấu dữ liệu. XML thì tương tự như HMTL, dùng thẻ để đánh dấu và được gọi là nodes.
Lấy luôn ví dụ ở trên nhưng viết bằng xml, nó sẽ như thế này:
Định dạng dữ liệu được sử dụng như thế nào trong HTTP.
Quay lại bài 2, phần header có chức năng lưu những thông tin mà người dùng không biết, trong đó có 1 thành phần xác định format của data: Content-Type
Khi client gửi Content-Type trong header của request, nó đang nói với server rằng dữ liệu trong phần body của request là được định dạng theo kiểu đó. Khi client muốn gửi JSON nó sẽ đặt Content-Type là “application/json”. Khi bắt đầu nhận request, server sẽ check cái Content-Type đầu tiên và như thế nó biết cách đọc dữ liệu trong body. Ngược lại, khi server gửi lại client 1 response, nó cũng gửi lại Content-Type để cho client biết cách đọc body của response.
Đôi khi client chỉ đọc được 1 loại định dạng, ví dụ là JSON mà server lại trả về XML thì client sẽ bị lỗi. Do đó, 1 thành phần khác ở trong header là Accept sẽ giúp client xử lý vấn đề trên bằng cách nói luôn với server loại nó có thể đọc được. Ví dụ : Accept : “application/json” . Chốt lại: dựa vào 2 thành phần Content-Type và Accept, client và server có thể hiểu và làm việc một cách chính xác. Ở những bài sử dụng công cụ, mình sẽ chụp ảnh và minh họa rõ ràng Content-Type và Accept trong Header. Chọn ảnh tiêu biểu
Trong môi trường Developement mà thao tác với dữ liệu thực là chết người. Nên khi có 1 ứng dụng cần sử dụng API để xử lý hàng nghìn dữ liệu thì việc thực hiện test rất cần thiết và nếu tạo bằng tay data để test thì không ai rãnh vì thế tạo dữ liệu ảo là rất cần thiết.
Vậy việc đầu tiên sau khi có Database test thì chúng ta sẽ “Dummy Data” hay tạo dữ liệu ảo và tên gọi của việc này là Database seeding.
name;// Tạo dữ liệu ảo Địa chỉ
echo $faker->address;// Tạo dữ liệu ảo Đoạn văn
echo $faker->text;// Nếu muốn tạo nhiều dữ liệu mẫu cùng lúc thì để trong vòng For nhé
for ($i=0; $i < 10; $i++) {
echo $faker->name, "\n";
}
Trợ lý giọng nói (voice assistant) và tìm kiếm bằng giọng nói (voice search) đang dần trở thành một xu hướng mới. Trong số những trợ lý ảo phổ biến nhất hiện nay thì Google Assistant là cái tên bạn không nên bỏ qua. Hiện tại, Google Assistant đang hỗ trợ hơn 19 ngôn ngữ, trên hơn 80 quốc gia và có mặt trên nhiều loại thiết bị khác nhau như các thiết bị Smart Display, các dòng điện thoại Android, Iphone, Google Home, loa thông minh,…
Bạn có thể tận dụng Google Assistant để tiếp cận lượng người dùng lớn của trợ lý ảo này bằng cách tạo ra các action thông qua nền tảng Actions on Google. Bài viết sẽ hướng dẫn chi tiết về nền tảng này thông qua topic “Multiple ways to build for Google Assistant” của Ms. Jessica Dene Earley-Cha – Developer Advocate @Google tại sự kiện Vietnam Web Summit 2020 LIVE – biên tập bởi TopDev.
Bài viết sẽ phù hợp nếu bạn:
Là một Web Content Owner, hãy tìm hiểu về Content Actions: các loại công thức, những nội dung hướng dẫn, podcast, tin tức,…
Là một Android App Developer, hãy tìm hiểu về App Actions.
Là một người thích tìm hiểu về công nghệ giao tiếp bằng giọng nói, hãy tìm hiểu về Conversational Actions với Interactive Canvas.
Là một Hardware Developer, hãy tìm hiểu về Smart Home SDK.
Google Assistant hỗ trợ người dùng, khiến đời sống và công việc trở nên hiệu quả hơn
Content Action – Khiến nội dung của bạn xuất hiện trên Google Assistant
Nếu bạn đã có một website, một podcast hay một danh sách công thức thì bạn có thể đưa những nội dung trên của bạn vào trong Google Assistant.
Tương tự như việc tối ưu website cho Google Search, bạn cần cấu trúc website nhằm giúp Google Search có thể hiểu được dữ liệu đó và có thể mang chúng đến và phục vụ cho user thì với Google Assistant điều này cũng diễn ra tương tự.
Tất cả những việc bạn cần làm là thêm vào scheme.org và cấu trúc các trang làm sao để Google Assistant có thể nhận biết, hiểu và mang những nội dung của bạn đến người dùng.
Các loại nội dung trên Google Assistant
Thường những nội dung trong Google Assistant được phân loại thành: news; podcast; recipes; FAQs; how-to.
Việc bạn cần làm là hãy thêm vào Structured Data Markup (Đánh dấu dữ liệu có cấu trúc) để content của bạn có thể được tìm thấy bởi nhiều công cụ tìm kiếm hơn, trong đó có cả Google Assistant.
Đây là một ví dụ về một quyển sách nói. Họ chỉ sử dụng RSS, đồng thời có thể thêm vào cấu trúc dữ liệu ấy, từ đó mà có thể được tối ưu hoạt động trên cả Search và Assistant.
App Action – Kích hoạt chức năng giọng nói cho các ứng dụng Android
App Action là một hành động giúp bạn gửi một ứng dụng Android đến người dùng thông qua câu thoại lệnh “Hey Google, open *your app*”.
Thách thức “Tái tương tác ứng dụng”
Thông thường doanh nghiệp sẽ phải tốn rất nhiều chi phí để có thể khiến người dùng cài đặt app cũng như khiến họ tương tác, sử dụng ứng dụng một cách thường xuyên. Một trong số những lý do tại sao khiến chi phí này lại trở nên đáng kể là bởi trên thiết bị người dùng thường có hàng tá icon khiến ứng dụng của bạn bị lu mờ (mình biết điều này vì trên điện thoại mình cũng thế :P)
Bởi thế, việc mà chúng ta cần làm chính là tìm cách xem rằng làm thế nào để vượt qua những rào cản trên, giúp người dùng không cần phải nhớ xem app của bạn trông như thế nào và họ có thể dễ dàng tìm thấy nó.
Với App Action, công nghệ này cho phép Android Developer sử dụng Google Assistant như một công cụ để mang ứng dụng của bạn nhanh chóng đến người dùng thông qua giọng nói.
Để hiểu rõ hơn, chúng ta cùng xem một ví dụ được gọi là SmoresSmores.
Hoàn tất ‘chiếc bánh’ S’more theo cách cũ
Bước 1: Tìm ứng dụng SmoreSmores bên trong mớ icon hỗn độn trên thiết bị.
Bước 2: Chọn loại bánh quy.
Bước 3: Chọn loại marshmallow.
Bước 4: Chọn loại chocolate.
Bước 5: Xác định độ nóng giòn.
Bước 6: Xác định số lượng.
Bước 7: Xem lại order.
Bước 8: Hoàn tất order.
Bạn có thể hình dung một số bước có thể diễn ra để hoàn tất một chiếc bánh S’more trên ứng dụng theo cách thông thường:
Đầu tiên, người dùng sẽ phải tìm kiếm icon của ứng dụng trong số mớ icon hỗn độn trong thiết bị của họ. Sau đó, họ sẽ phải lựa chọn từng option cho mình như: loại bánh quy nào , loại marshmallow nào, có thêm chocolate hay không, độ chính và số lượng bánh,…
Người dùng sẽ phải trải qua nhiều bước mới có thể hoàn tất order của mình. Điều này sẽ mất khá nhiều thời gian. Tuy nhiên, có một phương pháp khác để hoàn tất order và tiết kiệm công sức hơn cho người dùng, đó là triển khai thực hiện App Action. Người dùng sẽ có thể nói chuyện và thực hiện order một cách tự nhiên nhất thông qua chính cách nói chuyện của họ.
Hoàn tất S’more bằng giọng nói với App Actions
Với giải pháp này, người dùng chỉ cần thực hiện 2 bước đơn giản.
Bước 1: Thao tác bằng giọng
Bước 2: Xác nhận order
Theo cách này, tất cả thông tin sẽ được đưa vào ứng dụng và ứng dụng mở lên trang xác nhận thông tin mà người dùng đã cung cấp. Điều này sẽ khiến cho người dùng dễ dàng thao tác hơn.
Với App Actions, tất cả thông tin sẽ được đưa vào ứng dụng và ứng dụng sẽ mở lên đến ngay trang xác nhận thông tin mà người dùng đã cung cấp bằng chính giọng nói của họ (loại bánh, marshmallow, chocolate, độ nóng và số lượng,…)
Một ví dụ khác, hãy tham khảo trường hợp của Nike
Bạn có thể nói chuyện với ứng dụng của Nike, yêu cầu mở ứng dụng Nike Run Club.
Ngoài ra bạn còn có thể thực hiện những thao tác khác như chuyển tiền hay đặt hàng thông qua ứng dụng.
Tất cả những tác vụ như trên có thể được thực hiện là bởi Google Assistant sử dụng một thứ được gọi là built-in intents.
Built-in intents
Hãy để Google biết rằng bạn có những tính năng nào, chẳng hạn như tính năng đặt đồ ăn hay bắt đầu một buổi tập luyện, từ đó giúp Google có thể biết được đâu là nơi mà nó cần kết nối đến.
Hãy cho Google biết đâu là những tính năng mà người dùng có thể sử dụng; các loại parameter mà bạn cung cấp, đâu là các fulfillment URL, đâu là trang đích khi tôi tổng hợp tất cả các parameter đó lại.
Bạn có thể sử dụng bao nhiêu built-in intent và thêm vào bao nhiêu mà bạn muốn.
Các intent google assistant đang hỗ trợ
Và đây là một file XML để ví dụ cho việc sử dụng built-in intent cho hành động gọi món.
Một file XML để ví dụ cho việc sử dụng built-in intent cho hành động gọi món
Bạn có thể thấy trên ví dụ sử dụng:
intentName=“actions.intent.ORDER_MENU_ITEM”
Đây là một built-in intent mà Google đã cung cấp và bạn có thể sử dụng nó và người dùng sẽ có thể gọi món từ menu.
Android Slices
Slices là các UI template có thể hiển thị các nội dung tương tác động từ ứng dụng của bạn bên trong Google Assistant.
Hẳn rằng nhiều lần bạn muốn cung cấp cho người dùng một thông báo nhanh hay một xác nhận đơn hàng nào đó. Bạn có thể sử dụng Android Slice để cung cấp những thông tin ấy một cách đơn giản và thân thiện đến người dùng và giúp họ cập nhật thông tin một cách nhanh chóng.
Một lưu ý nhỏ: khi bắt đầu với các built-in intent và item này, Google Assistant đã kiểm soát và quản lý tất cả NLU (Natural Language Understanding) nên bạn không cần phải lo về cách mà người dùng nói chuyện khi bạn sử dụng built-in intent trong Android Slices.
Smart home – Control hardware with just your voice
Smart Home Device Integration
Khi nhắc về Smart Home, tất cả những gì chúng ta cần nói đến đều chỉ là về tích hợp với phần cứng (integrating with hardware).
Nếu bạn quan tâm đến việc tự động hóa ngôi nhà của mình, bạn có thể hiện thực hóa điều ấy thông qua giải pháp Smart Home Integration của Google.
Giải pháp này sẽ cung cấp cho bạn một Home Graph, giúp ngôi nhà nắm được trạng thái và khả năng thực hiện của tất cả các thiết bị kết nối bên trong ngôi nhà. Người dùng có thể đưa ra yêu cầu như “làm mờ đèn một chút” và ngôi nhà sẽ biết rằng chúng cần làm gì.
NLU
Phần hay nhất nội dung này chính là Natural Language Understanding. Giống với Android và built-in intents, Google Assistant sẽ kiểm soát tất cả vấn đề này cho bạn. Nếu ai đó nói “làm ấm phòng” hay “bật/tắt đèn” hay nói về một nhiệt độ cụ thể như “tôi muốn 24 độ”, nó sẽ chuyển dữ liệu đó như một parameter và thực hiện yêu cầu đó.
Types & traits
Type thực chất là những thiết bị, phần cứng (hardware). Bạn có thể nghĩ đến những thứ như cửa sổ, đèn, lò sưởi, rèm che,…
Trait là thuộc tính hay đặc điểm, chức năng của type. Bạn có thể hình dung về những yêu cầu đơn giản như bật/tắt đèn, nhưng cũng có một số loại đèn có thể điều chỉnh độ sáng.
Google có đề cập rất nhiều về nội dung này trong tài liệu, bạn có thể kiểm tra chúng tại đây.
Smart Home Integration
Để có thể tích hợp thiết bị của bạn với Google Assistant, nhà phát triển cần cung cấp cloud service của chính họ cho người dùng để đăng ký và quản lý thiết bị, điện thoại thông minh và Action Platform. Sau đó, hãy tích hợp với cloud service thông qua một chuỗi các webhook để truy cập và kiểm soát các thiết bị đó.
Một thứ mà chúng tôi tự hào khi bắt đầu nó từ năm ngoái chính là local SDK. Local home SDK của Google được thực hiện thông qua wifi. Các thiết bị trên local network có thể được phát hiện và kiểm soát với local protocols, giúp giảm chi phí giao dịch đám mây.
Conversation
Cuối cùng, nếu bạn muốn xây dựng một trải nghiệm phong phú cho người dùng, bạn nên xây dựng các conversation action.
Tương tự như cách smart home được cấu trúc và những thứ tương tự, người dùng sẽ gửi một request và Google Assistant tiến hành xử lý.
Tuy nhiên, không giống như với Android hay với Smart Home – chúng đã có các built-in intents hay types of traits, nơi mọi thứ đã được định nghĩa và Google đã xử lý phần natural language processing, tại đây, Google Assistant sẽ xử lý phần text-to-speech và cho bạn text của người dùng. Sau đó, tùy vào action mà bạn muốn thực hiện mà hệ thống sẽ xác định đâu là thứ cần gửi lại, nhưng việc xác định đâu là thứ mà cần hệ thống gửi lại bạn vẫn là người quyết định. Hãy gửi một response tới Google Assistant và Google Assistant sẽ chuyển nó đến cho người dùng.
Natural Language Understanding (NLU)
Giả sử, nếu người dùng chỉ nói “yes” thì việc phân tích cú pháp sẽ tiến hành khá nhanh phải không?
Tuy nhiên, nếu người dùng nói “yes” theo nhiều cách (biến/variation) khác nhau như: “Perfect”, “Let’s do it”, “Go ahead”, “Sure”, “Yep”, “oke”,… thì bây giờ phải làm sao?
Lúc này, bạn sẽ cần một công cụ để quản lý tất cả các biến và đó chính là NLU.
Để giúp xử lý thông tin của người dùng, bạn hãy xác định các intent khác nhau mà người dùng có thể sẽ thể hiện một “yes” intent hay “no” intent, hay khi người dùng muốn chơi một trò chơi nào đó.
Khi bạn đã xác định những thứ trên thì hãy đưa ra những hướng dẫn (training phrases) cho các intent ấy, từ đó Google Assistant có thể hiểu được và biết được rằng khi người dùng nói về các intent đó, như “yes” chẳng hạn, Google sẽ đóng gói tất các các biến của “yes” và tiếp tục xây dựng thêm dựa trên đó.
Nhưng ai sẽ làm việc đó?
Cuộc đối thoại hai chiều với action của bạn được thực hiện theo cách sau.
Input của người dùng sẽ được gửi đến, và sau đó, Google Assistant sẽ tiến hành xử lý nó bằng NLU và gửi thông tin đó đến Action.
Tiếp đến, action sử dụng chúng để tạo ra một prompt và gửi prompt ấy đến Google Assistant. Cuối cùng, Google Assistant sẽ chuyển nó đến người dùng.
Nhưng làm sao để bạn biết được khi nào người dùng nói “yes”? Họ sẽ nói “yes” ở vị trí nào trong câu thoại? Có phải họ đang nói “yes, I want to play the game” hay “yes, I want to drink the magic potion in the game”. Bạn không thể xây dựng các intent kiểu như “fly around”. Bạn cần phải tổ chức nó theo một cách nào đó.
Quản lý mô hình hội thoại (conversational model) cho action chính là chìa khóa để tạo ra các prompt phù hợp ấy và bạn có thể sử dụng Actions Builder để thực hiện điều này.
Actions Builder
Actions Builder là một web-based IDE được tích hợp hoàn toàn vào Action Console.
Hiện tại bạn chỉ có duy nhất một window để xây dựng các action. Bạn có thể thiết kế các luồng hội thoại một cách trực quan và đồng thời hỗ trợ và cải tiến NLU khi nó tiến hành xử lý các input của người dùng. Đồng thời, nó cũng bao gồm một inline editor vì thế bạn cũng có thể xây dựng các phản hồi động bằng cách sử dụng webhook.
Với Actions Builder, nó sẽ xử lý input của người dùng và cũng làm như thế với intent matching hoặc slot filling khi nó đến các types. Bạn cũng có thể lấy các phần dữ liệu ra khi nó đến users responses.
Tương tự, chúng ta có khái niệm scenes để quản lý logic. Nếu nó thực sự bắt đầu và người dùng nói “yes”, intent sẽ khớp với “Yes” intent, nhưng bởi vì nó đang ở start scene, hệ thống biết “oh, họ đang nói yes để bắt đầu trò chơi chứ không phải nói yes để sử dụng item hồi máu”. Từ đấy, nó có thể chuyển tiếp bạn tới scene kế tiếp để bắt đầu trò chơi, và bạn cũng có thể xây dựng các prompt động hoặc tĩnh bằng cách sử dụng công cụ này.
Scenes
Scene là một khái niệm mới khi “Action Developer” sử dụng công cụ cũ của Google.
Scenes là những khối logic (logical chunks) của Mô hình thoại hội của bạn và chúng cũng là thứ sẽ thực thi các action của bạn. Chúng sẽ xử lý những phần nặng nhọc, xử lý các logic cần thiết để định hướng cuộc trò chuyện. Đây là một cách thức để modularize action của bạn.
Bạn có thể nhận được một welcome scene, và ở scene này, bạn sẽ được hỏi rằng liệu bạn có muốn làm cái này, làm cái kia hay không. Chúng có mối liên hệ chặt chẽ với nhau.
Nếu một người dùng nói gì đó, hệ thống có thể sẽ chuyển tiếp họ đến scene tiếp theo. Điều thú vị là scene của bạn có thể hỏi và thu thập dữ liệu thông qua các slot. Nếu socket của bạn được fill, một scene có thể chuyển tiếp đến scene tiếp theo. Tương tự, bạn có thể liên tưởng đến việc order, bạn có thể lấp đầy hoặc chúng ta có thể có tất cả thông tin như mua một chiếc đầm dạ hội, chúng ta sẽ biết được họ muốn mua size nào, sau đó chuyển tiếp bạn đến nơi có thể mua hàng hoặc nơi cung cấp thêm thông tin.
Vòng đời thực thi của scene
Các scene rất linh hoạt, chúng cho phép bạn thực hiện những thứ rất tuyệt và tất cả các scene chạy bên trong vòng lặp, mọi thứ điều tuyệt vời, thế nên, vòng đời thực thi (executor lifecycle) là một thứ gì đó mà chúng ta nên thử phân tích cú pháp và đào sâu thêm vào nội dung này. Hãy xem video ngắn bên dưới.
Action SDK
Bổ sung thêm cho Actions Builder là Actions SDK. Action SDK sẽ cho bạn một file-based representation của action và khả năng sử dụng các local IDE của chính bạn. Phần hay nhất là nó hoạt động song song với Actions Builder. Nó bao gồm tất cả các tài nguyên cấu hình của action của bạn, gồm có khả năng hỗ trợ và quốc tế hóa.
Bạn có thể build bằng cách sử dụng Actions Builder, sau đó kéo dự án xuống local, thay đổi và đẩy những thay đổi mới ấy lên Action Builder và cuối cùng là cho phép team làm việc với công cụ yêu thích của họ.
Google cũng đã cải thiện phần giả lập – thứ cung cấp cho bạn các execution log cùng với các dữ liệu về request và response để giúp bạn dễ dàng debug các action của mình. Chúng được tổ chức theo scene, theo sự tương tác giữa người dùng và Google Assistant.
Sau đó, bạn có thể click lên nó và bạn có thể nhìn thấy dữ liệu của scene.
Bạn có thể thấy những parameter nào đang được chuyển qua khi các webhook đang bị gọi.
Google cũng đã thêm state editor để khiến nó dễ dàng để update và debug content của bạn.
Thông qua việc tạo ra các thay đổi trong scene, trong phần scene editor, bạn không nhất thiết phải trải qua tất cả flow, bạn có thể kết nối tới các thứ dễ dàng hơn hoặc khiến nó như kiểu người dùng đã hoàn thành việc xyz và theo cách đó, bạn có thể đi trực tiếp đến nơi mà bạn muốn test bên trong luồng hội thoại.
Gia tăng trải nghiệm cho người dùng
Action Builder và Action SDK cung cấp các thành phần hội thoại (conversational components) của action nhưng bạn cũng có thể thêm vào những thành tố (element) khác để gia tăng trải nghiệm cho người dùng.
Bạn có thể sử dụng Interactive Canvas để build những trải nghiệm game thú vị, cung cấp những hình ảnh trực quan sinh động cho cuộc trò chuyện của bạn.
Một điều rất tuyệt chính là Interactive Canvas chỉ sử dụng như web app, vì thế HTML, CSS và JavaScript là tất cả những gì bạn cần để có thể tạo ra một web app ấy. Interactive Canvas sẽ kết nối trang web với cuộc hội thoại của người dùng và sẵn sàng để thực hiện trải nghiệm giọng nói khi bắt đầu action này.
Bạn có thể tìm hiểu chi tiết về công nghệ Interactive Canvas tại bài viết này.
Continuous Match Mode
Một điều khác là bạn có thể gia tăng thêm trải nghiệm người dùng với chế độ khớp liên tục (Continuous Match Mode). Thứ công nghệ này cho phép người dùng có thể nói nhiều từ hoặc cụm từ khác nhau nhất quán.
Một ví dụ điển hình là khi bạn muốn có một bản đồ quốc gia với tất cả các vùng, hoặc bản đồ thế giới có tất cả các quốc gia bên trong bản đồ đó, việc bạn cần làm là hiển thị chúng thông qua interactive canvas. Nhờ đó, bạn có thể trực quan hóa chúng và cho phép người dùng nói tên các quốc gia và thậm chí nói một cách nhanh chóng. Người dùng sẽ chỉ cần gọi tên từng quốc gia liên tục và chúng – hình ảnh các quốc gia sẽ được hiển thị liên tục thông qua công nghệ Continuous Match Mode.
Continuous Match Mode Developer
Ở góc độ của một lập trình viên, tất cả việc bạn cần làm là cung cấp cấu hình bao gồm danh sách các cụm từ chính xác mà người dùng có thể sử dụng / nói.
Trở lại ví dụ về bản đồ thế giới với khả năng hiển thị tất cả các quốc gia ở trên, bạn sẽ cần liệt kê ra tất cả các quốc gia và cung cấp thông tin đó đến action. Lúc này, interactive canvas web app có thể tiến hành xử lý chúng một cách nhanh chóng cho người dùng.
Bài viết được trích dẫn từ phần trình bày của Jessica Dene Earley-Cha tại sự kiện Vietnam Web Summit 2020 LIVE do TopDev tổ chức
Bài viết được sự cho phép của tác giả Lê Xuân Quỳnh
Qua 5 bài trước, chúng ta đã hiểu các khái niệm cơ bản về RxSwift. Và bây giờ, chúng ta sẽ bắt đầu làm quen với Observables, khái niệm quan trọng của RxSwift. Dịch sát nghĩa là có thể quan sát được. Ở Rx, nó là 1 lớp cho phép bạn quan sát bất cứ thứ gì mà có sự thay đổi về trạng thái, như trạng thái bấm nút, ô text,..
Sau gõ mở terminal, cd vào thư mục source code và gõ:
pod install
để cài đặt RxSwift cho project.
Vậy 1 observable là gì?
observable là trái tim của RxSwift. Chúng ta sẽ giành thời gian để nghiên cứu, thử nghiệm với nó.
Bạn sẽ nghe các khái niệm observable, observable sequence hay sequence trong Rx. Thậm chí bạn sẽ nghe các khái niệm stream từ các developer khác bằng các khái niệm ở môi trường phát triển khác tương tự. Tuy nhiên ở Rx nó không phải là stream, mà mọi thứ đều là sequence.
Nghĩa là trong Rx mọi thứ đều là luồng quan sát – dịch sát nghĩa là đối tượng quan sát sự kiện có thứ tự.
stream: sự kiện phát liên tục
sequence: sự kiện phát không đồng bộ, tùy ý
Sự kiện luồng phát 3 sự kiện 1, 2, 3 trong thời gian thực
Mũi tên từ trái sang phải mô tả thời gian, vòng tròn đánh số thể hiện sự kiện phát ra. Đầu tiên sự kiện 1 phát ra, tiếp theo lần lượt sự kiện 2, và cuối cùng là 3. Vậy lúc nào thì phát ra sự kiện? Nó có thể bất kỳ lúc nào ở trong vòng đời của 1 observable.
Vòng đời của 1 obserbvable
Sự kiện bấm vào nút
Những thứ có thể quan sát được và phát ra các sự kiện như nút bấm, được thể hiện bằng sơ đồ trên. Nút được phát ra 3 lần bấm, và kết thúc.
Tuy nhiên đôi khi sự kết thúc có thể trước 3 lần, do trục trặc lỗi.
Sự kiến phát ra có lỗi thể hiện màu đỏ
Khi 1 sự kiện lỗi phát ra, nó kết thúc luôn vòng đời của observable.
Một observable có thể quan sát và phát ra các sự kiện tiếp theo cho đến khi tạo ra một sự kiện lỗi và kết thúc
Hoặc phát ra 1 sự kiện hoàn thành và kết thúc
Một khi observable kết thúc, nó sẽ không thể phát ra thêm sự kiện nào nữa
Ví dụ trực tiếp từ mã nguồn RxSwift:
/// Represents a sequence event.
///
/// Sequence grammar:
/// **next\* (error | completed)**
public enum Event<Element> {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
Như ta thấy ở đây:
Sự kiện Next chứa instance của Element
Sự kiện error chứa lỗi Swift.Error
Sự kiện completed đơn giản là dừng và không có bất kỳ dữ liệu nào
Tạo observables
Quay lại với project các bạn vừa tạo ở đầu bài, chúng ta thêm đoạn code sau vào file playground:
example(of: "just, of, from") {
// 1
let one = 1
let two = 2
let three = 3
// 2
let observable: Observable<Int> = Observable<Int>.just(one)
}
Ở đoạn code trên:
Tạo 3 biến one, two, three
Tạo 1 observable và phát ra sự kiện one – tức là phát ra số nguyên one
just là hàm phát ra 1 sự kiện, nó là phương thức của observable. Tuy nhiên trong Rx, nó được gọi là toán tử.
Thêm tiếp mã sau vào cuối chương trình:
let observable2 = Observable.of(one, two, three)
Lần này, chúng ta không chỉ định rõ ràng kiểu cho observable, tuy nhiên chúng ta hiểu đây là 1 observable kiểu int, và đây không phải là mảng. Bấm Option + click chuột để xem:
observable tự chuyển về kiểu int
Nếu bạn muốn tạo 1 observable kiểu mảng, chúng ta dùng toán tử of như sau:
let observable3 = Observable.of([one, two, three])
Còn khi dùng toán tử from, chúng ta tạo 1 observable như sau:
let observable4 = Observable.from([one, two, three])
Toán tử from nhận các giá trị từ mảng, và tạo 1 observable riêng rẽ, nó có kiểu int, chứ không phải kiểu mảng [int].
from tạo ra 1 observable kiểu int, từ 1 mảng kiểu int
Subscribing tới 1 observable
Là một nhà phát triển IOS, chắc các bạn đã quen thuộc với NotificationCenter, nó phát ra 1 notification tới observers. Dưới đây là ví dụ về xử lý sự kiện UIKeyboardDidChangeFrame, được xử lý ở hàm closure:
let observer = NotificationCenter.default.addObserver(
forName: .UIKeyboardDidChangeFrame,
object: nil,
queue: nil
) { notification in
// Handle receiving notification
}
Việc đăng ký – subscribing tới 1 observable cũng tương tự. Thay vì dùng hàm addObserver(), thì ta dùng hàm subscribe().
Tuy nhiên việc đăng ký các observable hợp lý hơn. Như đã trình bày, các observable phát ra các sự kiện next, completed và error. Để hiểu hơn, chúng ta thêm ví dụ sau vào cuối chương trình:
example(of: "subscribe") {
let one = 1
let two = 2
let three = 3
let observable = Observable.of(one, two, three)
observable.subscribe { event in
print(event)
}
}
Kết quả:
--- Example of: subscribe ---
next(1)
next(2)
next(3)
completed
Observable phát ra các sự kiện 1, 2, 3 và completed để kết thúc.
Thường chúng ta sẽ quan tâm giá trị phát ra từ các sự kiện đó hơn là chính nó. Chúng ta sửa ví dụ trên sau:
observable.subscribe { event in
if let element = event.element {
print(element)
}
}
Kết quả:
--- Example of: subscribe ---
1
2
3
Sự kiện phát ra chứa giá trị, và là 1 giá trị optional. Vì vấy chúng ta check trước giá trị đó để tránh nil. Các sự kiện không chứa phần tử(ở đây là completed) sẽ không được in ra. Như ta đã biết, các sự kiện onNext đều có phần tử.
Thay thế đoạn code trên như sau:
observable.subscribe(onNext: { element in
print(element)
})
Vậy là bạn đã biết cách tạo các observable có phần tử. Vậy muốn tạo 1 observable rỗng(empty) thì làm thế nào? Xem ví dụ sau:
example(of: "empty") {
let observable = Observable<Void>.empty()
observable
.subscribe(
// 1
onNext: { element in
print(element)
},
// 2
onCompleted: {
print("Completed")
} )
}
Một observable phải được định nghĩa 1 kiểu có thể quan sát được. Trong trường hợp này nó là kiểu void.
Giống như sự kiện onNext bạn xem ở ví dụ trước
sự kiện onCompleted không trả ra phần tử nào mà chỉ đơn giản là phát sự kiện hoàn thành.
--- Example of: empty ---
Completed
Nhưng mục đích của việc sử dụng 1 observable empty là gì? Nó thật sự hữu ích khi bạn muốn trả về 1 sự kiện kết thúc ngay lập tức và không trả về giá trị.
Trái với toán tử empty, toán tử never không emit ra sự kiện nào và không bao giờ kết thúc. Cùng xem ví dụ sau:
example(of: "never") {
let observable = Observable<Any>.never()
observable
.subscribe(
onNext: { element in
print(element)
},
onCompleted: {
print("Completed")
}
) }
Nó không in ra bất cứ thứ gì kể cả Completed, vậy làm sao để biết là nó hoạt động? Hãy giữ vững tinh thần học hỏi cho đến phần Challenges sau này nhé.
Cho đến bây giờ, bạn đang làm việc với các observable có thể quan sát được. Nhưng bạn cũng có thể tạo ra observable từ 1 loạt các giá trị. Cùng xem ví dụ sinh dãy Phibonaci sau:
example(of: "range") {
// 1
let observable = Observable<Int>.range(start: 1, count: 10)
observable
.subscribe(onNext: { i in
// 2
let n = Double(i)
let fibonacci = Int(((pow(1.61803, n) - pow(0.61803, n)) /
2.23606).rounded())
print(fibonacci)
})
}
Tạo 1 observable từ các số có phạm vi từ 1 đến 10
Tính toán và in ra số fibonacci thứ n cho mỗi phần tử được emit ra.
hàm pow(x, y) trả về x^y.
Trên thực tế, có 1 nơi tốt hơn ngoài xử lý onNext, để đặt mã biến đổi emit sự kiện. Chúng ta sẽ tìm hiểu vấn đề này sau ở bài Transforming Operators.
Ngoài trừ ví dụ về never(), cho đến thời điểm này bạn đang làm việc với các observables có thể quan sát tự động phát ra sự kiện completed để kết thúc. Điều này giúp bạn tập trung vào cơ chế đăng ký observables, tuy nhiên lại gạt đi 1 khía cạnh quan trọng trong việc đăng ký đã xử lý như nào. Khi cấp phát thì nghĩa là cần phải giải phóng cho nó. Đã đến lúc chúng ta nghiên cứu việc giải phóng 1 observable như nào trong bài tiếp theo.
Trong bài viết này, mình sẽ hướng dẫn các bạn cài Flutter SDK lên Linux, cụ thể mình sử dụng distro phổ biến nhất của Linux là Ubuntu để hướng dẫn các bạn. Trước khi bắt đầu, do mình đã có video trên Youtube, và trong bài hướng dẫn cho Windows mình cũng đã hướng dẫn khá chi tiết nên trong bài viết này, những thao tác nào giống mình sẽ nói nhanh qua thôi. Con bây giờ thì bắt đầu thôi!
Đầu tiên bạn truy cập trang web flutter.dev, bấm vào Get Started, tiếp theo bạn chọn Linux. Ở trang này, bạn tìm đến mục “Get the Flutter SDK”, sau đó bấm vào nút download Flutter SDK ngay bên dưới mục 1 của mục “Get the Flutter SDK” để tải Flutter SDK về. Các bạn chờ tải về xong và mở file bằng Archive Manager hoặc chọn Open with Archive Manager cũng được.
Sau khi mở file bằng Archive Manager, các bạn chọn Extract.
Tiếp tục, các bạn chọn đường dẫn để giải nén folder “flutter” ra. Mình sẽ chọn giải nén ra thư mục “/home/khiemle/Development”. Sau khi giải nén xong, các bạn mở Terminal (ctrl + alt + T), nhập lệnh “sudo gedit ~/.bashrc” và enter, sau đó nhập mật khẩu. Một cửa sổ editor sẽ hiện lên để bạn chỉnh sửa file .bashrc.
Các bạn thêm đoạn sau vào cuối file .bashrc: export PATH=”$PATH:[PATH_TO_FLUTTER_GIT_DIRECTORY]/flutter/bin”. Các bạn sửa [PATH_TO_FLUTTER_GIT_DIRECTORY] thành đường dẫn chứa folder flutter của bạn. Ví dụ trong trường hợp của mình là: export PATH=”$PATH:/home/khiemle/Development/flutter/bin”.
Mục đích của việc trên là để thêm flutter vào trong $PATH để có thể sử dụng command flutter trong terminal mà không cần mỗi lần mở terminal mới phải export mất thời gian, và cũng để cho các ứng dụng khác ví dụ như VS Code có thể sử dụng được command flutter. Sau khi đã thêm flutter vào $PATH xong, các bạn nhấn Save để lưu lại và tắt nó đi.
Tiếp theo, các bạn tắt các cửa sổ Terminal hiện tại đi, mở một cửa sổ terminal mới, gõ lệnh sau: flutter –version. Nếu như nó hiện thông tin về Flutter SDK, Dart version… thì bạn đã cài thành công SDK. Nếu như máy bạn chưa cài đặt Git, khi bạn chạy lệnh trên, nó sẽ báo là “Unable to find git in your PATH”, lúc này, bạn chỉ cần chạy câu lệnh “sudo apt-get install git -y” là được. Sau khi cài git, bạn có thể chạy câu lệnh flutter bình thường.
Lời kết
Vậy là qua bài viết này, mình đã hướng dẫn các bạn cài đặt Flutter SDK trên Linux (Ubuntu distro). Nếu các bạn có thắc mắc gì có thể để lại bình luận bên dưới video hoặc bài viết này để mình có thể giúp các bạn nha. Bạn nào chưa biết cách cài trên Windows đừng quên tham khảo bài viết này nha. Đừng quên chia sẻ cho mọi người nếu bạn thấy hay! Cảm ơn các bạn đã theo dõi bài viết!
Nếu các bạn thích đọc hướng dẫn và tài liệu bằng tiếng anh và ví dụ cụ thể thì có thể tham khảo các nguồn sau đây mình cũng học và tham khảo trên các trang này:
Để download gói cài đặt các bạn vào trang này để lấy gói phù hợp với máy các bạn: https://golang.org/dl/
Cài đặt Golang trên Windows.
Cài đặt
Các bạn vào trang download của Golang mình đã để link phía trên và tím đến chỗ dowload gói cài đặt cho Microsoft Windows, tiến hành download về thôi.
Cũng như cài đặt các chương trình khác các bạn nếu không muốn điều chỉnh gì thì cứ nhấn Next, next nhé.
Chấp nhận điều khoản, nhấn Next.
Ở bước này các bạn có thể điều chỉnh lại đường dẫn mà chúng ta muốn cài đặt Golang trên máy, sau đó nhấn Next.
Nhấn Install nào hỏi nhiều quá đấy.
Các bạn chờ trong giây lát nhé, 1 xí nữa thôi là xong rồi.
Ok, nhấn Finish để hoàn thành việc cài đặt.
Như vậy chúng ta đã hoàn tất cả các bước cài đặt, bước tiếp theo là setup biến môi trường GOROOT và GOPATH.
Thiết lập biến môi trường.
Các bạn nhấn nút Windows và gõ tìm đến cài đặt biến môi trường như hình bên dưới.
Chọn vào Environment Variables.
Sau đó chọn New và thêm một biến (variable) tên là GOPATH với đường dẫn đến thư mục chúng ta muốn tạo các project Golang, đây là nơi làm việc với code Go của chúng ta.
Và nhìn xuống biến có tên Path để xem đã dẫn tới thư mục bin của Go mà ban đầu chúng ta đã cài đặt chưa nhé.
Mình thấy nó đã có rồi nên là ok, nếu chưa có thì mình sẽ thêm vào.
Sau đó restart lại máy và kiểm tra Go đã được setup đúng chưa.
Kiểm tra version của Go bằng command go version
Kiểm tra cấu hình cài đặt biến môi trường lưu ý 2 biến là GOROOT và GOPATH.
Cài đặt Golang trên Ubuntu.
Truy cập vào máy Ubuntu thực hiện việc update apt.
sudo apt-get update
sudo apt-get -y upgrade
Tải Golang về máy.
Vào thư mục mà chúng ta muốn ở đây mình sẽ bỏ vào thư mục /home/vxphong/go và thư mục này mình đã tạo sẵn trên máy.
Sau khi đã tải được gói cài đặt xuống ta tiến hành giải nén (extract).
Ở đây mình cái nhiều phiên bản nên mình sẽ đổi tên thư mục go vừa extract ra thành số version nha.
mv go 1.12.7
Để gọn trông nhìn sạch hơn mình xóa luôn file tar.gz nha, và đây là thư mục mà mình đã cài đặt Go.
cd 1.12.7
pwd
Chúng ta sẽ nhớ đường dẫn này để tí nữa cài đặt biến môi trường GOROOT nha.
/home/vxphong/go/1.12.7
Tạo ra thư mục làm việc với code Go
mkdir /home/vxphong/gopath
Tiếp theo chúng ta hãy thiết lập GOPATH và GOROOT
nano /home/vxphong/.bashrc
Và export các đường dẫn để thiết lập biến môi trường GOPATH, GOROOT và PATH.
export GOROOT=/home/vxphong/go/1.12.7
export GOPATH=/home/vxphong/gopath
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
Như vậy là đã xong kiểm tra version của Go nào các bạn, chúng ta tắt terminal đang làm việc đi và mở lại.
go version
Kiểm tra các biến môi trường đã thiết lập trước đó.
go env
Hello world trong Golang.
Quay trở lại với máy Windows của mình, các bạn nhớ lúc trước mình đá thiết lập biến môi trường GOPATH không? Trong thư mục GOPATH thì chúng ta tạo thêm thư mục src nữa nha. nơi làm việc để tạo ra các project sẽ nằm ở đó.
mkdir src
cd src
mkdir hello-world
Tạo file main.go trong thư mục hello-world với nội dung sau:
package main
import "fmt"
func main(){
fmt.Println("Hello World")
}
Bây giờ chúng ta tiến hành code và chạy chương trình Hello-World thôi nào
Dùng câu lệnh sau đây để chạy chương trình.
go run main.go
Tóm Lại:
Thư mục làm việc sẽ nằm trong thư mục src nằm ở đường dẫn GOPATH do chúng ta đã cài đặt trước đó. Các bạn có thấy các thư mục như github.com, golang.org không đó là nơi chứa các thư viện mà chúng ta sẽ cài đặt để sử dụng cho project. Có thể những bài tiếp theo mình sẽ hướng dẫn các bạn cái thư viện như thế nào nha, nhưng thật ra nó rất đơn giản thôi à chỉ cần 1 câu lệnh là nó sẽ tự động cài và bỏ vào trong các thư mục tương ứng, chỉ cần import để lấy ra sử dụng thôi.
Bài viết được sự cho phép của tác giả Chung Nguyễn
Hôm nay, nhóm Laravel đã phát hành một phiên bản chính mới của “laravel/installer” bao gồm hỗ trợ khởi động nhanh các dự án Jetstream. Với phiên bản mới này khi bạn chạy laravel new project-name, bạn sẽ nhận được các tùy chọn Jetstream. Ví dụ:
React Props Cheatsheet: 10 Patterns mà bạn nên biết (Phần 2)
Tác giả: Reed Barger
6. Cập nhật giá trị của React prop thông qua thanh trạng thái
Để chuyển một giá trị prop cho một thành phần, trong các thành phần của hàm đó cần cố gắng thay đổi ngay lập tức giá trị của prop.
Giá trị prop phải là giá trị thuần túy. Nói cách khác, chúng không thể bị biến dạng hoặc thay đổi trực tiếp. Trong React nếu chúng ta muốn thay đổi các giá trị theo thời gian, thì phương tiện thích hợp để làm điều đó là với status – trạng thái.
Nếu chúng ta muốn chuyển một giá trị prop vào một thành phần và thay đổi nó sau này, có thể đưa nó vào một hook React trạng thái để lưu trữ giá trị đó dưới dạng một biến. Sau đó, cập nhật bằng cách sử dụng chức năng setter thích hợp. Ví dụ, chúng ta có thể làm như vậy với useStatehook hoặc useReducerhook.
Điều gì sẽ xảy ra nếu chúng ta có một đối tượng gồm các thuộc tính mà chúng ta muốn chuyển xuống dưới dạng các giá trị prop độc lập? Tuy nhiên, đối tượng này có rất nhiều thuộc tính. Chúng ta có cần tạo thủ công các đạo cụ riêng lẻ và đặt giá đỡ thành object.propertyName không?
Thay vì làm điều đó cho mọi thuộc tính, chúng ta có thể rất dễ dàng lấy đối tượng và trải rộng các thuộc tính của nó xuống một thành phần dưới dạng các giá trị chống đỡ riêng lẻ bằng cách sử dụng toán tử mở rộng đối tượng {...myPropObject}.
Bằng cách sử dụng cú pháp này – một tính năng trong ES7, cho phép tự động tạo các đạo cụ riêng lẻ theo tên thuộc tính của đối tượng mà không cần phải viết tất cả các tên thuộc tính đó. Nó rất thuận tiện khi làm việc với các đối tượng lớn với nhiều thuộc tính mà chúng ta muốn chuyển dưới dạng các đạo cụ riêng lẻ cho một thành phần.
8. React props có thể tạo ra default value nếu không có giá trị nào được cung cấp
Điều gì sẽ xảy ra nếu ta đã biết rằng chúng mình đang chuyển một chỗ dựa cho một thể hiện của một thành phần, nhưng chúng ta không chuyển chỗ dựa đó cho một instance khác của nó?
Hoặc có thể biết rằng chỗ dựa có thể không có giá trị. Cần làm gì để cung cấp cho nó một giá trị mặc định thay vì chỉ một giá trị undefined nếu không có giá trị prop nào được chuyển cho nó?
Nếu chúng ta đang sử dụng hàm destructuring để truy cập prop đó trong thành phần hàm của mình, chúng ta có thể sử dụng toán tử bằng để cung cấp cho nó một giá trị mặc định. Vì vậy, nếu không có giá trị prop nào được chuyển cho prop đó, chúng ta có thể sử dụng toán tử bằng bên cạnh nó và đặt nó thành giá trị mặc định tốt hơn.
Đặt một giá trị mặc định là rất quan trọng vì giá trị mặc định bình thường cho một giá trị là undefined. Điều này có thể giúp tránh các lỗi có thể xảy ra do default value không ở đó.
9. React props có thể đặt lại tên để tránh errors
Ngoài ra, nếu có xung đột khi đặt tên với một trong các đạo cụ thì sao? Điều gì sẽ xảy ra nếu chúng ta sử dụng một tên prop trên nhiều thành phần và thấy rằng có một giá trị khác trong thành phần của chúng ta có cùng tên biến?
Thay vì phải đi vòng quanh và đổi tên tất cả các giá trị prop trên tất cả các phiên bản của các thành phần, chúng ta chỉ cần sử dụng dấu hai chấm sau tên prop đó, nếu chúng ta đang destructure nó, để đặt cho nó một tên gọi riêng biệt.
Nói cách khác, bạn có thể đặt cho nó một cái tên khác chỉ trong trường hợp đó. Điều này sẽ tránh được xung đột đặt tên cũng như lỗi.
10. Không nên destructure React props nhiều lần
Nếu chúng ta đang destructure một đối tượng từ các props, hãy lưu ý rằng có thể destructure prop đó sâu hơn nữa, tùy vào các thuộc tính cấu thành của nó. Tuy nhiên, thông thường không nên làm như vậy trừ khi bạn rất tin tưởng rằng đối tượng đó sẽ luôn có những thuộc tính đó.
Nếu một trong những thuộc tính đó bị thiếu và bạn cố gắng destructure nó ở nhiều cấp độ chuyên sâu, nó có thể gây ra cho bạn một lỗi khá khó chịu khi bạn đang cố gắng truy cập vào một thuộc tính không tồn tại.
Lưu ý rằng bạn có thể sử dụng destructure bao nhiêu tùy thích, nhưng nó có thể khó đọc đến một điểm nhất định và nó cũng có thể không đáng tin cậy. Nếu bạn đang cố gắng truy cập một thuộc tính trên một đối tượng, có thể không tồn tại, nó sẽ gây ra lỗi.
Trong các bài viết trước chúng ta đã cùng tìm hiểu về các loại Exchange trong RabbitMQ. Có một câu hỏi đặt ra là có thể thực hiện binding một Exchange đến Exchange khác hay không? Câu trả lời là có và chúng ta sẽ thấy cách thực hiện như thế nào trong phần tiếp theo của bài viết này.
Sơ đồ bên dưới là sự kết hợp của sơ đồ ở bài viết Topic Exchange và Header Exchange. Điểm khác biệt duy nhất là cách mà Header Exchange binding tới Topic Exchange với routing key pattern “#.gpcoder.com“.
Một Producer publish một Message đến source Exchange với một routing key dựa trên loại của Exchange. Trong trường hợp này là GPCoderTopicExchange.
Có 2 Queue: QJava và QAll được binding tới Exchange GPCoderTopicExchange lần lượt với routing key patter:
QJava : Queue này sẽ nhận tất cả message có Key match với routing key “java.*.gpcoder.com“. Nghĩa là chỉ nhận các message cho một topic Java cụ thể từ gpcoder.com, chẳng hạn: java.core.gpcoder.com, java.collection.gpcoder.com.
QAll : Queue này nhận tất cả message có Key match với routing key “#.gpcoder.com“. Nghĩa là nhận tất cả message từ gpcoder.com, chẳng hạn: design-pattern.gpcoder, java.gpcoder.com, creational.design-pattern.gpcoder.com.
Một Exchange khác là GPCoderHeadersExchange binding tới Exchange GPCoderTopicExchange với routing key pattern #.gpcoder.com.
Có 3 Queue: QDeveloper, QManager và QPublished được binding tới Exchange GPCoderHeadersExchange với các header:
QDeveloper : Queue này sẽ nhận tất cả message có header là {“dev”, “Developer Channel”} hoặc {“general”, “General Channel”}.
QManager : Queue này nhận tất cả message có header là {“dev”, “Developer Channel”} hoặc {“general”, “General Channel”} hoặc {“manager”, “Manager Channel”}.
QPublished : Queue này nhận tất cả message có header là {“dev”, “Developer Channel”} và {“access”, “publish”}.
Bây giờ nếu một Message được gởi tới Exchange với routing key là design-pattern.gpcoder.com và header là {“dev”, “Developer Channel”} sẽ được chuyển tới Queue QAll và GPCoderHeadersExchange. Sau đó GPCoderHeadersExchange sẽ chuyển đến Queue QDeveloper và QManager dựa vào header {“dev”, “Developer Channel”}.
Tương tự vậy, chúng ta cũng có thể định nghĩa một hay nhiều Exchange khác, chẳng hạn GPCoderDirectExchange binding tới GPCoderHeadersExchange và GPCoderTopicExchange.
Một Exchange nhận Message từ publisher được gọi là Source Exchange. Trong sơ đồ trên, GPCoderTopicExchange là Source Exchange.
Một Exchange nhận Message từ một Exchange khác gọi là Destination Exchange. Trong sơ đồ trên, GPCoderHeadersExchange là Destination Exchange.
Ví dụ binding Exchange to Exchange trong RabbitMQ
Một số class của chương trình:
ConnectionManager : hỗ trợ tạo Connection đến RabbitMQ.
ExchangeChannel : class util hỗ trợ tạo Echange, Queue, binding Queue đến Exchange, binding Exchange đến Exchange, publish/ subscribe message, …
Constant : định nghĩa constant chứa các thông tin về tên Exchange, Queue.
HeadersExchangeProducer : để gửi Message đến GPCoderHeadersExchange.
TopicExchangeProducer : để gửi Message đến GPCoderTopicExchange.
HeadersExchangeConsumer : để nhận Message từ Queue được binding đến GPCoderHeadersExchange.
TopicExchangeConsumer : để nhận Message từ Queue được binding đến GPCoderTopicExchange.
App: giả lập việc gửi nhận Message thông qua Topic Exchange của RabbitMQ.
ConnectionManager.java
package com.gpcoder.exchange2exchange;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ConnectionManager {
private ConnectionManager() {
super();
}
public static Connection createConnection() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
return factory.newConnection();
}
}
package com.gpcoder.exchange2exchange;
public final class Constant {
// Exchange
public static final String HEADERS_EXCHANGE_NAME = "GPCoderHeadersExchange";
public static final String TOPIC_EXCHANGE_NAME = "GPCoderTopicExchange";
// Queue
public static final String DEV_QUEUE_NAME = "QDeveloper";
public static final String MANAGER_QUEUE_NAME = "QManager";
public static final String PUBLISHED_QUEUE_NAME = "QPublished";
public static final String JAVA_QUEUE_NAME = "QJava";
public static final String ALL_QUEUE_NAME = "QAll";
// Routing key pattern
public static final String JAVA_ROUTING_KEY = "java.*.gpcoder.com";
public static final String GPCODER_ROUTING_KEY = "#.gpcoder.com";
// Message key
public static final String JAVA_CORE_MSG_KEY = "java.core.gpcoder.com";
public static final String JAVA_MSG_KEY = "java.gpcoder.com";
public static final String DESIGN_PATTERN_MSG_KEY = "design-pattern.gpcoder.com";
public static final String NOT_MATCHING_MSG_KEY = "java.collection.gpcoder.com.vn";
private Constant() {
super();
}
}
HeadersExchangeProducer.java
package com.gpcoder.exchange2exchange;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import static com.gpcoder.exchange2exchange.Constant.*;
public class HeadersExchangeProducer {
private ExchangeChannel channel;
public void start() throws IOException, TimeoutException {
// Create connection
Connection connection = ConnectionManager.createConnection();
// Create channel
channel = new ExchangeChannel(connection, HEADERS_EXCHANGE_NAME);
// Create headers exchange
channel.declareExchange(BuiltinExchangeType.HEADERS);
// Create queues
channel.declareQueues(DEV_QUEUE_NAME, MANAGER_QUEUE_NAME, PUBLISHED_QUEUE_NAME);
// Binding queues with headers
Map<String, Object> devGroup = new HashMap<>();
devGroup.put("x-match", "any"); // Match any of the header
devGroup.put("dev", "Developer Channel");
devGroup.put("general", "General Channel");
Map<String, Object> managerGroup = new HashMap<>();
managerGroup.put("x-match", "any"); // Match any of the header
managerGroup.put("dev", "Developer Channel");
managerGroup.put("manager", "Manager Channel");
managerGroup.put("general", "General Channel");
Map<String, Object> publishedGroup = new HashMap<>();
publishedGroup.put("x-match", "all"); // Match all of the header
publishedGroup.put("general", "General Channel");
publishedGroup.put("access", "publish");
channel.performQueueBinding(DEV_QUEUE_NAME, "", devGroup);
channel.performQueueBinding(MANAGER_QUEUE_NAME, "", managerGroup);
channel.performQueueBinding(PUBLISHED_QUEUE_NAME, "", publishedGroup);
}
}
package com.gpcoder.exchange2exchange;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import static com.gpcoder.exchange2exchange.Constant.DESIGN_PATTERN_MSG_KEY;
public class App {
public static void main(String[] args) throws IOException, TimeoutException {
HeadersExchangeProducer producer1 = new HeadersExchangeProducer();
producer1.start();
TopicExchangeProducer producer2 = new TopicExchangeProducer();
producer2.start();
// Publish some messages
Map<String, Object> devHeader = new HashMap<>();
devHeader.put("dev", "Developer Channel");
producer2.send("[1] Head First Design Pattern", DESIGN_PATTERN_MSG_KEY, devHeader);
HeadersExchangeConsumer consumer1 = new HeadersExchangeConsumer();
consumer1.start();
consumer1.subscribe();
TopicExchangeConsumer consumer2 = new TopicExchangeConsumer();
consumer2.start();
consumer2.subscribe();
}
}
Output của chương trình:
[Send] [{dev=Developer Channel}]: [1] Head First Design Pattern
[Received] [QDeveloper]: amq.ctag-QVREG0uBg6XsIebvbecqCg
[Received] [QDeveloper]: [1] Head First Design Pattern
[Received] [QManager]: amq.ctag-vLHleyPNedl2ZiMYukIing
[Received] [QManager]: [1] Head First Design Pattern
[Received] [QAll]: amq.ctag-GymeB0uwpALjgoM3RP_cvg
[Received] [QAll]: [1] Head First Design Pattern
Như bạn thấy, Message được gởi tới Exchange với routing key là design-pattern.gpcoder.com và header là {“dev”, “Developer Channel”} sẽ được chuyển tới Queue QAll và GPCoderHeadersExchange. Sau đó GPCoderHeadersExchange sẽ chuyển đến Queue QDeveloper và QManager dựa vào header {“dev”, “Developer Channel”}.
Tóm lại, Exchange Binding trong RabbitMQ là một tính năng vô cùng mạnh mẽ, giúp chúng ta kết hợp nhiều loại Exchange lại với nhau, giúp hệ thống mở rộng tốt hơn và đáp ứng được hầu hết yêu cầu phức tạp của hệ thống.
p5.js là một thư viện Javascript, thường thì nó sẽ sử dụng để dùng làm những thứ linh tinh với đồ họa các thứ sử dụng canvas. Các bạn có thể sử dụng p5.js bằng p5 Web Editor ở đây.
Ở trong p5.js thì sẽ có hai function quan trọng. Đó là setup() và draw().
setup()
Đây là function sẽ chạy ngay lập tức khi chạy. Thường thì hay dùng để config trước khi chạy thực tế.
draw()
Function này sẽ chạy ngay sau thằng setup() ở trên. Đây là function chính của p5.js.
ml5.js là gì?
ml5.js là một thư viện bao gồm các thuật toán và pre-trained models cho browser. ml5.js được build trên nền của tensoeflow.js. Vì thế, mình có thể dùng ml5.js để build một số thứ hay ho dựa trên các pre-trained models có sẵn.
Lý do mình dùng p5.js trong bài này là vì thằng ml5.js này chơi thân với thằng p5.js nên nó dễ sử dụng hơn.
Ví dụ
Bây giờ thì mình làm linh tinh một cái gì đó để ví dụ về cái ml5.js này cho vui. Mình ở đây sẽ làm một cái project vui vui sử dụng poseNet pre-trained model.
poseNet pre-trained model
poseNet pre-trained model là một pre-trained model dùng để mô phỏng vị trí tay, chân, mắt, mũi, miệng, bla bla bla của con người.
Cấu trúc Project
Về cấu trúc thì đơn giản như sau.
index.html
nancy.mp4
script.js
Bash
Trong file index.html thì import p5.js và ml5.js vào thôi
<!DOCTYPE html><htmllang="en"><head><title>Getting Started with ml5.js</title><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/addons/p5.dom.min.js"></script><scriptsrc="https://unpkg.com/ml5@0.4.1/dist/ml5.min.js"></script></head><body><scriptsrc="script.js"></script></body></html>
HTML
Load Video bằng function createVideo của p5.js
Để load Video thì trong p5.js mình dùng function createVideo(). Vì là load video thì mình viết nó trong setup(), sau khi load xong rồi thì mới hiển thị đó ra ở draw() function.
let video;let poses =[];functionsetup(){createCanvas(406,720);
video =createVideo(["nancy.mp4"],()=>{
video.loop();
video.volume(0);});
video.size(width, height);
video.hide();}functiondraw(){image(video,0,0, width, height);}
JavaScript
Lúc này kết quả như sau.
Load poseNet pre-trained model
Công việc tiếp theo là mình load tiếp pre-trained model vào. Mình sẽ viết tiếp trong function setup() như sau.
poseNet = ml5.poseNet(video,()=>{console.log("Model is ready");});// Listen to new 'pose' events
poseNet.on("pose",function(results){
poses = results;});
JavaScript
Vẽ skeleton lên Video
Sau khi lắng nghe sự kiện pose ở trên. Nó sẽ trả một mảng các pose, bao gồm các properties như sau:
// A function to draw the skeletonsfunctiondrawSkeleton(){// Loop through all the skeletons detectedfor(let i =0; i < poses.length; i++){let skeleton = poses[i].skeleton;// For every skeleton, loop through all body connectionsfor(let j =0; j < skeleton.length; j++){let partA = skeleton[j][0];let partB = skeleton[j][1];stroke(255,0,0);strokeWeight(4);line(
partA.position.x,
partA.position.y,
partB.position.x,
partB.position.y
);}}}
JavaScript
Sau đó để function drawSkeleton() này vào function draw()
Trên đây là một ứng dụng cơ bản của thằng ml5.js. Ngoài poseNet pre-trained model, thằng ml5.js còn cung cấp một số pre-trained model khác cũng hay ho, ví dụ như là Image Classifier sử dụng Mobilet Net, FaceApi, hay YOLO. Các bạn cứ ghé vào trang chủ của ml5.js để ngó qua nhé.
Bài viết được sự cho phép của tác giả Trần Khôi Nguyên Hoàng
Ngày nay, xử lý dữ liệu lớn đã trở thành bài toán tiếp xúc hằng ngày đối với Kỹ sư phần mềm, Distributed Data Processing dựa trên MapReduce mà một kỹ thuật được sử dụng để giải quyết những bài toán đó.
Thông qua kiến trúc và cách xử lý của MapReduce. Dữ liệu lên tới hàng Petabyte cũng có thể xử lý linh hoạt và nhanh chóng.
Còn chần chừ gì nữa mà không tìm hiểu xem ta có thể thực hiện như thế nào?. Bắt dầu thôi!
1. Distributed Data Processing
Processing đối với data được đề cập trong bài viết này là đề cập tới dữ liệu lớn (large data). Con số lên tới petabytes.
Ngoài ra, process cũng bao gồm các vấn đề khó nhắn như: Search LIKE, trả về tất cả dữ liệu có chứa dòng chữ “hello”. Sẽ không có gì để bàn nếu ta đang muốn giữ cho peformance ở mức tốt cho dù dữ liệu rất lớn. Bằng cách sử dụng hệ cơ sở dữ liệu phân tán (Distributed Database) kết hợp với kiến trúc MapReduce
Bắt đầu với MapReduce, vậy MapReduce là gì?
1.1 MapReduce = Map + Reduce.
Map, written by the user, takes an input pair and produces a set of intermediate key/value pairs. The MapReduce library groups together all intermediate values associated with the same intermediate key I and passes them to the Reduce function
Map, thường được viết bởi user, nhận đầu vào là một cặp key value, tạo ra một tập hợp các key-value trun gian. Thư viện MapReduce sẽ gom tất cả các cặp key value trung gian cùng với khóa tạm I và gửi chúng tới Reduce function
Túm váy lại thì Map sẽ tạo ra các cặp key-value, nó là input cho Reduce, khi đã có danh sách key-value trung gian, ta sẽ gọi Reduce function.
The Reduce function, also written by the user, accepts an intermediate key I and a set of values for that key. It merges together these values to form a possibly smaller set of values. The intermediate values are supplied to the user’s reduce function via an iterator. This allows us to handle lists of values that are too large totinmemory
Reduce function, cũng được viết bởi user, nhận vào khóa tạm I và danh sách giá trị cho khóa đó. Merge chúng lại với nhau để tạo thành một tập giá trị có thể nhỏ hơn. Các giá trị trung gian được tạo ra thông qua quá trình lặp (interator). Điều này cho phép chúng ta xử lý danh sách các giá trị quá lớn để vừa trong bộ nhớ
Đọc hơi khó hiểu, tuy nhiên chỉ cần nhớ đơn giản rằng MapReduce cho phép lặp các giá trị tuần tự trong MapReduce, đem ra xử lí. Quá trình xử lí này là tuần tự cho từng phần tử. Trường hợp dữ liệu cực lớn, không cần phải có toàn bộ dữ liệu mới thực hiện xử lý (data processing)
2. MapReduce Architecture
Với lượng dữ liệu khổng lồ, nếu muốn xử lí dữ liệu theo hướng thông thường như lưu trên disk, ram, thực hiện tuần tự là điều không khả thi. Chính vì vậy, ta cần một kiến trúc mới, xử lí tuần tự như MapReduce. Lúc này Distributed Data Processing cho dữ liệu lớn trở nên khả thi hơn bao giờ hết.
Bài toán thực tế cho thấy ta có:
Documents: file cần xử lý với dung lượng khổng lồ, cỡ Petabyte
Master: thực hiện tính toán phân chia cho các worker trên system. Dựa vào tài nguyên thì mỗi worker sẽ có khả năng xử lý nhất định, (MB/s)
Worker: Xử lí nội dung file theo yêu cầu từ phía master, sức mạnh của các worker là ngang bằng nhau.
Áp dụng nguyên lý và ý tưởng từ Distributed Storage và Distributed Database, ta sẽ chia Documents thành các phần nhỏ. Mỗi phần sẽ vừa đủ để một Worker có thể xử lí
Trường hợp mỗi Worker có thể xử lý 256MB, ta sẽ chia document thành 4000 phần nhỏ. Nếu có 1000 workers, mỗi worker sẽ chịu trách nhiệm xử lý cho 4 Splits.
Lúc này, vai trò của master trở thành người giám sát, ghi lại tiến trình của từng worker. Phân phối Splits nếu worker đã không còn gì để mà xúc.
Để đảm bảo tính phân tán và stateless của mỗi worker, splits không phân chia tuần tự tới từng worker
Các splits không phân chia tuần tự tới từng worker
3. Handle khi MapReduce fail
Trong quá trình processing data, không thể tránh khỏi các trường hợp bị fail. Lúc này, khi một worker bị fail, master sẽ ghi nhận và đổi trạng thái của worker thành failed
Any map task or reduce task in progress on a failed worker is also reset to idle and becomes eligible for rescheduling
Bất kì Map task hay reduce task đang có trạng thái in progress hay failed đều sẽ reset về trạng thái idle và có thể rescheduling lại
Về phía master
If the master task dies,a new copy can be started from the last check pointed state. However, given that there is only a single master, its failure is unlikely ;therefore our current implementation aborts the MapReduce computation if the master fails. Client scan check for this condition and retry the MapReduce operation if they desir
Nếu master tiêu, một bản copy khác có thể bắt đầu lại từ trạng thái kiểm tra cuối cùng. Tuy nhiên, do chỉ có một master duy nhất, nên việc thất bại ở master thường không xảy ra. Do đó, trường hợp master fail, ta đơn giản hủy bỏ tính toán của MapReduce hiện tại, phía client có thể kiểm tra điều kiện này và thử lại ở MapReduce nào bị fail
React Props Cheatsheet: 10 Patterns mà bạn nên biết (Phần 1)
Tác giả: Reed Barger
Props là công cụ mạnh mẽ được sử dụng trong React, nhưng làm thế nào để bạn sử dụng chúng một cách hiệu quả để viết các thành phần và ứng dụng React mạnh mẽ, đáng tin cậy?
1. React props có thể pass một cách có điều kiện
Các đạo cụ được chuyển cho các thành phần có thể được coi như các đối số được truyền cho một hàm. Nếu các giá trị prop không được thông qua một thành phần nhất định, error sẽ không xuất hiện. Thay vào đó, trong thành phần prop sẽ có giá trị là undefined.
Nếu bạn muốn được cảnh báo về thời điểm một giá trị không được chuyển làm hỗ trợ cho một thành phần, bạn có thể sử dụng một công cụ như prop-type hoặc TypeScript bằng các công cụ này.
Trong React đơn giản, hãy lưu ý rằng bạn thường quên việc chuyển các đạo cụ. Ngoài ra, bạn có thể không chuyển một chỗ dựa nhất định cho một thành phần, nếu bạn chọn.
2. React props được thông qua khi tên chúng có giá trị đúng
Mỗi prop phải được cung cấp một giá trị liên quan được cung cấp sau toán tử bằng. Nhưng điều gì sẽ xảy ra khi chúng ta không cung cấp toán tử bằng cũng như một giá trị?
Nếu bạn chỉ cung cấp tên riêng cho một thành phần mà không có gì khác, bạn sẽ chuyển một giá trị boolean là true cho phần hỗ trợ đó cho thành phần đó. Không cần phải viết rằng một prop bằng true. Thay vào đó, bạn chỉ có thể bao gồm giá trị prop và nó sẽ được cung cấp giá trị boolean true khi bạn sử dụng nó trong một thành phần mà bạn chuyển nó vào.
3. React props có thể truy cập như một object hoặc tái cấu trúc
Có một số mẫu mà chúng ta có thể sử dụng để truy cập các giá trị prop trong các thành phần của chúng ta. Props có thể được truy cập dưới dạng toàn bộ đối tượng thường được gọi là “props”. Hoặc chúng có thể tái cấu trúc, vì đạo cụ sẽ luôn là một đối tượng, thành các biến riêng biệt. Nếu bạn có nhiều đạo cụ đang chuyển cho thành phần của mình, tốt nhất có thể đưa chúng vào toàn bộ đối tượng đạo cụ và truy cập chúng bằng cách nói props.propName.
Tuy nhiên, nếu bạn chỉ có một vài đạo cụ mà bạn đang chuyển xuống thành phần của mình, bạn có thể hủy cấu trúc chúng ngay lập tức trong các tham số của thành phần chức năng của bạn.
Bạn có thể sử dụng cấu trúc đối tượng ES6 để bao gồm một tập hợp các dấu ngoặc nhọn trong các tham số của thành phần hàm của bạn và ngay lập tức lấy ra các thuộc tính của đối tượng, cũng như khai báo chúng dưới dạng các biến riêng biệt. Điều này cắt giảm code của chúng tôi và loại bỏ sự cần thiết phải nói props.propName để nhận từng giá trị đạo cụ.
Chúng tôi đã thấy rằng các đạo cụ rất linh hoạt và nếu chúng tôi không chuyển chúng cho một thành phần, error sẽ không xuất hiện. Tính linh hoạt này cũng được mở rộng cho những gì chúng ta có thể vượt qua như một chỗ dựa. Không chỉ các phần tử JSX có thể được chuyển làm đạo cụ cho các thành phần mà chúng ta còn có thể chuyển các thành phần khác làm đạo cụ.
Trên thực tế, có một loại giá đỡ đặc biệt được cung cấp tự động trên đối tượng đạo cụ được gọi là children.
5. Mọi thứ có thể được thông qua như prop trong React
Bất kỳ giá trị JavaScript bình thường nào cũng có thể được chuyển dưới dạng props, bao gồm cả các hàm. Có một số mẫu mạnh mẽ đã xuất hiện, do khả năng chuyển các chức năng làm đạo cụ. Một mẫu rất phổ biến là truyền một hàm xuống một thành phần con như một chỗ dựa, có thể cập nhật trạng thái của thành phần mẹ, và sau đó gọi nó trong thành phần con đó.
Ngoài ra, còn có các mẫu khác, chẳng hạn như mẫu đạo cụ kết xuất, cũng liên quan đến việc chuyển một hàm xuống thành phần con để sau đó được gọi lại và thực hiện một số chức năng thành phần chéo.
Thay vì tạo mỗi observable để chờ đợi các subscribers đăng ký, chúng ta có thể tạo 1 factory observable sinh ra các observable cho mỗi lượt đăng ký mới của chúng.
Tạo 1 factory observable kiểu nguyên bằng toán tử deferred
Nghịch đảo flip, mỗi khi factory observable được đăng ký
Trả về các Observale khác nhau tùy thuộc giá trị của flip
Thêm mã sau vào sau hàm trên:
for _ in 0...3 {
factory.subscribe(onNext: {
print($0, terminator: "")
})
.disposed(by: disposeBag)
print()
}
Kết quả như sau:
--- Example of: deferred ---
123
456
123
456
Vậy ta hiểu là, mỗi lần khi ta đăng ký tới factory observable, thì ta nhận các giá trị khác nhau của chúng theo thứ tự xen kẽ nhau. Điều này để thể hiện rằng factory observable có thể tạo ra các observable khác nhau cho các subscriber.
Vậy là bài hôm nay kết thúc rồi. Trong bài tiếp theo chúng ta sẽ tập trung thực hành các khái niệm chính của Rxswift. Cụ thể bài tới là về Subjects.
Windows có lẽ là một hệ điều hành đã quá quen thuộc với người dùng phổ thông rồi, và macOS cũng vậy (vì đã xài Macbook thì mặc định sẽ là macOS mà – tuy nhiên mức độ phổ biến thì không thể bằng Windows được.
Còn với Linux và các phiên bản phát triển từ nhân Linux (như Ubuntu, Kali Linux, Pop!_OS…) thì không phải ai cũng lựa chọn để dùng và biết cách để dùng.
Vậy tại sao mình lại nói các bạn lập trình viên nên sử hệ điều hành này? Vâng, ở trong bài viết này mình sẽ chỉ ra cho bạn 5 lý do mà mình thấy là hợp lý nhất để các bạn chuyển sang sử dụng nền tảng này, các bạn có thể bổ sung thêm dưới phần comment về góc nhìn của bạn nhé
#1. Không phù hợp để chơi game !
Mình tin chắc là sẽ có nhiều bạn nghĩ lý do này là không thuyết phục, vì chuyện chơi game sẽ phụ thuộc vào sở thích cũng như lý trí mỗi người.
Mình không hề phủ nhận điều đó, nhưng một trong những cách để bạn tập trung hơn vào công việc đó là đừng bắt bản thân phải đưa ra lựa chọn. OK !
Ví dụ như khi bạn bạn đang xài hệ điều hành Windows và máy bạn có cài sẵn một số tựa game. Điều này khiến bạn nhiều lúc phải đưa ra lựa chọn giữa làm việc và chơi game để giải trí một lúc.
Tất nhiên rồi, sẽ có lúc bạn không thể cưỡng lại được sự lôi cuốn của mấy con game mình thích, và thế là bạn lại lao vào chơi game mà quên béng đi mất mình đang phải làm việc.
Mình đã từng trong tình trạng này rồi nên mình hiểu rất rõ cảm giác việc phải đưa ra lựa chọn như thế. Về lâu về dài nó sẽ không tốt cho việc hình thành thói quen của bạn.
Đấy là với các bạn làm chủ bản thân kém, còn với các bạn học ra học, chơi ra chơi thì đây có lẽ không phải là một lý do thuyết phục thật.
#2. Linux hoàn toàn miễn phí và Open Source
Windows hay Mac OS đều là những hệ điều trả phí. Và tất nhiên, bạn sẽ phải trả một khoản tiền không hề nhỏ so với thu nhập bình quân đầu người ở Việt Nam để có thể sở hữu bản quyền CHÍNH THỨC.
Mặc dù ở Việt Nam vấn đề bản quyền chưa được đề cao nên số lượng người sử dụng bản quyền lậu vẫn rất nhiều, đặc biệt là với hệ điều hành Windows. Nhưng mình thấy mấy năm trở lại đây chúng ta đã đỡ hơn rất nhiều rồi.
Còn ở nước ngoài thì khác, nơi mà vấn đề bản quyền được đề cao thì việc dùng lậu sẽ mang đến rất nhiều rủi ro cho người dùng. Chúng ta dần dần cũng vậy thôi, chắc chắn là như vậy !
Trong khi đó, lập trình viên lại là những người làm việc thường xuyên với máy tính và hệ điều hành. Ai sẽ là người chịu trách nhiệm khi mà hệ điều hành bạn dùng bị lỗi trong khi bạn đang dùng các phiên bản c.r.a.c.k, các phiên bản bẻ khóa…
Đây chính là lý do tại sao khi bạn hỏi các lập trình viên ở các nước phương tây, họ dùng Linux rất nhiều !
Linux là hệ điều hành mã nguồn mở, hoàn toàn miễn phí và open source. Có nghĩa là bạn không cần phải trả phí để mua bản quyền, mà chỉ cần tải về, cài đặt và dùng thôi.
Vậy một câu hỏi đặt ra là nhỡ có bị lỗi thì ai là người đứng ra chịu trách nhiệm? Vâng, thực ra là Linux được cả cộng đồng xây dựng chung nên rất ít lỗi và nếu có lỗi thì cộng đồng Linux cũng rất đông và sẽ giúp đỡ bạn thôi.
#3. Linux hỗ trợ hầu hết các ngôn ngữ lập trình
Đã là lập trình viên thì phần lớn thời gian họ làm việc với các ngôn ngữ lập trình, mà Linux và các phiên bản của Linux thì lại hỗ trợ hầu hết các ngôn ngữ lập trình: C/C++, Java, Python, PHP…
Không những vậy, việc thao tác dòng lệnh (command line) trên Linux phải gọi là rất sướng, sướng hơn Windows rất nhiều. Các bạn có thể cài đặt mọi thứ, từ ngôn ngữ lập trình cho đến các IDE, tools bằng cách gõ các dòng lệnh. Cảm giác mình sử dụng máy tính ở một cái tầm khác.
Nhiều bạn lập trình viên có tần suất dùng chuột rất ít do là đã quá quen với việc thao tác với dòng lệnh rồi. Tất nhiên khi bạn dùng quen rồi thì bạn cũng có khả năng làm được như vậy thôi.
Hơn nữa, hiện nay hầu hết các ngôn ngữ lập trình đều hỗ trợ CLI (Command Line Interface) nên khi bạn dùng Linux, bạn chỉ cần gõ lệnh là hầu như làm được hết mọi thứ.
#4. Tính bảo mật cao
Trước khi nói về tính bảo mật của Linux thì mình sẽ nói về tính bảo mật của Windows trước. Các bạn vẫn thường nghe về các lần vá lỗi bảo mật hoặc là thông báo lỗ hổng bảo mật trên Windows rất nhiều, đúng chứ.
Đó là khi bạn dùng bản Windows sạch, còn nếu bạn dùng các phiên bản c.r.ac.k, phiên bản lậu thì nguy cơ bị đe dọa bảo mật lại càng nghiêm trọng hơn nữa. Nhiều bạn cẩn thận, máy có nhiều tài liệu quan trọng sẽ còn phát sinh thêm chi phí mua thêm các phần mềm diệt virus.
Nhưng trên Linux thì không, Linux được cả cộng đồng đông đảo chung tay phát triển. Tất nhiên, mình không khẳng định Linux không có lỗi, nhưng nếu xét về tính bảo mật và an toàn thì mình vẫn đánh giá cao Linux hơn.
Vậy tại sao Linux lại bảo mật hơn Windows?
+ Quyền “root”: Gọi nôm na là quyền Admin đấy các bạn. Bình thường khi bạn cài Windows thì mặc định bạn sẽ có quyền này, nó cho phép bạn thực hiện mọi thao tác với hệ thống với quyền hạn cao nhất.
Nhưng trên Linux thì không, người dùng không được cấp quyền này theo mặc định. Nói cách khác thì dù có bị virus xâm nhập thì cũng không có quyền root để mà phá hoại hệ thống.
+ Ít bị dòm ngó bởi các hacker: Vâng, dễ hiểu thôi, mình lấy ngay ví dụ bên trên nhé giả dụ như có cài virus vào được rồi mà không làm gì được thì đương nhiên hacker sẽ tìm những nạn nhân khác dễ nuốt hơn. Mà khi ngoài tầm ngắm rồi thì càng ít bị tấn công, vậy nên vấn đề bảo mật càng ít bị đe dọa.
Còn nhiều nguyên nhân khác nữa, nhưng theo mình thấy đây là hai nguyên nhân tiêu biểu cũng như là dễ thấy nhất.
#5. Khả năng tùy biến cực cao
Như mình đã nói ở trên, Linux miễn phí 100%, có nghĩa là bạn có thể tùy chỉnh mọi thứ từ font chữ, theme, icon… bạn có thể tùy biến rất sâu vào hệ thống, tùy theo ý của bạn.
Thậm chí nhiều phiên bản hệ điều hành còn được xây dựng trên nền tảng các hệ điều hành Linux có sẵn. Khi bạn sử dụng Linux nó sẽ đem lại cho bạn một cảm giác gọi là cảm giác “được kiểm soát”.
Nói nôm na là bạn muốn làm gì thì làm, điều này không giống với Windows hay Mac OS vì hai hệ điều hành này còn liên quan tới vấn đề bản quyền nữa.
Mà các bạn lập trình nếu làm việc can thiệp sâu vào hệ thống, tới tầng hệ điều hành thì điều này lại cực kỳ quan trọng hơn nữa, do phải hiểu thì mới làm được, mới xây dựng sản phẩm trên hệ điều hành đó được.
#6. Lời Kết
Như vậy là trong trong bài viết này mình đã cùng với các bạn điểm qua 5 lý do rất thuyết phục để những bạn đang hoặc sẽ là lập trình viên thì nên sử dụng hệ điều hành Linux rồi nhé.
Có thể đúng nhưng chưa đủ, vậy nên rất mong được các bạn đóng góp thêm ý kiến mới, cũng như là đưa ra góc nhìn của bạn về vấn đề này để anh em cùng trao đổi thêm. Cám ơn các bạn trước nhé
Bài viết được sự cho phép của tác giả Trần Hữu Cương
Giới thiệu lớp Activity trong Android
Lớp Activity là thành phần quan trọng nhất của ứng dụng Android, cách mà chúng hoạt động tạo thành nền tảng cơ bản của mô hình lập trình ứng dụng.
Khi một Activity chỉ định là Activity chính, nó sẽ là màn hình đầu tiên khi khởi chạy ứng dụng. Một Activity này lại có thể gọi và kích hoạt một Activity khác.
Activity chịu trách nhiệm chuyển giao sự kiện cho các view trong nó và quản lý vòng đời của nó. Một ứng dụng Android có thể có một hoặc nhiều Activity.
Một class được gọi là Activity khi nó extend (kế thừa) từ những class cha như : AppCompatActivity, Activity, FragmentActivity
Để tạo một Activity thì bạn phải tạo ra một lớp kế thừa lớp Activity, sau đó triển khai tối thiểu phương thức onCreate(Bundle savedInstanceState), sau đó tùy ngữ cảnh mà khi Activity hoạt động vòng đời của nó diễn ra như mô tả ở hình sau:
onCreate(Bundle savedInstanceState): Được gọi khi hoạt động mới được tạo, tại đây khởi tạo các biến, nạp giao diện layout …, phương thức này cũng nhận dữ liệu lưu lại trạng thái hoạt động trước đó (với mục đích để phục hồi – savedInstanceState).
onStart(): Được gọi ngay trước khi Activity hiển thị trên màn hình.
onResume(): Được gọi ngay khi Activity bắt đầu có thể tương tác với người dùng, và Activity nằm trên cùng trong danh sách các Activity của hệ thống.
onPause(): Được gọi khi hệ thống sắp kích hoạt một Activity khác, nếu bạn quá tải phương thức này, thường để lưu lại dữ liệu thật nhanh để hệ thống còn kích hoạt Activity khác.
onStop(): Được gọi khi nó bị ẩn đi. Sau phương thức này, Activity có thể gọi onRestart() nếu nó được người dùng kích hoạt lại hoặc gọi onDestroy() để hết thúc.
onDestroy(): gọi khi Activity bị hủy hoàn toàn (ví dụ gọi finish(), hoặc người dùng kill Activity)
Trong vòng đời của ứng dụng Android bạn cần phân biệt 2 loại sau: Visible Lifetime và Foreground Lifetime
Visible Lifetime: xảy ra từ sau khi gọi onStart –> cho tới lúc gọi onStop : trong trường hợp này ta vẫn có thể thấy màn hình Activity
Foreground Lifetime: xảy ra từ khi gọi onResume –> cho tới lúc gọi onPause : trong suốt thời gian này Activity luôn nằm ở trên cùng và ta có thể tương tác được với nó
Bài viết được sự cho phép của tác giả Phạm Công Sơn
Sắp xếp chèn (insertion sort) là một thuật toán sắp xếp bắt chước cách sắp xếp quân bài của những người chơi bài. Muốn sắp một bộ bài theo trật tự người chơi bài rút lần lượt từ quân thứ 2, so với các quân đứng trước nó để chèn vào vị trí thích hợp.
var InsertionSort = function ()
{
$.extend(this, new SortX());
this.onCreateScripts = function (array, scripts)
{
var $this = this;
var addScript = function (a1, a2, action)
{
scripts.push($this.createScript0(a1, a2)); // Script lấy ra 2 số cần so sánh
scripts.push($this.createScript1(a1, a2)); // Script để thực hiện so sánh giữa 2 số
return action();
}
var j, last;
for (var i = 1; i < array.length; i++) { last = array[i]; j = i; while ((j > 0) && addScript(array[j - 1], last, () => array[j - 1].number > last.number))
{
var a1 = array[j - 1];
array[j] = a1;
scripts.push($this.createScript2(a1, last));
j = j - 1;
}
array[j] = last;
}
}
}
var pageSortX = new PageSortX();
pageSortX.sortX = new InsertionSort();
pageSortX.start();
Bài viết được sự cho phép của tác giả Phạm Công Sơn
Chọn phần tử nhỏ nhất trong n phần tử ban đầu, đưa phần tử này về vị trí đúng là đầu tiên của dãy hiện hành. Sau đó không quan tâm đến nó nữa, xem dãy hiện hành chỉ còn n-1 phần tử của dãy ban đầu, bắt đầu từ vị trí thứ 2. Lặp lại quá trình trên cho dãy hiện hành đến khi dãy hiện hành chỉ còn một phần tử. Dãy ban đầu có n phần tử, vậy tóm tắt ý tưởng thuật toán là thực hiện n-1 lượt việc đưa phần tử nhỏ nhất trong dãy hiện hành về vị trí đúng ở đầu dãy.
var SelectionSort = function ()
{
$.extend(this, new SortX());
this.name = "SelectionSort";
this.onCreateScripts = function (array, scripts)
{
var $this = this;
var addScript = function (a1, a2, action)
{
scripts.push($this.createScript0(a1, a2)); // Script lấy ra 2 số cần so sánh
scripts.push($this.createScript1(a1, a2)); // Script để thực hiện so sánh giữa 2 số
return action();
}
var min;
for (var i = 0; i < array.length - 1; i++)
{
min = i;
scripts.push(this.createScriptOneItem(array[min]));
for (var j = i + 1; j < array.length; j++) { if (addScript(array[j], array[min], () => array[j].number < array[min].number))
{
min = j;
scripts.push(this.createScriptOneItem(array[min]));
}
}
var b1 = array[i];
var b2 = array[min];
array[min] = b1;
array[i] = b2;
scripts.push(this.createScript2(b1, b2)); // Script thực hiện hoán đổi giữa 2 số với nhau.
}
}
}
var pageSortX = new PageSortX();
pageSortX.sortX = new SelectionSort();
pageSortX.start();