Month: February 2020

  • [Android Jetpack] Schedule tasks with WorkManager

    [Android Jetpack] Schedule tasks with WorkManager

    Giới thiệu Android Jetpack

    Bạn có thể hiểu Jetpack như 1 hệ sinh thái của android vậy. Jetpack là một tập hợp của Foundation, Architecture, Behavior, UI để giúp bạn tạo các ứng dụng Android tuyệt vời một cách nhanh chóng và dễ dàng.

    Theo đánh giá cá nhân của mình thì đây sẽ là tương lai của Android. Khi nó đã tích hợp mọi thứ mà một lập trình viên Android cần. Và điều mình thích nhất ở Jetpack là bộ Architecture.
    Chưa hề có 1 chuẩn nào về architecture cho đến khi Google chính thức đưa ra 1 chuẩn architecture cho lập trình viên Android.

    Các bạn có thể xem về Android Architecture Components tại đây

    Trong bài viết này thì mình sẽ viết về WorkManager.

    WorkManager

    Define

    WorkManager là 1 thành viên trong bộ Jetpack Architecture. WorkManager manage your Android background jobs. Tức là sao?. Mọi task vụ thực hiện dưới background như download, upload file, network… Các bạn đều có thể dùng WorkManager để thực thi.

    Overview

    Schedule tasks with WorkManager Là sao? Là thế này. Với WorkManager bạn có thể sắp xếp các task vụ của mình. Bạn có thể quyết định khi nào thực hiện task. Và giả sử như bạn có nhiều task cần thực hiện, bạn có thể quyết định thằng B chạy trước A rồi sau đó sẽ chạy thằng C hoặc ở 1 màn hình khác bạn lại cần chạy task theo thứ tự A > B > C. Nếu như task sau của bạn có input là output của task trước thì bạn cũng đừng lo lắng. Với WorkManager bạn có thể làm được.

    Chú ý nhé :

    WorkManager dành cho các task vụ mà ngay cả khi bạn thoát ứng dụng thì task vẫn thực hiện ví dụ như việc bạn upload data lên server. Còn các task vụ mà sẽ tắt khi thoát app ra thì nên dùng ThreadPools nhé các bạn.

    Xem qua cách hoạt động nào:

    WorkManager chọn cách thích hợp để chạy tác vụ của bạn dựa trên các yếu tố như level API
    thiết bị và trạng thái ứng dụng. Nếu WorkManager thực thi một trong các nhiệm vụ của bạn
    trong khi ứng dụng đang chạy, WorkManager có thể chạy tác vụ của bạn trong một luồng mới.
    Nếu ứng dụng của bạn không chạy, WorkManager chọn một cách thích hợp để schedule
    một backgorund task – tùy thuộc vào mức API của thiết bị và các phụ thuộc kèm theo,
    WorkManager có thể sử dụng JobScheduler, Firebase JobDispatcher hoặc AlarmManager.
    Bạn không cần viết logic để tìm ra khả năng của thiết bị và chọn một API thích hợp,
    thay vào đó bạn chỉ có thể giao nhiệm vụ của mình cho WorkManager và để cho nó chọn
    tùy chọn tốt nhất. (Đoạn này hiểu đơn giản là bạn chỉ cần sử dụng WorkManager thôi, đừng
    lăn tăn về việc nó sẽ sử dụng JobScheduler hay Firebase JobDispatcher, cũng như bạn k cần
    viết code check API này kia làm gì. WorkManager đã xử lý điều đó).

    WorkManager cung cấp một số tính năng nâng cao. Ví dụ, bạn có thể thiết lập một chuỗi
    các nhiệm vụ; khi một tác vụ kết thúc, WorkManager sẽ xếp hàng nhiệm vụ tiếp theo
    trong chuỗi. Bạn cũng có thể kiểm tra trạng thái của nhiệm vụ và các giá trị trả về của nó
    bằng cách quan sát LiveData của nó.
    (LiveData cũng là 1 component mới trong bộ Android Architecture Component nhé)

    Classes and concepts

    API WorkManager sử dụng một số lớp khác nhau. Tuy nhiên mình thấy có các class cần chú ý sau:

    Worker: class xác định task cần thực hiện. API WorkManager bao gồm 1 class abstract Worker. Bạn cần extend class này và thực hiện công việc tại đây.

    WorkRequest: đại diện cho một nhiệm vụ riêng lẻ. Ở mức tối thiểu, một đối tượng WorkRequest xác định lớp Worker nào sẽ thực hiện nhiệm vụ. Tuy nhiên, bạn cũng có thể thêm các chi tiết vào đối tượng WorkRequest, chỉ định những thứ như các trường hợp mà tác vụ sẽ chạy. Mỗi WorkRequest có một ID duy nhất được tạo tự động; bạn có thể sử dụng ID để thực hiện những việc như hủy một công việc xếp hàng đợi hoặc nhận trạng thái của tác vụ. WorkRequest là một lớp trừu tượng; trong mã của bạn, bạn sẽ sử dụng một trong các lớp con trực tiếp, OneTimeWorkRequest hoặc PeriodicWorkRequest.

    WorkRequest.Builder: một lớp trợ giúp để tạo các đối tượng WorkRequest. Bạn sẽ sử dụng một trong các lớp con, OneTimeWorkRequest.Builder hoặc PeriodicWorkRequest.Builder.
    Ràng buộc: chỉ định các hạn chế về thời điểm tác vụ sẽ chạy (ví dụ: “chỉ khi được kết nối với mạng”). Bạn tạo đối tượng Constraints với Constraints.Builder và chuyển các ràng buộc tới WorkRequest.Builder trước khi tạo WorkRequest.

    WorkManager: enqueues và quản lý các yêu cầu công việc. Bạn chuyển đối tượng WorkRequest của bạn tới WorkManager để enqueue nhiệm vụ. WorkManager lên lịch nhiệm vụ theo cách như vậy để trải rộng tải trên tài nguyên hệ thống, tất nhiên nó sẽ thực hiện với các ràng buộc mà bạn đã chỉ định.

    WorkStatus: chứa thông tin về một tác vụ cụ thể. WorkManager cung cấp một LiveData cho mỗi đối tượng WorkRequest. LiveData chứa đối tượng WorkStatus; bằng cách quan sát LiveData, bạn có thể xác định trạng thái hiện tại của tác vụ và nhận bất kỳ giá trị trả về nào sau khi tác vụ kết thúc.

    Typical workflow

    Giả sử bạn đang viết một ứng dụng thư viện ảnh và ứng dụng đó cần nén định kỳ hình ảnh được lưu trữ của nó. Bạn muốn sử dụng các API WorkManager để lên lịch nén ảnh. Trong trường hợp này, bạn không đặc biệt quan tâm khi nén xảy ra; bạn muốn thiết lập nhiệm vụ và quên nó đi.

    Đầu tiên, bạn sẽ định nghĩa lớp Worker của mình và ghi đè phương thức doWork () của nó. Lớp Worker của bạn chỉ định cách thực hiện thao tác, nhưng không có bất kỳ thông tin nào về thời điểm tác vụ sẽ chạy.

    public class CompressWorker extends Worker {
    @Override
    public Worker.WorkerResult doWork() {
    
        // Thực hiện task ở đây. 
        //Trong case này task là nén ảnh và không có params
        myCompress();
    
        // Cho biết kết quả: Thành công hay thất bại
        return WorkerResult.SUCCESS;
    
        // trả về RETRY nếu thấy bại. Task sẽ được thực thi lại
        // trả về FAILURE nếu muốn kết thúc, không thực thi lại.)
    }

    }

    Tiếp theo, bạn tạo một đối tượng OneTimeWorkRequest dựa trên Worker đó, sau đó enqueue nhiệm vụ với WorkManager:

    OneTimeWorkRequest compressionWork =
        new OneTimeWorkRequest.Builder(CompressWorker.class)
    .build();
    WorkManager.getInstance().enqueue(compressionWork);

    WorkManager chọn một thời điểm thích hợp để chạy tác vụ.Trong hầu hết các trường hợp, nếu bạn không chỉ định bất kỳ ràng buộc nào, WorkManager sẽ chạy tác vụ của bạn ngay lập tức. Nếu bạn cần kiểm tra trạng thái tác vụ, bạn có thể lấy đối tượng WorkStatus bằng cách xử lý LiveData. Ví dụ: nếu bạn muốn kiểm tra xem tác vụ đã hoàn tất chưa, bạn có thể sử dụng mã như sau:

    WorkManager.getInstance().getStatusById(compressionWork.getId())
    .observe(lifecycleOwner, workStatus -> {
        // Do something with the status
        if (workStatus != null && workStatus.getState().isFinished())
        { ... }
    });

    Để hiểu thêm về LiveData, các bạn xem tại đây

    Task constraints ( Sự ràng buộc)

    Nếu muốn, bạn có thể chỉ định các ràng buộc khi nhiệm vụ được chạy. Ví dụ: bạn có thể muốn chỉ định rằng tác vụ chỉ nên chạy khi thiết bị ở chế độ chờ và kết nối với nguồn. Trong trường hợp này, bạn cần tạo một đối tượng OneTimeWorkRequest.Builder và sử dụng trình tạo đó để tạo OneTimeWorkRequest:

    // Tạo sự ràng buộc, khi nào thì task được thực thi
    Constraints myConstraints = new Constraints.Builder()
    .setRequiresDeviceIdle(true)
    .setRequiresCharging(true)
    // Có rất nhiều ràng buộc có sẵn.
    // Tham khảo tại Constraints.Builder 
     .build();
    
    // ...sau đó tạo một OneTimeWorkRequest sử dụng các ràng buộc đó
     OneTimeWorkRequest compressionWork =
                new OneTimeWorkRequest.Builder(CompressWorker.class)
     .setConstraints(myConstraints)
     .build();

    Canceling a Task

    Bạn có thể hủy một task sau khi bạn enqueue nó. Để hủy tác vụ, bạn cần ID task đó, mà bạn có thể nhận được từ đối tượng WorkRequest. Ví dụ, đoạn mã sau hủy bỏ yêu cầu compressionWork từ phần trước:

    UUID compressionWorkId = compressionWork.getId();
    WorkManager.getInstance().cancelByWorkId(compressionWorkId);

    WorkManager cố gắng hết sức để hủy tác vụ, nhưng điều này vốn dĩ không chắc chắn – nhiệm vụ có thể đã chạy hoặc kết thúc khi bạn cố hủy nó. WorkManager cũng cung cấp các phương thức để hủy bỏ tất cả các nhiệm vụ trong một chuỗi công việc duy nhất, hoặc tất cả các nhiệm vụ với một thẻ (TAG) được chỉ định.

    Advanced functionality (Nâng cao)

    API WorkManager cung cấp các tính năng nâng cao cho phép bạn thiết lập các yêu cầu phức tạp.

    Recurring tasks(Nhiệm vụ định kỳ)

    Bạn có thể có một nhiệm vụ mà bạn cần phải thực hiện nhiều lần. Ví dụ: ứng dụng trình quản lý ảnh sẽ không muốn nén ảnh một lần. Nhiều khả năng, nó sẽ muốn kiểm tra hình ảnh được chia sẻ của nó thường xuyên như vậy, và xem nếu có bất kỳ hình ảnh mới hoặc thay đổi cần phải được nén. Tác vụ lặp lại này có thể nén hình ảnh mà nó tìm thấy, hoặc cách khác, nó có thể kích hoạt 1 tác vụ mới : “nén hình ảnh này”.

    Để tạo một nhiệm vụ định kỳ, sử dụng lớp PeriodicWorkRequest.Builder để tạo một đối tượng PeriodicWorkRequest, sau đó enqueue PeriodicWorkRequest giống như cách bạn sẽ làm đối tượng OneTimeWorkRequest. Ví dụ, giả sử chúng ta định nghĩa một lớp PhotoCheckWorker để xác định các hình ảnh cần được nén. Nếu chúng ta muốn chạy tác vụ kiểm kê mỗi 12 giờ, chúng ta sẽ tạo một đối tượng PeriodicWorkRequest như sau:

    new PeriodicWorkRequest.Builder photoWorkBuilder =
        new PeriodicWorkRequest.Builder(PhotoCheckWorker.class, 12,
                TimeUnit.HOURS);
    // ...nếu bạn muốn, bạn có thể áp dụng các ràng buộc  ở đây ...
    
    PeriodicWorkRequest photoWork = photoWorkBuilder.build();
    //  enqueue task:
    WorkManager.getInstance().enqueue(photoWork );

    WorkManager cố gắng chạy nhiệm vụ của bạn tại khoảng thời gian bạn yêu cầu, tùy thuộc vào các ràng buộc mà bạn áp đặt và các yêu cầu khác của nó.

    Chained tasks ( Chuỗi công việc)

    Ứng dụng của bạn có thể cần chạy một số tác vụ theo một thứ tự cụ thể. WorkManager cho phép bạn tạo và enqueue một chuỗi công việc xác định nhiều nhiệm vụ và thứ tự chúng sẽ chạy.

    Ví dụ: giả sử ứng dụng của bạn có ba đối tượng OneTimeWorkRequest: workA, workB và workC. Các nhiệm vụ phải được chạy theo thứ tự đó. Để enqueue chúng, tạo một chuỗi với phương thức WorkManager.beginWith (), truyền đối tượng OneTimeWorkRequest đầu tiên; phương thức đó trả về một đối tượng WorkContinuation, nó định nghĩa một chuỗi các nhiệm vụ. Sau đó, thêm các đối tượng OneTimeWorkRequest còn lại, theo thứ tự, với WorkContinuation.then (), và cuối cùng, enqueue toàn bộ chuỗi với WorkContinuation.enqueue ():

    Hơi khó hiểu đúng không? Xem ví dụ nhé:

    WorkManager.getInstance()
    .beginWith(workA)
        // Note: WorkManager.beginWith()  trả về 1 đối tượng WorkContinuation
    .then(workB)    // FYI, then() cũng trả về 1 thể hiện của WorkContinuation
    .then(workC)
    .enqueue();

    WorkManager chạy các tác vụ theo thứ tự được yêu cầu, theo các ràng buộc cụ thể của mỗi tác vụ. Nếu bất kỳ tác vụ nào trả về Worker.WorkerResult.FAILURE, toàn bộ chuỗi sẽ kết thúc.

    Bạn cũng có thể truyền nhiều đối tượng OneTimeWorkRequest cho bất kỳ lệnh gọi startsWith () và .then () nào. Nếu bạn truyền một số đối tượng OneTimeWorkRequest cho một cuộc gọi phương thức duy nhất, WorkManager sẽ chạy tất cả các tác vụ đó (song song) trước khi nó chạy phần còn lại của chuỗi. Ví dụ:

    WorkManager.getInstance()
    // First, run all the A tasks (in parallel):
    .beginWith(workA1, workA2, workA3)
    // ...when all A tasks are finished, run the single B task:
    .then(workB)
    // ...then run the C tasks (in any order):
    .then(workC1, workC2)
    .enqueue();

    Bạn có thể tạo các chuỗi phức tạp hơn bằng cách nối nhiều chuỗi với các phương thức WorkContinuation.combine (). Ví dụ: giả sử bạn muốn chạy một chuỗi như sau:

    Để thiết lập trình tự này, hãy tạo hai chuỗi riêng biệt, sau đó ghép chúng lại với nhau thành một chuỗi thứ ba:

    WorkContinuation chain1 = WorkManager.getInstance()
    .beginWith(workA)
    .then(workB);
    WorkContinuation chain2 = WorkManager.getInstance()
    .beginWith(workC)
    .then(workD);
    WorkContinuation chain3 = WorkContinuation
    .combine(chain1, chain2)
    .then(workE);
    chain3.enqueue();

    Trong trường hợp này, WorkManager chạy workA trước khi làm việc. Nó cũng hoạt động trước khi làm việc. Sau khi cả hai công việc và workD đã hoàn thành, WorkManager chạy workE.

    Có một số biến thể của phương thức WorkContinuation cung cấp viết tắt cho các tình huống cụ thể. Ví dụ, có một phương thức WorkContinuation.combine (OneTimeWorkRequest, WorkContinuation…), hướng dẫn WorkManager hoàn thành tất cả các chuỗi WorkContinuation đã chỉ định, sau đó kết thúc với OneTimeWorkRequest được chỉ định. Để biết chi tiết, xem WorkContinuation nhé.

    Unique work sequences (một chuỗi công việc duy nhất)

    Bạn có thể tạo một chuỗi công việc duy nhất, bằng cách bắt đầu chuỗi với một cuộc gọi đến beginUniqueWork () thay vì beginWith (). Mỗi chuỗi công việc duy nhất có một tên; WorkManager chỉ cho phép một chuỗi công việc với tên đó tại một thời điểm. Khi bạn tạo một chuỗi công việc duy nhất mới, bạn chỉ định những gì WorkManager sẽ làm nếu có một chuỗi chưa hoàn thành có cùng tên:

    • Hủy chuỗi hiện tại và thay thế bằng trình tự mới
    • Giữ chuỗi hiện tại và bỏ qua yêu cầu mới của bạn
    • Nối chuỗi mới của bạn vào chuỗi hiện tại, chạy tác vụ đầu tiên của chuỗi mới sau khi tác vụ cuối cùng của chuỗi hiện tại kết thúc.

    Một chuỗi công việc duy nhất có thể hữu ích nếu bạn có một nhiệm vụ không nên được enqueued nhiều lần. Ví dụ: nếu ứng dụng của bạn cần đồng bộ hóa dữ liệu với mạng, bạn có thể enqueue một chuỗi có tên là “sync” và chỉ định rằng tác vụ mới của bạn sẽ bị bỏ qua nếu đã có một chuỗi có tên đó. Một chuỗi công việc duy nhất cũng có thể hữu ích nếu bạn cần dần dần xây dựng một chuỗi nhiệm vụ dài. Ví dụ: ứng dụng chỉnh sửa ảnh có thể cho phép người dùng hoàn tác một chuỗi hành động dài. Mỗi hoạt động hoàn tác có thể mất một thời gian, nhưng chúng phải được thực hiện đúng thứ tự. Trong trường hợp này, ứng dụng có thể tạo chuỗi “hoàn tác” và nối thêm từng hoạt động hoàn tác vào chuỗi khi cần.

    Tagged work ( Task được gắn thẻ)

    Bạn có thể nhóm các nhiệm vụ của bạn một cách hợp lý bằng cách gán một chuỗi thẻ cho bất kỳ đối tượng WorkRequest nào. Để đặt thẻ, hãy gọi WorkRequest.Builder.addTag (), ví dụ:

    OneTimeWorkRequest cacheCleanupTask =
        new OneTimeWorkRequest.Builder(MyCacheCleanupWorker.class)
    .setConstraints(myConstraints)
    .addTag("cleanup")
    .build();

    Các lớp WorkManager cung cấp một số phương thức tiện ích cho phép bạn thao tác trên tất cả các nhiệm vụ với một thẻ cụ thể. Ví dụ, WorkManager.cancelAllWorkByTag () hủy bỏ tất cả các nhiệm vụ với một thẻ cụ thể, và WorkManager.getStatusesByTag () trả về một danh sách tất cả các WorkStatus cho tất cả các tác vụ với thẻ đó.

    Input parameters and returned values

    Để linh hoạt hơn, bạn có thể chuyển đối số cho công việc của mình và có nhiệm vụ trả về kết quả. Các giá trị được trả về và trả về là các cặp khóa-giá trị. Để chuyển một đối số cho một nhiệm vụ, hãy gọi phương thức WorkRequest.Builder.setInputData () trước khi bạn tạo đối tượng WorkRequest. Phương thức đó lấy một đối tượng Data, mà bạn tạo ra với Data.Builder. Lớp Worker có thể truy cập các đối số đó bằng cách gọi hàm Worker.getInputData (). Để xuất ra một giá trị trả về bạn sử dụng Worker.setOutputData (), lấy một đối tượng Data, bạn có thể lấy kết quả bằng cách quan sát LiveData .

    Ví dụ, giả sử bạn có một lớp Worker thực hiện một phép tính tốn thời gian. Đoạn mã sau cho thấy lớp Worker sẽ trông như thế nào:

    // Define the Worker class:
    public class MathWorker extends Worker {
    
    // Define the parameter keys:
    public static final String KEY_X_ARG = "X";
    public static final String KEY_Y_ARG = "Y";
    public static final String KEY_Z_ARG = "Z";
    // ...and the result key:
    public static final String KEY_RESULT = "result";
    
    @Override
    public Worker.WorkerResult doWork() {
    
    
        // Fetch the arguments (and specify default values):
        int x = getInputData().getInt(KEY_X_ARG, 0);
        int y = getInputData().getInt(KEY_Y_ARG, 0);
        int z = getInputData().getInt(KEY_Z_ARG, 0);
    
        // ...do the math...
        int result = myCrazyMathFunction(x, y, z);
    
        //...set the output, and we're done!
        Data output = new Data.Builder()
            .putInt(KEY_RESULT, result)
            .build();
        setOutputData(output);
        return WorkerResult.SUCCESS;
    }
    }

    Để tạo công việc và chuyển các đối số, bạn sẽ sử dụng mã như sau:

    // Create the Data object:
    Data myData = new Data.Builder()
        // We need to pass three integers: X, Y, and Z
        .putInt(KEY_X_ARG, 42)
        .putInt(KEY_Y_ARG, 421)
        .putInt(KEY_Z_ARG, 8675309)
        // ... and build the actual Data object:
        .build();
    
    // ...then create and enqueue a OneTimeWorkRequest that uses those arguments
    OneTimeWorkRequest.Builder argsWorkBuilder =
              new OneTimeWorkRequest.Builder(MathWorker.class)
        .setInputData(myData);
    OneTimeWorkRequest mathWork = argsWorkBuilder.build();
    WorkManager.getInstance().enqueue(mathWork);

    Giá trị trả về sẽ có sẵn trong WorkStatus của tác vụ:

    WorkManager.getInstance().getStatusById(mathWork.getId())
    .observe(lifecycleOwner, status -> {
         if (status != null) {
           int myResult =
               status.getOutputData().getInt(KEY_RESULT,
                  myDefaultValue));
    // ... do something with the result ...
         }
    });

    Nếu bạn có một chuỗi nhiệm vụ, kết quả đầu ra từ một nhiệm vụ có sẵn như là đầu vào cho nhiệm vụ tiếp theo trong chuỗi. Nếu đó là một chuỗi đơn giản, với một OneTimeWorkRequest duy nhất được theo sau bởi một OneTimeWorkRequest khác, tác vụ đầu tiên trả về kết quả của nó bằng cách gọi hàm setOutputData () và nhiệm vụ tiếp theo sẽ lấy kết quả đó bằng cách gọi phương thức getInputData (). Nếu chuỗi phức tạp hơn – ví dụ, bởi vì một số tác vụ gửi đầu ra tới một tác vụ duy nhất sau đây, bạn có thể định nghĩa một InputMerger trên OneTimeWorkRequest.Builder để xác định điều gì sẽ xảy ra nếu các tác vụ khác nhau trả về đầu ra có cùng khóa.

    Lời kết

    Đây là lần đầu tiên mình viết bài nên nếu có sai xót gì nhờ anh em đóng góp.
    Bài viết này mình tham khảo và dịch từ trang developer android, anh em có thể xem bản gốc tại đây

  • Architecting Android…The clean way?

    Architecting Android…The clean way?

    Lời Mở Đầu

    Nếu như bạn đã dành thời gian ra tìm hiểu về Architectute thì chắc chắn đã nghe qua “The Clean Architecture” rồi đúng không?

    Việc định hình ra architecture cho một project chưa bao giờ là đơn giản, nó phụ thuộc vào rất nhiều yếu tố như yêu cầu của khách hàng, UI , kĩ năng của member, chức năng của app… Dựa vào các yếu tố đó mà Project Technical Lead hay TeamLead sẽ quyết định dùng architecture, mô hình nào vào project.

    Trong bài viết này mình sẽ viết về The Clean Architecture – “Kiến trúc sạch”

    Getting Started

    Để viết ra một sản phẩm tốt là điều rất khó, tốt ở đây là sao? Ý kiến cá nhân của mình tốt có nghĩa là
    * easy to maintain
    * easy to debug
    * easy to develop
    * easy to understand
    * code clear

    Sau một hồi tìm kiếm, mình đã tìm ra The Clean Architecture.

    Bộ nguyên tắc của Clean Architecture như sau:

    * Độc lập ( tách biệt) với Framework.
    * Dễ dàng cho việc test code.
    * Tách biệt giữa bussiness và UI
    * Tách biệt với cơ sở dữ liệu
    * Independent of any external agency

    Nguyên tắc cuối mình để tiếng anh vì dịch ra tiếng việt nó tối nghĩa. khó hiểu. Nó được giải thích như sau: In fact your business rules simply don’t know anything at all about the outside world. Nghĩa là phần code logic của bạn nó là tách biệt, độc lập, nó không quan tâm đến cái cách mà nó được dùng như nào.

    Tóm lại cái quan trọng nhất mình thấy ở architecture này chính là ĐỘC LẬP (TÁCH BIỆT). tách biệt mọi thứ, càng tách biệt rõ càng tốt, càng clear. Mà vì lẽ đó nên nếu apply architecture này thì số lượng class của bạn sẽ rất lớn đó nhé

    Cấu trúc của The Clean Architecture

    Mình sẽ giải thích về 4 vòng tròn thông qua 1 ứng dụng Movie nhé.

    * Entities: Là các Object phục vụ cho Bussiness của bạn (là các model đó. Ví dụ MovieObject :P)
    * Use Cases: Mình thấy khá giống với khái niệm Usecase bên UML, nó đại diện cho các nghiệp vụ trong ứng dụng. ( Ví dụ Usecase: Show list HotMovies, show list favorite movies…)
    * Interface Adapters: hay còn gọi là tầng Presentation. Nó là cầu nối giữa tầng bussiness với tầng UI của bạn. Nếu bạn đã làm mô hình MVC hay MVP thì đây là nơi chứa Controller or Presenter.
    * Frameworks and Drivers: là tầng chứa UI, tools, frameworks, etc (ví dụ trong android thì tầng này là chứa Activity/Fragment đấy)

    Android Architecture

    Mục đích là tách ứng dụng thành các tầng tách biệt, không phụ thuộc vào nhau, như vậy mới dễ dàng cho việc test, cũng như maintain, phát triển.

    Để đạt được điều này, chúng ta sẽ chia nhỏ dự án thành 3 lớp khác nhau, trong đó mỗi lớp có mục đích riêng và hoạt động riêng biệt với các mục đích khác nhau.

    Mỗi lớp có thể thực hiện theo các mô hình, pattern khác nhau để đạt được mục đích của lớp đó.

    Presentation Layer
    Đây là cầu nối giữa logic với UI (animation…) bạn có thể apply MVP hay MVC, MVVM tại đây, mình sẽ không đi chi tiết vào mô hình. Chú ý rằng tại tầng này chỉ có views (Fragment, Activity), sẽ không viết bất kì logic nào trừ logic liên quan đến UI.

    Tầng Presenter sẽ sử dụng các interactors (uses case) để thực hiện logic dưới background thread (không thực hiện trên Mainthead(UI thread), sau đó trả về kết quả thông qua callback để view hiển thị.

    Domain Layer

    Tầng này chỉ viết logic mà thôi. Tại đây chúng ta sẽ định nghĩa các interactors (usecase, UserRepository).

    chú ý rằng lớp này là 1 module thuần java (kotlin) không chứa bất kì phụ thuộc Android nào. Các thành phần bên ngoài khác muốn trỏ đến sẽ dùng interface.
     

    Data Layer

    Tất cả data của ứng dụng sẽ xử lý tại đây (hãy hiểu như này cho đơn giản, tầng này là tầng implement lại các interface repository ở tầng Domain cũng như định nghĩa ra các data model) .Chúng ta sẽ sử dụng Repository Pattern cho tầng này.
    Ví dụ khi chúng ta muốn lấy ra 1 bộ phim theo ID, repository sẽ quyết định lấy ra từ local nếu đã lưu bộ phim đó vào cache từ lần load trước, nếu chưa có sẽ request API để lấy bộ phim từ Server.

    Lời kết

    Như vậy là mình đã giới thiệu qua về The Clear Architecture.

    Bài viết mang yếu tố chủ quan, đánh giá cá nhân nên nếu sai xót nhờ anh em bổ sung giúp mình nhé.

  • Hướng dẫn sử dung mono repository với yarn workspaces

    Hướng dẫn sử dung mono repository với yarn workspaces

    Thông thường khi viết ứng dụng node, chúng ta thương sử dụng các thử viện có sẵn trên npmjs. Trong bài viết này tôi sẽ hướng dẫn các bạn tạo một dự án node có thể chia nhỏ thành các packages nhưng vẫn có thể viết trên cùng một repository. Yarn cung cấp cho bạn chức năng để tạo và link các packages với nhau bằng yarn workspaces. Chúng ta tìm hiểu yarn workspaces hoạt động ra sao nhé

    Các bạn có thể tham khảo hướng dẫn cài đặt yarn ở đây

    Tạo mới project

    hieunv@HieuNV hieunv % mkdir mono
    hieunv@HieuNV hieunv % cd mono
    hieunv@HieuNV mono % yarn init
    yarn init v1.22.0
    question name (mono):
    question version (1.0.0):
    question description:
    question entry point (index.js):
    question repository url:
    question author:
    question license (MIT):
    question private:
    success Saved package.json
    ✨  Done in 5.46s.
    hieunv@HieuNV mono %
    

    Enable yarn workspaces

    • Thêm đoạn "workspaces": ["packages/*"] vào package.json

    package.json

    {
      "name": "mono",
      "workspaces": ["packages/*"],
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT"
    }
    

    Tiến hành tạo mono repository theo các bước sau:

    • Tạo 3 package trong thư mục packages như sau:
    hieunv@HieuNV mono % find . -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
    .
    |____package.json
    |____packages
    | |____package-c
    | | |____index.js
    | | |____package.json
    | |____package-b
    | | |____index.js
    | | |____package.json
    | |____package-a
    | | |____index.js
    | | |____package.json
    
    • Tại thư mục package-apackage-b, thực hiện đăng ký package:

    package-a

    hieunv@HieuNV package-a % yarn link
    yarn link v1.22.0
    success Registered "package-a".
    info You can now run `yarn link "package-a"` in the projects where you want to use this package and it will be used instead.
    ✨  Done in 0.04s.
    

    package-b

    hieunv@HieuNV package-b % yarn link
    yarn link v1.22.0
    success Registered "package-b".
    info You can now run `yarn link "package-b"` in the projects where you want to use this package and it will be used instead.
    ✨  Done in 0.04s.
    
    • Thêm package-apackage-b như là dependency của package-c:
    hieunv@HieuNV package-c % yarn link "package-a"
    yarn link v1.22.0
    success Using linked package for "package-a".
    ✨  Done in 0.04s.
    hieunv@HieuNV package-c % yarn link "package-b"
    yarn link v1.22.0
    success Using linked package for "package-b".
    ✨  Done in 0.04s.
    
    • Trong package-a tạo các file sau:

    package-a/package.json

    {
      "type": "module",
      "name": "package-a",
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT"
    }
    

    package-a/index.js

    export function packageA() {
      console.log('Package A');
    }
    
    • Trong của package-b tạo các file sau:

    package-b/package.json

    {
      "type": "module",
      "name": "package-b",
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT"
    }
    

    package-b/index.js

    export function packageB() {
      console.log('Package B');
    }
    
    • Sử dụng các hàm được khai báo package-apackage-b

    package-c/package.json

    {
      "type": "module",
      "name": "package-c",
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT"
    }
    

    package-c/index.js

    import { packageA } from 'package-a';
    import { packageB } from 'package-b';
    
    packageA();
    packageB();
    
    • Run package-c/index.js
    hieunv@HieuNV package-c % node index.js
    (node:10165) ExperimentalWarning: The ESM module loader is experimental.
    Package A
    Package B
    

    Tài liệu tham khảo:

    • https://classic.yarnpkg.com/en/docs/workspaces/
  • Cải thiện Terminal mặc định của MacOS với zsh và zsh-autosuggestions

    Cải thiện Terminal mặc định của MacOS với zsh và zsh-autosuggestions

    Giới thiệu chung

    Sau khoảng thời gian làm việc trên MacOS, có quá nhiều lệnh bạn phải nhớ, hoặc đôi khi bạn phải thực hiện đi thực hiện lại nhiều lần, giá như terminal ngoài auto-complete mà có thể suggest được lệnh cho chúng ta thì tốt biết mấy. Bài viết này giúp bạn giải quyết điều đó!

    ?

    Về mình, thực ra mình là một thằng Developer khá đơn giản  nên mình thích mọi thứ cũng đơn giản, rộng rãi và thoáng đãng. Cũng bởi lẽ đó nên ngay từ khi bắt đầu sử dụng MacOS để làm việc, mình đã yêu thích Terminal mặc định của nó; ngay từ khi nhìn cái logo đơn giản nhưng không kém phần bắt mắt.

    Để làm được việc suggest lệnh thì với /bin/bash là không đủ, chúng ta cần đổi bộ shell mặc định này sang một thằng khác mạnh mẽ hơn đó là Z-Shell hay còn gọi là zsh.

    Đi kèm zsh có một framework đó là oh-my-zsh support mọi thứ từ theme, command line prompts, auto suggestions, .etc. Trong bài mình sẽ hướng dẫn mọi người cài đặt zsh và oh-my-zsh trên MacOS, tích hợp plugin zsh-autosuggestions cho oh-my-zsh để terminal có thể tự động suggest lệnh cho chúng ta. Hãy cùng bắt đầu nhé!

    Cài đặt zsh

    Cài đặt trực tiếp zsh thông qua brew như sau:

    $ brew install zsh

    Kiểm tra xem đã cài đặt thành công chưa:

    $ which zsh
    
    /usr/local/bin/zsh

    Cài đặt oh-my-zsh

    Bây giờ chúng ta cài đặt oh-my-zsh, là một framework cho zsh sẽ giúp mình cài đặt nhiều thứ như theme, PS1 prompts:

    $ sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

    Cài đặt zsh-autosuggestions

    Cài đặt plugin zsh-autosuggestions, giúp tự động suggetions các lệnh mà mình đã dùng:

    ➜ brew install zsh-autosuggestions

    $ brew install zsh-autosuggestions

    Tiếp đó để kích hoạt plugin zsh-autosuggtestions lên, mình cần chạy thêm lệnh sau:

    ➜ source ~/.oh-my-zsh/custom/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh

    Khởi động lại terminal và tận hưởng:

  • Hướng dẫn cài đặt DynamoDB với Docker

    Hướng dẫn cài đặt DynamoDB với Docker

    Cài đặt docker

    • Cập nhập brew
    hieunv@HieuNV ~ % brew update && brew upgrade
    Updated 1 tap (homebrew/core).
    No changes to formulae.
    
    • Cài đặt docker
    brew install docker
    
    • Kiểm tra docker sau khi cài đặt
    hieunv@HieuNV ~ % docker -v
    Docker version 19.03.1, build 74b1e89
    
    • Khởi động Docker
    Kiểm tra trạng thái docker

    Tạo DynamoDB container

    • Tạo docker-compose.yml để up DynamoDB

    docker-compose.yml

    version: '3'
    services:
      dynamodb:
        image: amazon/dynamodb-local
        container_name: dynamodb
        ports:
          - '8000:8000'
        volumes:
          - ./dynamodb/data:/home/dynamodblocal/data
        entrypoint: java
        command: '-jar DynamoDBLocal.jar -sharedDb -dbPath /home/dynamodblocal/data'
    
    • Up DynamoDB container sau khi tạo docker-compose.yml
    docker-compose up -d
    
    Kiểm tra trạng thái docker sau khi up DynamoDB container

    Bạn có thể truy cập vào docker shell bằng link sau:

    http://localhost:8000/shell
    

    Như vậy là bạn đã tạo xong container cho DynamoDB rồi.

  • Quản lý python packages như thế nào cho đúng

    Quản lý python packages như thế nào cho đúng

    Maven dùng pom để quản lý packages, Node thì có packages.json. Anh em python thì quản lý python packages bằng pip (Package installer for Python). Tuy nhiên khi sử dụng pip sẽ gặp phải tình huống các dự án khác nhau sử dụng dánh sách packages khác nhau. Vấn đề lớn hơn nữa có thể xảy ra tình huống 2 dự án nào đó sử dụng cùng package ở hai phiên bản khách nhau. Trong bài viết này tôi sẽ hướng dẫn các bạn cách quản lý python packages cho các dự án khác nhau mà không bị phụ thuộc vào nhau. Chúng ta cùng bắt đầu nhé.

    Kiểm tra tình trạng hoạt động của brew nào:

    hieunv@HieuNV ~ % brew update && brew upgrade
    Updated 1 tap (homebrew/core).
    ==> Updated Formulae
    abcm2ps             consul              grails              pjproject
    allure              dps8m               graphicsmagick      rancid
    appstream-glib      erlang              i2p                 ratfor
    byteman             flyway              jruby               stress-ng
    camlp5              folly               libgphoto2          zydis
    cargo-instruments   fonttools           libjwt
    cfengine            gmsh                mill
    clblast             gptfdisk            mongoose
    

    Cài đặt python 3

    brew install python
    

    Mặc định MacOS sử dụng python 2

    hieunv@HieuNV ~ % python -V
    Python 2.7.16
    hieunv@HieuNV ~ % python3 -V
    Python 3.7.0
    

    Các bạn cần thêm đoạn sau vào ~/.zshrc.

    export PATH="/usr/local/opt/python/libexec/bin:/usr/local/sbin:$PATH"
    

    Nếu đang mở Terminal bạn cần đóng Terminal lại rồi kiểm tra lại python version

    hieunv@HieuNV ~ % python -V
    Python 3.7.6
    hieunv@HieuNV ~ % pip -V
    pip 19.3.1 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
    

    Cài đặt virtualenvvirtualenvwrapper

    Với python các packages không được cài đặt cục bộ giống như node. Do đó chúng ta cần tạo ra các môi trường khác nhau với các packages khác nhau để sử dụng cho các dự án khác nhau.

    Cài đặt virtualenv

    pip install virtualenv
    

    Cài đặt virtualenvwrapper

    pip install virtualenvwrapper
    

    Activate virtualenv mỗi khi bật khởi động Terminal

    Các bạn thêm đoạn sau vào ~/.zshrc để virutalenv có thể dượcd khởi động mỗi khi bạn bật Terminal

    export WORKON_HOME=$HOME/.virtualenvs
    source /usr/local/bin/virtualenvwrapper.sh
    

    Tiến hành tạo môi trưởng ảo và cài đặt packages mong muốn

    • Tạo môi trường ảo
    hieunv@HieuNV ~ % mkvirtualenv a
    created virtual environment CPython3.7.6.final.0-64 in 515ms
      creator CPython3Posix(dest=/Users/hieunv/.virtualenvs/a, clear=False, global=False)
      seeder FromAppData(download=False, pip=latest, setuptools=latest, wheel=latest, via=copy, app_data_dir=/Users/hieunv/Library/Application Support/virtualenv/seed-app-data/v1)
      activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/predeactivate
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/postdeactivate
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/preactivate
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/postactivate
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/get_env_details
    (a) hieunv@HieuNV ~ %
    

    Các bạn để ý dòng cuối cùng (a). Sau khi tạo xong thì zsh đã activate vào môi trường ảo.

    • Active vào môi trường ảo nếu môi trường chưa được active thì có thể làm như sau:
    hieunv@HieuNV ~ % workon a
    (a) hieunv@HieuNV ~ %
    
    • Khi muốn thoát khỏi môi trường ảo thì có thể làm như sau:
    (a) hieunv@HieuNV ~ % deactivate
    hieunv@HieuNV ~ %
    
    • Để cài đặt packages thì tiến hành cài đặt bằng pip như bình thường
    pip install boto3
    

    Sử dụng môi trường ảo với python 2

    • Cài đặt python 2
    brew install python2
    
    • Tạo môi trường ảo sử dụng python bằng tham số -p
    mkvirtualenv py2 -p python2
    

    Export package list để install trên máy khác

    (a) hieunv@HieuNV ~ % pip freeze > requirements.txt
    (a) hieunv@HieuNV ~ % cat requirements.txt
    boto3==1.12.11
    botocore==1.15.11
    docutils==0.15.2
    jmespath==0.9.5
    python-dateutil==2.8.1
    s3transfer==0.3.3
    six==1.14.0
    urllib3==1.25.8
    

    Cài đặt packages sử dụng requirements.txt

    pip install -r requirements.txt
    

    Tài liệu tham khảo:

    • https://swapps.com/blog/how-to-configure-virtualenvwrapper-with-python3-in-osx-mojave/
  • HTTP vs HTTPS: Làm thế nào để website bảo mật hơn với SSL

    HTTP vs HTTPS: Làm thế nào để website bảo mật hơn với SSL

    Vào tháng 8 năm 2014, Google công bố sử dụng HTTPS để khắc phục những vẫn đề bảo mật mà phương thức HTTP đang gặp phải.

    Đối với hầu hết các công ty, việc Google khuyến cáo sử dụng HTTPS là lý do để thực hiện chuyển đổi sang giao thức đó, nhưng bản thân mỗi chúng ta cũng cần hiểu sự khác biệt giữa chúng – ưu và nhược điểm của HTTP và HTTPS. Tôi sẽ bắt đầu bằng việc giới thiệu tổng quan về giao thức HTTP và sau đó xem xét lý do tại sao Google muốn các trang web chuyển đổi sang sử dụng HTTPS.

    HTTP là gì, hoạt động như thế nào, và tại sao nó không được bảo mật?

    HTTP – HyperText Transfer Protocol, là một giao thức đã tồn tại hơn 15 năm, dùng để truyền tải thông tin qua Internet.

    Cũng như nhiều giao thức khác, HTTP hoạt động theo mô hình Client – Server. Trình duyệt web tạo request được gọi là Client, nơi nhận và phản hồi request đó là Server.

    Giả sử, bạn đang ngồi trong quán cafe và thử đăng nhập Facebook thông qua wifi của quán (giả định là Facebook đang dùng HTTP). Mạng wifi của quán là public, bất cứ ai kết nối với nó đều có thể truy cập dữ liệu đang được chuyển giao.

    Bây giờ chúng ta hãy xem những gì đang xảy ra với dữ liệu của bạn nếu như website sử dụng HTTP.

    Dữ liệu ở đây là bao gồm tất cả thông tin đăng nhập, mật khẩu, … cho tài khoản Facebook của bạn.

    Để đăng nhập vào Facebook, bạn cần nhập các thông tin như email, số điện thoại và mật khẩu. Ngay khi bạn nhấp vào nút đăng nhập, dữ liệu của bạn sẽ được gửi đến Server của Facebook. Server nhận dữ liệu, và xác thực nó. Nếu thông tin nhập là chính xác, Server sẽ gửi về HTTP status là “OK”, và bạn được đăng nhập vào tài khoản của mình. Mọi thứ có vẻ ez.

    Nhưng vấn để xảy ra ở đây là, nếu dữ liệu của bạn gửi lên server thông qua HTTP, thì nó sẽ không được mã hóa (HTTP không mã hóa dữ liệu) và vì vậy, bất kỳ dữ liệu nào được truyền thông qua giao thức HTTP đều có thể bị đánh cắp hoặc thay đổi từ bên thứ ba.

    Có thể bạn chưa bao giờ nghe tới Network sniffing attack, nhưng loại tấn công đó khá phổ biến.

    Sniffing attacks là một loại tấn công mà các Hacker sử dụng để lấy cắp thông tin “nhạy cảm” của bạn (vd: password, creadit card, users id, …).

    Để thực hiện việc này, các hacker thường sử dụng Sniffer – một chương trình có thể bắt được các gói tin truyền qua mạng.

    Sniffer là công cụ để phân tích và khắc phục các sự cố mạng thông qua việc bắt các gói tin, nhưng Hacker có thể lợi dụng điều đó để sử dụng chúng vào mục đích bất chính.

    Nếu các gói tin không được mã hóa, dữ liệu trong các gói tin này nó thể được lấy bởi một Sniffer. Sniffer sẽ phân tích và đọc nội dung gói tin, từ đó, các hacker đã có thể lấy được các thông tin private của bạn một cách dễ dàng.

    Như chúng ta đã thấy, HTTP có một điểm yếu chí cmn mạng – thông tin chuyển tới Server thông qua HTTP không được mã hóa. Điều đó, về mặt lý thuyết, có thể bị chặn bởi Hacker bất cứ lúc nào.

    Đối với những trang web thuần túy để đọc báo hay xem thông tin thì không có vấn đề gì lớn. Nhưng nó rất nguy hiểm khi bạn thực hiện các giao dịch trực tuyến, mà trong đó phải cung cấp các thông tin cá nhân quan trọng như giao dịch ngân hàng, mua sắm, …

    Tuy nhiên, điểm yếu đó của HTTP có thể được giải quyết dễ dàng bằng cách sử dụng 1 protocol khác – HTTPS.

    HTTPS ngoài Sniffing attacks ra cũng có thể bảo vệ bạn khỏi những kiểu tấn công khác như man-in-the-middle attacksDNS rebindingreplay attacks. Nhưng trong bài viết này, tôi chỉ để cập tới cách để HTTPS bảo vệ bạn khỏi Sniffing attacks đã nói ở trên thôi.

    HTTPS là gì và làm thế nào nó có thể bảo vệ website của bạn

    Giống như HTTP, HTTPS cũng là một giao thức giúp truyền thông tin giữa Client và Server. (Nó là một phiên bản của HTTP với thêm chứ “S” ở cuối – viết tắt của “Secure”). HTTPS bảo mật dữ liệu của bạn bằng cách sử dụng giao thức TSL (Transport Layer Security) hay còn gọi là SSL. Nhưng SSL là gì?

    SSL là tiêu chuẩn bảo mật cung cấp 3 lớp bảo vệ:

    • Mã hóa (Encryption): tất cả dữ liệu được gửi giữa browsers (Client) và Server đều được mã hóa. Nếu Hacker lấy được gói tin đó, cũng không thể giải mã được.
    • Toàn vẹn dữ liệu (Data integrity): Đảm bảo dữ liệu truyền đi không thể sửa đổi hoặc bị hỏng mà không bị phát hiện.
    • Xác thực (Authentication): Xác minh xem bạn thực sự đang giao tiếp với Server đã định hay không.

    Để ý một chút, khi truy cập website sử dụng HTTPS, trên url của bạn sẽ hiển thị ra chữ màu xanh như sau:

    Hoạt động của SSL

    SL sử dụng cái gọi là Public Key Cryptography hoặc hệ thống Public Key Infrastructure (PKI). Hệ thống PKI (key không đối xứng) sử dụng 2 key khác nhau để mã hóa thông tin: public key và private key. Bất cứ thứ gì được mã hóa bằng public key đều chỉ có thể giải mã bằng private key tương ứng và ngược lại.

    Lưu ý rằng, private key – giống như cái tên của nó, nên được bảo vệ kỹ và chỉ được truy cập bởi chính owner mà thôi. Với một trang web, private key phải được giữ an toàn trên Server. Nhưng ngược lại, public key lại được cấp phát công khai cho bất kỳ ai, và tất cả mọi người đều cần nó để giải mã thông tin đã được mã hóa trước đấy bằng private key. Bây giờ, chúng ta đã hiểu cách làm việc của cặp public key và private key, ta sẽ tiếp tục mô tả quá trình hoạt động SSL thông qua từng bước như sau.

    Giả sử bạn truy cập 1 website có sử dụng HTTPS:

    • Bước 1: Thiết lập một “giao tiếp” an toàn giữa Server và Client – còn được gọi là Handshake (bắt tay). Quá trình Handshake được bắt đầu khi browsers truy cập trang web thông qua url. Bằng cách request trên, Client sẽ khởi tạo kết nối SSL với Server cùng với thông tin về phiên bản và kiểu mã hóa. Việc Client gửi request và khởi tạo kết nối SSL được gọi là client hello.
    • Bước 2: Bước tiếp theo được gọi là server hello. Sau khi nhận được yêu cầu từ Client, Server trả về cho Client SSL certificate cùng với public key của nó. Hoàn tất quá trình chào hỏi xã giao.
    • Bước 3: Client nhận được dữ liệu từ Server, browser sẽ xác minh SSL certificate đó. Những certificate này được kiểm soát bởi các tổ chức bảo mật (Certificate Authority) như Symantec, Comodo, GoDaddy. SSL Certificate là một khối dữ liệu bao gồm nhiều thông tin về server như:
      • Tên domain.
      • Tên công ty sở hữu.
      • Thời gian certificate được cấp.
      • Thời hạn certificate.
      • Public key.
    • Bước 4: Sau khi xác minh xong, Browser sẽ sinh ra 1 Key - K và được mã hóa bởi public key nhận được từ bước 2. K sẽ được sử dụng để mã hóa tất cả dữ liệu truyền tải giữa Client và Server.
    • Bước 5: Do quá trình mã hóa dữ liệu sử dụng PKI (key đối xứng), nên Client cần gửi cho Server cái khóa K này để giải mã gói tin. Server sẽ dùng private key để giải mã gói tin này và lấy được thông tin về khóa K.
    • Bước 6: Từ đây, các thông tin truyền tải giữa Client và Server đều được mã hóa bằng khóa K. Khóa K này là unique và chỉ có hiệu lực trong thời gian Session đó tồn tại.

    Việc sử dụng HTTPS sẽ bảo vệ trang web của bạn tới những kiểu tấn công mà tôi đã đề cập trước đó. Bạn có authentication, bạn có thể biết rằng mình đang giao tiếp một cách an toàn với Server dự định. Dữ liệu của bạn được mã hóa encryption – ngay cả khi sniffer lấy được gói tin, cũng không thể giải mã được nội dung bên trong. Và tất nhiên bạn có được toàn vẹn dữ liệu data integrity, vì vậy bạn có thể truyền những dữ liệu nhạy cảm mà không cần lo lắng về việc nó bị hỏng hoặc sửa đổi mà không phát hiện ra.

    Bạn có nên sử dụng HTTPS

    Trước khi đưa ra quyết định sử dụng HTTPS thay cho HTTP, tôi sẽ tổng hợp những lợi ích chính nếu website của bạn sử dụng HTTPS:

    • Security: Tất cả thông tin truyền tải giữa Client và Server đều được mã hóa và xác minh. Điều này giúp bạn chống được một số kiểu tấn công của hacker như man-in-the-middle attacksDNS rebindingreplay attacks.
    • Trust: Người dùng sẽ cảm thấy an tâm với trang web của bạn hơn. HTTPS giúp xây dựng lòng tin với họ, nhất là khi web của bạn cung cấp các dịch vụ dính đến tiền (yaoming).
    • SEO: Việc sử dụng HTTPS sẽ tăng thứ hạng tìm kiếm trang web của bạn trên các search engine.

    Nguồn:

  • Hướng dẫn cài đặt yarn trên MacOS

    Hướng dẫn cài đặt yarn trên MacOS

    Trong bài viết này tôi sẽ hướng dẫn các bạn cài đặt và sử dụng yarn để quản lý node packages thay cho npm

    Cài đặt brew

    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    
    brew update && brew upgrade
    

    Cài đặt yarn

    brew install yarn
    

    Kiểm tra phiên bản yarn

    hieunv@HieuNV ~ % yarn -v
    1.22.0
    

    Tạo mới node project bằng yarn

    yarn init
    

    Thêm dependencies

    yarn add react
    

    Thêm devDependencies

    yarn add node-sass -D
    

    Xoá dependencies hoặc devDependencies

    yarn remove react
    
  • Sử dụng nhiều phiên bản node cùng lúc

    Sử dụng nhiều phiên bản node cùng lúc

    Khi sử dụng node nhiều bạn nghĩ rằng chỉ cần sử dụng phiên bản LTS mới nhất là đủ rồi. Tuy nhiên nếu bạn phải tham gia nhiều dự án cùng lúc (như mình chẳng hạn) thì mới thấy là chẳng dễ gì các dự án sống chung với nhau được. Nói vậy để thấy rằng dùng có thể dùng nhiều phiên bản cùng lúc là cần thiết. Trong bài viết này tôi sẽ hướng dẫn các bản cách sử dụng nhiều phiên bản node cùng lúc.

    nvm là gì?

    nvm là viết tắt của Node Version Manager. Như đã nói ở trên bài viết này sẽ hướng dẫn cách các bạn có thể sử dụng nhiều phiên bản node cùng lúc. Vậy thì nm là cần thiết nhỉ.

    Để cài đặt nvm bạn có thể sử dụng lệnh sau:

    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
    
    wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
    

    Tuỳ thuộc vào hệ điều hành bạn đang sử dụng thì cần thêm đoạn sau vào ~/.bash_profile hoặc ~/.profile hoặc ~/.bashrc hoặc ~/.zshrc. Mình đang sử dụng zsh nên mình sẽ thêm vào ~/.zshrc

    export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
    [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
    

    Kiểm tra xem nvm đã được cài thành công chưa

    nvm -v
    

    Giờ thì cài đặt phiên bản node mà muốn sử dụng thôi nào

    Cài đặt phiên bản mới nhất:

    nvm install node
    

    Cài đặt phiên bản xác định mà bạn muốn

    nvm install 10.10.0
    

    Để chuyển qua phiên bản node bản muốn sử dụng

    nvm use v13.9.0
    

    v13.9.0 là tên phiên bản các bạn nhìn thấy sau khi sử dụng nvm ls như ở trên.

    Chi tiết cách sử dụng nvm thì các bạn có thể tham khảo hướng dẫn ở link sau

    https://github.com/nvm-sh/nvm
    
  • Android Animation (Part 2)

    Android Animation (Part 2)

    Xin chào các bạn, bài tiếp theo trong series về animation của mình đó là FrameAnimation.

    Như ở bài trước mình đã nói thì FrameAnimation là khởi tạo một animation bằng cách sử dụng một chuỗi các hình ảnh được hiển thị theo một thứ tự nhất định với AnimationDrawable. Chúng ta cùng đi tìm hiểu về nó. Les’t go…

    Với định nghĩa về FrameAnimation bên trên thì chúng ta sẽ hình dung là trước tiên chúng ta cần tạo file xml để định nghĩa các frame.
    Bạn có thể tham khảo file numeric_animation.xml của tôi dưới đây:

     <!-- Animation frames are ic_numeric_0.png through ic_numeric_2.png
         files inside the res/drawable/ folder -->
     <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/selected"
        android:oneshot="false">
        <item
            android:drawable="@drawable/ic_numeric_0"
            android:duration="1000" />
        <item
            android:drawable="@drawable/ic_numeric_1"
            android:duration="1000" />
        <item
            android:drawable="@drawable/ic_numeric_2"
            android:duration="1000" />
    </animation-list>

    syntax:

    <?xml version="1.0" encoding="utf-8"?>
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
        android:oneshot=["true" | "false"] >
        <item
            android:drawable="@[package:]drawable/drawable_resource_name"
            android:duration="integer" />
    </animation-list>

    element:
    <animation-list>: là thẻ bắt buộc, nó chứa một hoặc nhiều thẻ <item>.
    android:oneshot: kiểu giá trị kiểu Boolean. “true” là khi bạn muốn thực hiện 1 lần duy nhất. “false” là khi bạn muốn lặp lại.
    <item>: là 1 item trong danh sách, bạn hiểu nó là 1 khung hình khi chạy.
    android:drawable: Drawable của bạn.
    android:duration: kiểu giá trị Integer, nó là thời lượng hiển thị được tính bằng milliseconds.
    Chạy nào, chạy nào…

    private lateinit var numericAnimation: AnimationDrawable
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            // Load the ImageView that will host the animation and
            // set its background to our AnimationDrawable XML resource.
            findViewById<ImageView>(R.id.numeric_image).apply {
                setBackgroundResource(R.drawable.numeric_animation)
                // Get the background, which has been compiled to an AnimationDrawable object.
                numericAnimation = background as AnimationDrawable
                // Start the animation (looped playback by default).
                numericAnimation.start()
            }
    
        }

    Ở đây thì bạn cũng thấy rằng trên màn hình main của tôi sẽ có 1 ImageView. Sau đó tôi sẽ chạy animation với AnimationDrawable đã được định nghĩa trong numeric_animation.xml.

    FrameAnimation


    Thật đơn giản phải không các bạn? 😀
    Nhưng nó có nhược điểm khi bạn sử dụng nhiều ảnh với animation có độ phức tạp cao sẽ làm tăng kích thước của ứng dụng, hay là bạn sẽ mất khá nhiều thời gian để custom lại animation đó cho phù hợp với bài toán của bạn. Để giải quyết vấn đề này, bài tiếp theo tôi sẽ giới thiệu cho các bạn về AnimatedVectorDrawable, các bạn hãy đón chờ nhé 😀

    Link tham khảo:

    • https://developer.android.com/guide/topics/graphics/drawable-animation