Trang chủ > Phát triển di động > Nội dung chính

Xử lý bất đồng bộ trong phát triển Android và iOS (ba) —— Hợp tác giữa nhiều tác vụ bất đồng bộ


Bài viết này là phần tiếp theo của loạt bài viết về: Xử lý bất đồng bộ trong phát triển Android và iOS. Trong phần thứ ba của tác phẩm này789 Club, chúng ta sẽ tập trung những vấn đề liên quan có thể xảy ra khi thực hiện đồng thời nhiều tác vụ bất đồng bộ. Việc quản lý và xử lý các tác vụ như vậy thường đòi hỏi kỹ năng đặc biệt để tránh những rủi ro tiềm ẩn mà nó mang lại.

Thông thườngbacarat, chúng ta cần thực hiện nhiều tác vụ bất đồng bộ và khiến chúng phối hợp với nhau để hoàn thành yêu cầu. Bài viết này sẽ dựa trên các tình huống sử dụng điển hình, giải thích ba mối quan hệ phối hợp chính giữa các tác vụ bất đồng bộ: 1. **Phe phẩy cùng một mái chèo**: Khi các tác vụ phải đợi lẫn nhau trước khi tiếp tục. 2. **Đồng hành cùng nhau**: Khi các tác vụ chạy đồng thời nhưng kết quả của chúng phụ thuộc vào nhau. 3. **Chia sẻ tài nguyên hiệu quả**: Khi các tác vụ sử dụng chung dữ liệu mà không gây xung đột hoặc lỗi trong quá trình vận hành. Bằng cách hiểu rõ những mối quan hệ này, bạn có thể tối ưu hóa quy trình xử lý tác vụ bất đồng bộ trong ứng dụng của mình.

  • Thực hiện tuần tự sau nhau.
  • Thực hiện đồng thời và hợp nhất kết quả.
  • Thực hiện đồng thời với một bên ưu tiên.

Ba mối quan hệ cộng tác trên sẽ được thảo luận chi tiết qua ba ứng dụng cụ thể. Ba ứng dụng cụ thể đó là:

  • Cache đa cấp.
  • Yêu cầu mạng đồng thời.
  • Cache trang.

yêu cầu mạng song song

Lưu ý: Mã nguồn trong loạt bài viết này đã được tổ chức trên GitHub (liên tục cập nhật)i9bet.com nhận 100k, đường dẫn kho lưu trữ mã là:

Trong bài viết này789 Club, mã nguồn Java được đề cập nằm trong gói có tên là com. demos.async. multitask. Đây là nơi mà các đoạn code liên quan đến lập trình đa nhiệm và xử lý bất đồng bộ được tổ chức một cách logic và có hệ thống.


Nhiều nhiệm vụ bất đồng bộ thực hiện tuần tự sau nhau.

Thực hiện theo thứ tự nối tiếp

Một ví dụ điển hình là việc sử dụng đa cấp bộ nhớ đệm cho tài nguyên tĩnh789 Club, trong đó điều được nhiều người yêu thích nhất chính là việc sử dụng bộ nhớ đệm đa cấp cho hình ảnh tĩnh. Khi tải một hình ảnh tĩnh trên nền tảng client, thông thường sẽ có ít nhất hai cấp bộ nhớ đệm: cấp bộ nhớ đầu tiên là Cache bộ nhớ (Memory Cache) và cấp thứ hai là Cache ổ cứng (Disk Cache). Quy trình tải hình ảnh sẽ diễn ra như sau: Trước tiên, hệ thống sẽ kiểm tra xem liệu hình ảnh cần tải có đang tồn tại trong Cache bộ nhớ hay không. Nếu có, dữ liệu sẽ được truy xuất ngay lập tức từ bộ nhớ RAM, giúp giảm thiểu thời gian chờ đợi đáng kể so với việc đọc trực tiếp từ ổ cứng hoặc mạng. Tiếp theo, nếu hình ảnh không có sẵn trong Cache bộ nhớ, hệ thống sẽ chuyển sang kiểm tra Cache ổ cứng. Ở cấp độ này, dữ liệu đã được lưu trữ trước đó sẽ được trích xuất nhanh chóng thay vì phải tải lại từ nguồn gốc ban đầu. Việc sử dụng bộ nhớ đệm đa cấp này không chỉ làm tăng hiệu suất mà còn giúp tiết kiệm băng thông mạng và giảm tải cho máy chủ, từ đó cải thiện trải nghiệm người dùng một cách đáng kể.

  1. Đầu tiên kiểm tra cache bộ nhớbacarat, nếu trúng, trả về trực tiếp; nếu không, tiến hành bước tiếp theo.
  2. Tiếp tục kiểm tra cache đĩai9bet.com nhận 100k, nếu trúng, trả về trực tiếp; nếu không, tiến hành bước tiếp theo.
  3. Phát yêu cầu mạng để tải xuống và giải mã tệp hình ảnh.

thực hiện tuần tự sau khi hoàn thành

Tìm kiếm Disk Cache

Trước tiênbacarat, chúng ta cần xác định rõ giao diện cho hai tác vụ bất đồng bộ là "Bộ đệm Disk" và "Yêu cầu mạng". Việc này sẽ giúp chúng ta có một nền tảng vững chắc để quản lý và điều phối các hoạt động sau này.

								
									
										public
									 interface
									 ImageDiskCache
									 {
									
    /** * Lấy bất đồng bộ đối tượng Bitmap từ bộ nhớ cache. * @param key Khóa để truy xuất dữ liệu trong bộ nhớ cache. * @param callback Hàm trả về đối tượng Bitmap được lưu trữ trong bộ nhớ cache. */
    void
									 getImage
									(
									String
									 key
									,
									 AsyncCallback
									<
									Bitmap
									>
									 callback
									);
									
    /** * Lưu đối tượng Bitmap vào bộ nhớ đệm. * @param key Chuỗi duy nhất để xác định vị trí lưu trữ trong bộ nhớ đệm. * @param bitmap Đối tượng Bitmap cần được lưu giữ. * @param callback Hàm trả về kết quả hiện tại của thao tác lưu trữ789 Club, cho biết việc lưu thành công hay thất bại. */
    void
									 putImage
									(
									String
									 key
									,
									 Bitmap
									 bitmap
									,
									 AsyncCallback
									<
									Boolean
									>
									 callback
									);
									
}
									

								

Giao diện ImageDiskCache được sử dụng để lưu trữ và truy xuất Cache của hình ảnh trên đĩai9bet.com nhận 100k, trong đó tham số AsyncCallback là định nghĩa của một giao diện callback bất đồng bộ phổ quát. Mã định nghĩa của nó như sau (giao diện này sẽ còn được đề cập đến trong phần tiếp theo của bài viết): ```java public interface AsyncCallback{ void onSuccess(T result); void onFailure(Throwable error); } ``` Trong đóbacarat, phương thức `onSuccess` sẽ được gọi khi tác vụ hoàn thành thành công và trả về kết quả, trong khi `onFailure` sẽ được kích hoạt nếu có lỗi xảy ra trong quá trình thực hiện nhiệm vụ.

								
									/**<d>Loại dữ liệu tham số trả về từ giao diện bất đồng bộ.</d>
public
									 interface
									 AsyncCallback
									 <
									D
									>
									 {
									
    void
									 onResult
									(
									D
									 data
									);
									
}
									

								

Còn việc tải xuống tệp hình ảnh qua mạng789 Club, chúng ta gọi trực tiếp bài viết trước đó về: Xử lý bất đồng bộ trong phát triển Android và iOS (hai) —— Quay lại tác vụ bất đồng bộ Trong phần giới thiệu về giao diện Downloader (chú ý: phiên bản của giao diện Downloader có tham số contextData ở cuối)i9bet.com nhận 100k, bạn có thể tìm hiểu thêm về cách sử dụng nó một cách hiệu quả.

Phát yêu cầu tải xuống mạng

								
									// Kiểm tra cache cấp hai: cache đĩa
    imageDiskCache
									.
									getImage
									(
									url
									,
									 new
									 AsyncCallback
									<
									Bitmap
									>()
									 {
									
        @Override
									
        public
									 void
									 onResult
									(
									Bitmap
									 bitmap
									)
									 {
									
            if
									 (
									bitmap
									 !=
									 null
									)
									 {
									
                // Cache đĩa trúngi9bet.com nhận 100k, nhiệm vụ tải xuống kết thúc sớm.
                imageMemCache
									.
									putImage
									(
									url
									,
									 bitmap
									);
									
                successCallback
									(
									url
									,
									 bitmap
									,
									 contextData
									);
									
            }
									
            else
									 {
									
                // Cả hai cấp cache đều không trúngi9bet.com nhận 100k, gọi trình tải xuống để tải xuống
                downloader
									.
									startDownload
									(
									url
									,
									 getLocalPath
									(
									url
									),
									 contextData
									);
									
            }
									
        }
									
    });
									

								

Mã ví dụ cho hàm callback kết quả thành công của Downloader như sau:

								
									@Override
									
    public
									 void
									 downloadSuccess
									(
									final
									 String
									 url
									,
									 final
									 String
									 localPath
									,
									 final
									 Object
									 contextData
									)
									 {
									
        // Giải mã hình ảnhi9bet.com nhận 100k, đây là thao tác tốn thời gian, thực hiện bất đồng bộ.
        imageDecodingExecutor
									.
									execute
									(
									new
									 Runnable
									()
									 {
									
            @Override
									
            public
									 void
									 run
									()
									 {
									
                final
									 Bitmap
									 bitmap
									 =
									 decodeBitmap
									(
									new
									 File
									(
									localPath
									));
									
                // Điều độ lại về luồng chính.
                mainHandler
									.
									post
									(
									new
									 Runnable
									()
									 {
									
                    @Override
									
                    public
									 void
									 run
									()
									 {
									
                        if
									 (
									bitmap
									 !=
									 null
									)
									 {
									
                            imageMemCache
									.
									putImage
									(
									url
									,
									 bitmap
									);
									
                            imageDiskCache
									.
									putImage
									(
									url
									,
									 bitmap
									,
									 null
									);
									
                            successCallback
									(
									url
									,
									 bitmap
									,
									 contextData
									);
									
                        }
									
                        else
									 {
									
                            // Giải mã thất bại
                            failureCallback
									(
									url
									,
									 ImageLoaderListener
									.
									BITMAP_DECODE_FAILED
									,
									 contextData
									);
									
                        }
									
                    }
									
                });
									
            }
									
        });
									
    }
									

								

Nhiều nhiệm vụ bất đồng bộ thực hiện đồng thời và hợp nhất kết quả.

Thực thi đồng thờii9bet.com nhận 100k, hợp nhất kết quả

Một ví dụ điển hình là khi cùng lúc gửi nhiều yêu cầu mạng (hay còn gọi là giao diện API từ xa)789 Club, sau đó chỉ khi nhận được tất cả các phản hồi từ những yêu cầu đó, mới tiến hành xử lý dữ liệu và cập nhật giao diện người dùng. Cách làm này giúp giảm thời gian tổng thể của quá trình yêu cầu bằng cách tận dụng việc gửi đồng thời các yêu cầu mạng. Thêm vào đó, phương pháp này không chỉ tối ưu hóa hiệu suất mà còn mang lại trải nghiệm mượt mà hơn cho người dùng, đặc biệt trong các ứng dụng cần xử lý khối lượng lớn dữ liệu.

Chúng tôi đưa ra ví dụ mã cho trường hợp đơn giản nhất của hai yêu cầu mạng đồng thời.

Trước tiên789 Club, vẫn cần phải định nghĩa trước các giao diện bất đồng bộ cần thiết, tức là định nghĩa giao diện API từ xa.

								
									/**
public
									 interface
									 HttpService
									 {
									
    /** * Gửi yêu cầu HTTP. * @param apiUrl Đường URL của yêu cầu * @param request Đối tượng tham số yêu cầu (được biểu diễn bằng Java Bean) * @param listener Bộ xử lý sự kiện * @param contextData Tham số truyền tiếp * @param */<t>Kiểu mô hình yêu cầu<r>Kiểu mô hình phản hồi</r></t>
    <
									T
									,
									 R
									>
									 void
									 doRequest
									(
									String
									 apiUrl
									,
									 T
									 request
									,
									 HttpListener
									<?
									 super
									 T
									,
									 R
									>
									 listener
									,
									 Object
									 contextData
									);
									
}
									

/**<t>Kiểu mô hình yêu cầu<r>Kiểu mô hình phản hồi</r></t>
public
									 interface
									 HttpListener
									 <
									T
									,
									 R
									>
									 {
									
    /** * Giao diện callback được kích hoạt khi nhận được kết quả yêu cầu (thành công hoặc thất bại). * @param apiUrl Đường URL của yêu cầu * @param request Mô hình yêu cầu (request model) * @param result Kết quả của yêu cầu (bao gồm phản hồi hoặc lý do lỗi) * @param contextData Tham số truyền qua để giữ ngữ cảnh */
    void
									 onResult
									(
									String
									 apiUrl
									,
									 T
									 request
									,
									 HttpResult
									<
									R
									>
									 result
									,
									 Object
									 contextData
									);
									
}
									

								

Điều cần lưu ý là: trong giao diện HttpServicei9bet.com nhận 100k, tham số yêu cầu request được định nghĩa bằng kiể Nếu có một thực hiện cho giao diện này, thì trong mã thực hiện sẽ có sự chuyển đổi dựa trên loại thực tế của request (nó có thể là bất kỳ Java Bean nào) thành các tham số yêu cầu HTTP thông qua cơ chế phản chiếu (reflection). Tất nhiên, ở đây chúng ta chỉ tập trung vào việc phân tích giao diện, còn việc triển khai cụ thể không phải là trọng tâm trong phạm vi thảo luận này. Ngoài ra, khi nói đến việc sử dụng kiểu generic trong Java, điều này mang lại tính linh hoạt cao cho lập trình viên. Bằng cách sử dụng kiểu generic, bạn có thể viết các phương thức hoặc lớp mà không bị ràng buộc bởi một kiểu dữ liệu cụ thể. Điều này giúp giảm thiểu việc lặp lại mã và tăng cường khả năng tái sử dụng. Vì vậy, việc sử dụng request dưới dạng generic T trong giao diện HttpService không chỉ giúp tối ưu hóa mã nguồn mà còn tạo điều kiện thuận lợi cho việc mở rộng trong tương lai.

Tham số kết quả trả về result có kiểu HttpResulti9bet.com nhận 100k, điều này giúp nó có thể biểu thị cả kết quả phản hồi thành công lẫn kết quả phản hồi thất bại. Định nghĩa của HttpResult như sau:

								
									/** * Lớp HttpResult được sử dụng để đóng gói kết quả của một yêu cầu HTTP. * Khi máy chủ phản hồi thành côngi9bet.com nhận 100k, mã lỗi (errorCode) sẽ là SUCCESS và phản hồi từ máy chủ sẽ được chuyển đổi thành đối tượng response; * Ngược lại, khi máy chủ không thể phản hồi đúng cách, mã lỗi (errorCode) sẽ khác SUCCESS và giá trị của biến response sẽ không hợp lệ. * @param */Kiểu mô hình phản hồi
public
									 class
									 HttpResult
									 <
									R
									>
									 {
									
    /**
    public
									 static
									 final
									 int
									 SUCCESS
									 =
									 0
									;
									// Thành công
    public
									 static
									 final
									 int
									 REQUEST_ENCODING_ERROR
									 =
									 1
									;
									// Lỗi khi mã hóa yêu cầu
    public
									 static
									 final
									 int
									 RESPONSE_DECODING_ERROR
									 =
									 2
									;
									// Lỗi khi giải mã phản hồi
    public
									 static
									 final
									 int
									 NETWORK_UNAVAILABLE
									 =
									 3
									;
									// Mạng không khả dụng
    public
									 static
									 final
									 int
									 UNKNOWN_HOST
									 =
									 4
									;
									// Tên miền không thể phân giải
    public
									 static
									 final
									 int
									 CONNECT_TIMEOUT
									 =
									 5
									;
									// Kết nối quá hạn
    public
									 static
									 final
									 int
									 HTTP_STATUS_NOT_OK
									 =
									 6
									;
									// Yêu cầu tải xuống trả về khác 200
    public
									 static
									 final
									 int
									 UNKNOWN_FAILED
									 =
									 7
									;
									// Lỗi không xác định khác

    private
									 int
									 errorCode
									;
									
    private
									 String
									 errorMessage
									;
									
    /** * response đại diện cho phản hồi được trả về từ máy chủ. * Chỉ khi errorCode bằng với SUCCESSi9bet.com nhận 100k, giá trị của response mới mang ý nghĩa và có thể được sử dụng. */
    private
									 R
									 response
									;
									

    public
									 int
									 getErrorCode
									()
									 {
									
        return
									 errorCode
									;
									
    }
									

    public
									 void
									 setErrorCode
									(
									int
									 errorCode
									)
									 {
									
        this
									.
									errorCode
									 =
									 errorCode
									;
									
    }
									

    public
									 String
									 getErrorMessage
									()
									 {
									
        return
									 errorMessage
									;
									
    }
									

    public
									 void
									 setErrorMessage
									(
									String
									 errorMessage
									)
									 {
									
        this
									.
									errorMessage
									 =
									 errorMessage
									;
									
    }
									

    public
									 R
									 getResponse
									()
									 {
									
        return
									 response
									;
									
    }
									

    public
									 void
									 setResponse
									(
									R
									 response
									)
									 {
									
        this
									.
									response
									 =
									 response
									;
									
    }
									
}
									

								

Kết quả HttpResult cũng bao gồm một kiểu Generic Ri9bet.com nhận 100k, đây chính là loại tham số phản hồi được trả về khi yêu cầu thành công. Tương tự, trong việc triển khai có thể xảy ra của HttpService, cơ chế phản chiếu (reflection) sẽ lại được sử dụng để biến đổi nội dung phản hồi từ yêu cầu (có thể là một chuỗi JSON) thành kiểu R (nó có thể là bất kỳ đối tượng Java nào). Điều này cho phép hệ thống linh hoạt hơn trong việc xử lý các loại dữ liệu khác nhau mà không cần ràng buộc chặt chẽ vào một định dạng cụ thể.

Rồii9bet.com nhận 100k, hiện tại với sự tồn tại của giao diện HttpService, chúng ta có thể trình diễn cách gửi đồng thời hai yêu cầu mạng. Chỉ cần tưởng tượng hai luồng dữ liệu đang chạy song song trên mạng internet, mỗi luồng đều mang theo thông điệp riêng biệt nhưng cùng lúc được kích hoạt từ chính ứng dụng của chúng ta.

								
									
										public
									 class
									 MultiRequestsDemoActivity
									 extends
									 AppCompatActivity
									 {
									
    private
									 HttpService
									 httpService
									 =
									 new
									 MockHttpService
									();
									
    /**
    private
									 Map
									<
									String
									,
									 Object
									>
									 httpResults
									 =
									 new
									 HashMap
									<
									String
									,
									 Object
									>();
									

    @Override
									
    protected
									 void
									 onCreate
									(
									Bundle
									 savedInstanceState
									)
									 {
									
        super
									.
									onCreate
									(
									savedInstanceState
									);
									
        setContentView
									(
									R
									.
									layout
									.
									activity_multi_requests_demo
									);
									

        // Đồng thời phát hai yêu cầu bất đồng bộ
        httpService
									.
									doRequest
									(
									"http://..."
									,
									 new
									 HttpRequest1
									(),
									
                new
									 HttpListener
									<
									HttpRequest1
									,
									 HttpResponse1
									>()
									 {
									
                    @Override
									
                    public
									 void
									 onResult
									(
									String
									 apiUrl
									,
									
                                         HttpRequest1
									 request
									,
									
                                         HttpResult
									<
									HttpResponse1
									>
									 result
									,
									
                                         Object
									 contextData
									)
									 {
									
                        // Đưa kết quả yêu cầu vào bộ nhớ cache
                        httpResults
									.
									put
									(
									"request-1"
									,
									 result
									);
									
                        if
									 (
									checkAllHttpResultsReady
									())
									 {
									
                            // Hai yêu cầu đã kết thúc
                            HttpResult
									<
									HttpResponse1
									>
									 result1
									 =
									 result
									;
									
                            HttpResult
									<
									HttpResponse2
									>
									 result2
									 =
									 (
									HttpResult
									<
									HttpResponse2
									>)
									 httpResults
									.
									get
									(
									"request-2"
									);
									
                            if
									 (
									checkAllHttpResultsSuccess
									())
									 {
									
                                // Hai yêu cầu đã thành công
                                processData
									(
									result1
									.
									getResponse
									(),
									 result2
									.
									getResponse
									());
									
                            }
									
                            else
									 {
									
                                // Hai yêu cầu chưa hoàn toàn thành côngi9bet.com nhận 100k, xử lý như thất bại
                                processError
									(
									result1
									.
									getErrorCode
									(),
									 result2
									.
									getErrorCode
									());
									
                            }
									
                        }
									
                    }
									
                },
									
                null
									);
									
        httpService
									.
									doRequest
									(
									"http://..."
									,
									 new
									 HttpRequest2
									(),
									
                new
									 HttpListener
									<
									HttpRequest2
									,
									 HttpResponse2
									>()
									 {
									
                    @Override
									
                    public
									 void
									 onResult
									(
									String
									 apiUrl
									,
									
                                         HttpRequest2
									 request
									,
									
                                         HttpResult
									<
									HttpResponse2
									>
									 result
									,
									
                                         Object
									 contextData
									)
									 {
									
                        // Đưa kết quả yêu cầu vào bộ nhớ cache
                        httpResults
									.
									put
									(
									"request-2"
									,
									 result
									);
									
                        if
									 (
									checkAllHttpResultsReady
									())
									 {
									
                            // Hai yêu cầu đã kết thúc
                            HttpResult
									<
									HttpResponse1
									>
									 result1
									 =
									 (
									HttpResult
									<
									HttpResponse1
									>)
									 httpResults
									.
									get
									(
									"request-1"
									);
									
                            HttpResult
									<
									HttpResponse2
									>
									 result2
									 =
									 result
									;
									
                            if
									 (
									checkAllHttpResultsSuccess
									())
									 {
									
                                // Hai yêu cầu đã thành công
                                processData
									(
									result1
									.
									getResponse
									(),
									 result2
									.
									getResponse
									());
									
                            }
									
                            else
									 {
									
                                // Hai yêu cầu chưa hoàn toàn thành công789 Club, xử lý như thất bại
                                processError
									(
									result1
									.
									getErrorCode
									(),
									 result2
									.
									getErrorCode
									());
									
                            }
									
                        }
									
                    }
									
                },
									
                null
									);
									
    }
									

    /** * Kiểm tra xem tất cả các yêu cầu đã có kết quả chưa * @return */
    private
									 boolean
									 checkAllHttpResultsReady
									()
									 {
									
        int
									 requestsCount
									 =
									 2
									;
									
        for
									 (
									int
									 i
									 =
									 1
									;
									 i
									 <=
									 requestsCount
									;
									 i
									++)
									 {
									
            if
									 (
									httpResults
									.
									get
									(
									"request-"
									 +
									 i
									)
									 ==
									 null
									)
									 {
									
                return
									 false
									;
									
            }
									
        }
									
        return
									 true
									;
									
    }
									

    /** * Kiểm tra xem tất cả các yêu cầu đã thành công hay chưa * @return */
    private
									 boolean
									 checkAllHttpResultsSuccess
									()
									 {
									
        int
									 requestsCount
									 =
									 2
									;
									
        for
									 (
									int
									 i
									 =
									 1
									;
									 i
									 <=
									 requestsCount
									;
									 i
									++)
									 {
									
            HttpResult
									<? > result
									 =
									 (
									HttpResult
									<? >) httpResults
									.
									get
									(
									"request-"
									 +
									 i
									);
									
            if
									 (
									result
									 ==
									 null
									 ||
									 result
									.
									getErrorCode
									()
									 !=
									 HttpResult
									.
									SUCCESS
									)
									 {
									
                return
									 false
									;
									
            }
									
        }
									
        return
									 true
									;
									
    }
									

    private
									 void
									 processData
									(
									HttpResponse1
									 data1
									,
									 HttpResponse2
									 data2
									)
									 {
									
        // TODO: Cập nhật giao diện người dùngbacarat, hiển thị kết quả yêu cầu. Mã bỏ qua ở đây
    }
									

    private
									 void
									 processError
									(
									int
									 errorCode1
									,
									 int
									 errorCode2
									)
									 {
									
        // TODO: Cập nhật giao diện người dùngbacarat, hiển thị lỗi. Mã bỏ qua ở đây
    }
									
}
									

								

Trước tiêni9bet.com nhận 100k, chúng ta cần phải đợi cho đến khi cả hai yêu cầu hoàn tất trước khi có thể hợp nhất kết quả của chúng. Để xác định xem cả hai yêu cầu bất đồng bộ đã hoàn thành chưa, chúng ta cần kiểm tra trạng thái hoàn tất của tất cả các yêu cầu mỗi khi một trong những yêu cầu nhận được phản hồi từ callback. Điều quan trọng cần lưu ý ở đây là lý do chúng ta có thể áp dụng phương pháp kiểm tra này nằm ở một giả định cơ bản: phương thức onResult của HttpService đã được lên lịch để thực hiện trên luồng chính (main thread). Trong bài viết trước của chúng ta, " Xử lý bất đồng bộ trong phát triển Android và iOS (hai) —— Quay lại tác vụ bất đồng bộ Trong phần “mô hình luồng của hàm callback” trong tài liệu đã đề cập đến môi trường luồng nơi callback xảy ra. Giả sử rằng hai hàm onResult của hai yêu cầu đã được lên lịch để chạy trên luồng chínhbacarat, thì thứ tự thực thi của hai hàm onResult chỉ có thể xuất hiện theo hai kịch bản: hoặc là hàm onResult của yêu cầu đầu tiên sẽ được thực thi trước, sau đó là hàm onResult của yêu cầu thứ hai; hoặc ngược lại, hàm onResult của yêu cầu thứ hai sẽ được thực thi trước, tiếp đến là yêu cầu đầu tiên. Dù cho thứ tự nào xảy ra đi chăng nữa, các điều kiện và logic bên trong phần mã nguồn của hàm onResult vẫn luôn hợp lệ và hoạt động đúng như mong đợi.

Tuy nhiên789 Club, nếu phương thức onResult của HttpService được thực thi trên các luồng khác nhau, hai callback onResult của các yêu cầu có thể chạy xen kẽ vào nhau. Khi đó, các phép kiểm tra và xử lý bên trong sẽ gặp phải vấn đề về đồng bộ, dẫn đến kết quả không như mong muốn hoặc thậm chí gây ra lỗi trong quá trình xử lý.

So với việc thực hiện tuần tự mà chúng ta đã đề cập trước đó789 Club, cách thực thi song song ở đây rõ ràng làm tăng đáng kể độ phức tạp. Nếu không có yêu cầu đặc biệt mạnh mẽ về hiệu suất mà việc thực thi đồng thời mang lại, có lẽ chúng ta sẽ ưu tiên lựa chọn phương thức thực thi tuần tự để giữ cho logic mã nguồn vẫn đơn giản và dễ hiểu. Thực tế, trong nhiều trường hợp, sự đơn giản luôn là ưu tiên hàng đầu. Khi các đoạn mã được thực hiện tuần tự, việc theo dõi dòng chảy logic trở nên dễ dàng hơn rất nhiều. Ngược lại, khi áp dụng thực thi song song, chúng ta cần phải cân nhắc thêm nhiều yếu tố như xung đột tài nguyên, đồng bộ hóa giữa các luồng, và thậm chí cả vấn đề bảo trì lâu dài. Điều này đôi khi khiến việc quản lý mã nguồn trở nên khó khăn hơn so với mong đợi ban đầu. Tuy nhiên, nếu hiệu suất thực sự là một yếu tố quan trọng và không thể bỏ qua, thì việc chấp nhận độ phức tạp cao hơn cũng là một sự đánh đổi cần thiết. Điều quan trọng là phải hiểu rõ những thách thức tiềm ẩn và chuẩn bị sẵn sàng để đối phó với chúng một cách hiệu quả.

Nhiều nhiệm vụ bất đồng bộ thực hiện đồng thời với một bên ưu tiên

Thực thi đồng thời với ưu tiên cho một bên

Một ví dụ điển hình là việc sử dụng bộ nhớ đệ Ví dụbacarat, một trang cần hiển thị một danh sách dữ liệu động. Nếu mỗi lần mở trang đều chỉ tải danh sách đó từ máy chủ, thì trong trường hợp không có kết nối mạng hoặc mạng chậm, trang sẽ bị trắng màn hình trong thời gian dài. Điều này thường không phải là lựa chọn tốt, vì thay vào đó việc hiển thị dữ liệu cũ vẫn tốt hơn là không hiển thị gì cả. Do đó, chúng ta thường nghĩ đến việc thêm một lớp bộ nhớ đệm cục bộ để lưu trữ dữ liệu danh sách một cách vĩnh viễn. Điều này giúp cải thiện đáng kể trải nghiệm người dùng và giảm tải cho máy chủ, đặc biệt là khi mạng không ổn định. Hơn nữa, với sự phát triển của công nghệ, việc tích hợp bộ nhớ đệm cục bộ cũng trở nên dễ dàng hơn bao giờ hết.

Cache cục bộ cũng là một nhiệm vụ bất đồng bộbacarat, mã giao diện định nghĩa như sau:

								
									
										public
									 interface
									 LocalDataCache
									 {
									
    /** * Lấy đồng bộ đối tượng HttpResponse từ bộ nhớ cache địa phương. * @param key Khóa để truy xuất dữ liệu trong bộ nhớ cache * @param callback Hàm trả về đối tượng được lưu trữ trong bộ nhớ cache */
    void
									 getCachingData
									(
									String
									 key
									,
									 AsyncCallback
									<
									HttpResponse
									>
									 callback
									);
									

    /** * Lưu đối tượng HttpResponse vào bộ nhớ đệm. * @param key Chuỗi duy nhất để xác định vị trí của đối tượng trong bộ nhớ đệm. * @param data Đối tượng HttpResponse cần được lưu trữ. * @param callback Hàm trả về kết quả hoạt động lưu trữbacarat, cho biết liệu thao tác đã thành công hay không. */
    void
									 putCachingData
									(
									String
									 key
									,
									 HttpResponse
									 data
									,
									 AsyncCallback
									<
									Boolean
									>
									 callback
									);
									
}
									

								

Dữ liệu mà bộ nhớ cache cục bộ này đang lưu trữ chính là một đối tượng HttpResponse mà trước đó đã được lấy từ máy chủ. Giao diện callback bất đồng bộ AsyncCallbackbacarat, chúng ta đã thảo luận về nó ở phần trước. Ngoài ra, khi sử dụng bộ nhớ cache này, bạn cần đặc biệt chú ý đến thời gian sống của dữ liệu để đảm bảo thông tin luôn được cập nhật và chính xác. Việc kiểm soát chặt chẽ quá trình tải lại dữ liệu từ máy chủ cũng đóng vai trò quan trọng trong việc tối ưu hóa hiệu suất của ứng dụng.

Khi trang web được mở rabacarat, chúng ta có thể khởi động đồng thời nhiệm vụ đọc từ bộ nhớ đệm cục bộ và nhiệm vụ gửi yêu cầu đến API từ xa. Trong đó, nhiệm vụ thứ hai này sẽ có ưu tiên cao hơn so với nhiệm vụ đầu tiên. Điều này giúp đảm bảo rằng dữ liệu quan trọng nhất sẽ được tải xuống nhanh chóng trước tiên, trong khi việc đọc từ bộ nhớ đệm vẫn diễn ra song song để tối ưu hóa hiệu suất tổng thể. Điều này đặc biệt hữu ích khi người dùng cần nhận thông tin kịp thời mà không phải chờ đợi quá lâu.

								
									
										public
									 class
									 PageCachingDemoActivity
									 extends
									 AppCompatActivity
									 {
									
    private
									 HttpService
									 httpService
									 =
									 new
									 MockHttpService
									();
									
    private
									 LocalDataCache
									 localDataCache
									 =
									 new
									 MockLocalDataCache
									();
									
    /**
    private
									 boolean
									 dataFromHttpReady
									;
									

    @Override
									
    protected
									 void
									 onCreate
									(
									Bundle
									 savedInstanceState
									)
									 {
									
        super
									.
									onCreate
									(
									savedInstanceState
									);
									
        setContentView
									(
									R
									.
									layout
									.
									activity_page_caching_demo
									);
									

        // Đồng thời phát yêu cầu dữ liệu cục bộ và yêu cầu HTTP từ xa
        final
									 String
									 userId
									 =
									 "xxx"
									;
									
        localDataCache
									.
									getCachingData
									(
									userId
									,
									 new
									 AsyncCallback
									<
									HttpResponse
									>()
									 {
									
            @Override
									
            public
									 void
									 onResult
									(
									HttpResponse
									 data
									)
									 {
									
                if
									 (
									data
									 !=
									 null
									 &&
									 !
									dataFromHttpReady
									)
									 {
									
                    // Cache có dữ liệu cũ & yêu cầu HTTP từ xa chưa trả về789 Club, hiển thị dữ liệu cũ trước
                    processData
									(
									data
									);
									
                }
									
            }
									
        });
									
        httpService
									.
									doRequest
									(
									"http://..."
									,
									 new
									 HttpRequest
									(),
									
                new
									 HttpListener
									<
									HttpRequest
									,
									 HttpResponse
									>()
									 {
									
                    @Override
									
                    public
									 void
									 onResult
									(
									String
									 apiUrl
									,
									
                                         HttpRequest
									 request
									,
									
                                         HttpResult
									<
									HttpResponse
									>
									 result
									,
									
                                         Object
									 contextData
									)
									 {
									
                        if
									 (
									result
									.
									getErrorCode
									()
									 ==
									 HttpResult
									.
									SUCCESS
									)
									 {
									
                            dataFromHttpReady
									 =
									 true
									;
									
                            processData
									(
									result
									.
									getResponse
									());
									
                            // Kéo dữ liệu mới từ HTTP789 Club, cập nhật cache cục bộ
                            localDataCache
									.
									putCachingData
									(
									userId
									,
									 result
									.
									getResponse
									(),
									 null
									);
									
                        }
									
                        else
									 {
									
                            processError
									(
									result
									.
									getErrorCode
									());
									
                        }
									
                    }
									
                },
									
                null
									);
									
    }
									


    private
									 void
									 processData
									(
									HttpResponse
									 data
									)
									 {
									
        // TODO: Cập nhật giao diện người dùngi9bet.com nhận 100k, hiển thị dữ liệu. Mã bỏ qua ở đây
    }
									

    private
									 void
									 processError
									(
									int
									 errorCode
									)
									 {
									
        // TODO: Cập nhật giao diện người dùng789 Club, hiển thị lỗi. Mã bỏ qua ở đây
    }
									
}
									

								

Dù việc đọc dữ liệu từ bộ nhớ đệm cục bộ thường nhanh hơn rất nhiều so với việc lấy dữ liệu từ mạngi9bet.com nhận 100k, nhưng vì cả hai đều là các giao diện bất đồng bộ (asynchronous), nên hoàn toàn có khả năng logic rằng dữ liệu từ mạng được trả về trước khi dữ liệu từ bộ nhớ đệm thực hiệ Hơn nữa, trong bài viết trước của chúng ta mang tên " Xử lý bất đồng bộ trong phát triển Android và iOS (hai) —— Quay lại tác vụ bất đồng bộ Trong phần “thứ tự callback” của tài liệui9bet.com nhận 100k, việc đề cập đến “callback kết quả thất bại xảy ra sớm” và “callback kết quả thành công xảy ra sớm” đã cung cấp một nền tảng thực tế hơn cho trường hợp này. Điều này không chỉ làm rõ cách các sự kiện có thể diễn ra theo thứ tự bất ngờ mà còn giúp người đọc hiểu sâu hơn về cơ chế hoạt động tiềm ẩn trong các kịch bản tương tự.

Trong đoạn mã trên789 Club, nếu việc lấy dữ liệu từ mạng trả về trước khi dữ liệu từ bộ nhớ cache được gọi lại, chúng ta sẽ đánh dấu một biến logic kiểu boolean có tên là Khi nhiệm vụ lấy dữ liệu từ bộ nhớ cache hoàn tất, chúng ta sẽ kiểm tra biến này để quyết định bỏ qua dữ liệu từ bộ nhớ cache. Thêm vào đó, để đảm bảo tính nhất quán của dữ liệu, chúng tôi cũng thêm một cơ chế kiểm tra thời gian. Nếu khoảng thời gian giữa việc nhận dữ liệu từ mạng và bộ nhớ cache quá lớn, hệ thống sẽ tự động ưu tiên dữ liệu từ mạng hơn, tránh trường hợp dữ liệu cũ trong bộ nhớ cache gây ra lỗi hoặc xung đột thông tin.

thực hiện đồng thời với ưu tiên cho một bên

thực thi song songi9bet.com nhận 100k, ưu tiên một bên

Sử dụng RxJava zip để thực hiện yêu cầu mạng đồng thời

đấu tay đôi không vũ khí

Yêu cầu mạng đồng thời

thực hiện yêu cầu mạng song song

Chúng ta có thể xem hai yêu cầu mạng đồng thời như là hai chuỗi Observable789 Club, sau đó sử dụng phép toán zip để hợp nhất kết quả của chúng. Điều này thực sự làm cho mọi thứ trở nên đơn giản hơn rất nhiều. Tuy nhiên, trước tiên, chúng ta cần giải quyết một vấn đề khác: bao gói giao diện yêu cầu mạng bất đồng bộ được đại diện bởi HttpService thành mộ Trong trường hợp này, việc thiết kế lại lớp HttpService sẽ giúp chúng ta dễ dàng quản lý luồng dữ liệu từ mạng. Đầu tiên, chúng ta cần xác định rõ các phương thức mà giao diện này sẽ cung cấp, chẳng hạn như phương thức phát hành yêu cầu và xử lý phản hồi. Sau đó, chúng ta có thể sử dụng RxJava để chuyển đổi các thao tác bất đồng bộ thành các chuỗi Observable có cấu trúc rõ ràng hơn. Điều này không chỉ giúp kiểm soát tốt hơn luồng dữ liệu mà còn cải thiện khả năng bảo trì mã nguồn.

Thông thườngbacarat, việc bao gói một tác vụ đồng bộ thành một Observable khá đơn giản, nhưng khi nói đến việc chuyển đổi một tác vụ bất đồng bộ sẵn có thành Observable thì không phải lúc nào cũng rõ ràng. Trong trường hợp này, chúng ta cần sử dụng AsyncOnSubscribe để xử lý vấn đề. AsyncOnSubscribe giúp chúng ta kiểm soát chặt chẽ hơn quá trình sinh ra các giá trị từ nguồn dữ liệu bất đồng bộ và đảm bảo rằng mọi thứ diễn ra theo cách mà ta mong muốn. Nó cho phép bạn quản lý trạng thái và thực hiện các thao tác phức tạp mà không làm mất đi tính chất bất đồng bộ của nhiệm vụ gốc. Ví dụ như khi bạn đang làm việc với một API mạng hoặc một sự kiện thời gian thực, AsyncOnSubscribe sẽ là công cụ hữu ích để tạo ra một luồng dữ liệu Observable phù hợp với nhu cầu của bạn. Điều này đặc biệt quan trọng trong các ứng dụng cần xử lý nhiều yêu cầu phức tạp và yêu cầu độ chính xác cao.

								
									
										public
									 class
									 MultiRequestsDemoActivity
									 extends
									 AppCompatActivity
									 {
									
    private
									 HttpService
									 httpService
									 =
									 new
									 MockHttpService
									();
									

    private
									 TextView
									 apiResultDisplayTextView
									;
									

    @Override
									
    protected
									 void
									 onCreate
									(
									Bundle
									 savedInstanceState
									)
									 {
									
        super
									.
									onCreate
									(
									savedInstanceState
									);
									
        setContentView
									(
									R
									.
									layout
									.
									activity_multi_requests_demo
									);
									

        apiResultDisplayTextView
									 =
									 (
									TextView
									)
									 findViewById
									(
									R
									.
									id
									.
									api_result_display
									);
									

        /** * Đầu tiêni9bet.com nhận 100k, sử dụng cơ chế AsyncOnSubscribe để đóng gói hai lần yêu cầu thành hai Observable riêng biệt */

        Observable
									<
									HttpResponse1
									>
									 request1
									 =
									 Observable
									.
									create
									(
									new
									 AsyncOnSubscribe
									<
									Integer
									,
									 HttpResponse1
									>()
									 {
									
            @Override
									
            protected
									 Integer
									 generateState
									()
									 {
									
                return
									 0
									;
									
            }
									

            @Override
									
            protected
									 Integer
									 next
									(
									Integer
									 state
									,
									 long
									 requested
									,
									 Observer
									<
									Observable
									<?
									 extends
									 HttpResponse1
									>>
									 observer
									)
									 {
									
                final
									 Observable
									<
									HttpResponse1
									>
									 asyncObservable
									 =
									 Observable
									.
									create
									(
									new
									 Observable
									.
									OnSubscribe
									<
									HttpResponse1
									>()
									 {
									
                    @Override
									
                    public
									 void
									 call
									(
									final
									 Subscriber
									<?
									 super
									 HttpResponse1
									>
									 subscriber
									)
									 {
									
                        // Khởi động yêu cầu bất đồng bộ đầu tiên
                        httpService
									.
									doRequest
									(
									"http://..."
									,
									 new
									 HttpRequest1
									(),
									
                                new
									 HttpListener
									<
									HttpRequest1
									,
									 HttpResponse1
									>()
									 {
									
                                    @Override
									
                                    public
									 void
									 onResult
									(
									String
									 apiUrl
									,
									 HttpRequest1
									 request
									,
									 HttpResult
									<
									HttpResponse1
									>
									 result
									,
									 Object
									 contextData
									)
									 {
									
                                        // Yêu cầu bất đồng bộ đầu tiên kết thúc789 Club, gửi kết quả vào asyncObservable
                                        if
									 (
									result
									.
									getErrorCode
									()
									 ==
									 HttpResult
									.
									SUCCESS
									)
									 {
									
                                            subscriber
									.
									onNext
									(
									result
									.
									getResponse
									());
									
                                            subscriber
									.
									onCompleted
									();
									
                                        }
									
                                        else
									 {
									
                                            subscriber
									.
									onError
									(
									new
									 Exception
									(
									"request1 failed"
									));
									
                                        }
									
                                    }
									
                                },
									
                                null
									);
									
                    }
									
                });
									
                observer
									.
									onNext
									(
									asyncObservable
									);
									
                observer
									.
									onCompleted
									();
									
                return
									 1
									;
									
            }
									
        });
									

        Observable
									<
									HttpResponse2
									>
									 request2
									 =
									 Observable
									.
									create
									(
									new
									 AsyncOnSubscribe
									<
									Integer
									,
									 HttpResponse2
									>()
									 {
									
            @Override
									
            protected
									 Integer
									 generateState
									()
									 {
									
                return
									 0
									;
									
            }
									

            @Override
									
            protected
									 Integer
									 next
									(
									Integer
									 state
									,
									 long
									 requested
									,
									 Observer
									<
									Observable
									<?
									 extends
									 HttpResponse2
									>>
									 observer
									)
									 {
									
                final
									 Observable
									<
									HttpResponse2
									>
									 asyncObservable
									 =
									 Observable
									.
									create
									(
									new
									 Observable
									.
									OnSubscribe
									<
									HttpResponse2
									>()
									 {
									
                    @Override
									
                    public
									 void
									 call
									(
									final
									 Subscriber
									<?
									 super
									 HttpResponse2
									>
									 subscriber
									)
									 {
									
                        // Khởi động yêu cầu bất đồng bộ thứ hai
                        httpService
									.
									doRequest
									(
									"http://..."
									,
									 new
									 HttpRequest2
									(),
									
                                new
									 HttpListener
									<
									HttpRequest2
									,
									 HttpResponse2
									>()
									 {
									
                                    @Override
									
                                    public
									 void
									 onResult
									(
									String
									 apiUrl
									,
									 HttpRequest2
									 request
									,
									 HttpResult
									<
									HttpResponse2
									>
									 result
									,
									 Object
									 contextData
									)
									 {
									
                                        // Yêu cầu bất đồng bộ thứ hai kết thúcbacarat, gửi kết quả vào asyncObservable
                                        if
									 (
									result
									.
									getErrorCode
									()
									 ==
									 HttpResult
									.
									SUCCESS
									)
									 {
									
                                            subscriber
									.
									onNext
									(
									result
									.
									getResponse
									());
									
                                            subscriber
									.
									onCompleted
									();
									
                                        }
									
                                        else
									 {
									
                                            subscriber
									.
									onError
									(
									new
									 Exception
									(
									"reques2 failed"
									));
									
                                        }
									
                                    }
									
                                },
									
                                null
									);
									
                    }
									
                });
									
                observer
									.
									onNext
									(
									asyncObservable
									);
									
                observer
									.
									onCompleted
									();
									
                return
									 1
									;
									
            }
									
        });
									

        // Đối với hai Observable biểu diễn yêu cầu nàybacarat, sử dụng zip để hợp nhất kết quả
        Observable
									.
									zip
									(
									request1
									,
									 request2
									,
									 new
									 Func2
									<
									HttpResponse1
									,
									 HttpResponse2
									,
									 List
									<
									Object
									>>()
									 {
									
            @Override
									
            public
									 List
									<
									Object
									>
									 call
									(
									HttpResponse1
									 response1
									,
									 HttpResponse2
									 response2
									)
									 {
									
                List
									<
									Object
									>
									 responses
									 =
									 new
									 ArrayList
									<
									Object
									>(
									2
									);
									
                responses
									.
									add
									(
									response1
									);
									
                responses
									.
									add
									(
									response2
									);
									
                return
									 responses
									;
									
            }
									
        }).
									subscribe
									(
									new
									 Subscriber
									<
									List
									<
									Object
									>>()
									 {
									
            private
									 HttpResponse1
									 response1
									;
									
            private
									 HttpResponse2
									 response2
									;
									

            @Override
									
            public
									 void
									 onNext
									(
									List
									<
									Object
									>
									 responses
									)
									 {
									
                response1
									 =
									 (
									HttpResponse1
									)
									 responses
									.
									get
									(
									0
									);
									
                response2
									 =
									 (
									HttpResponse2
									)
									 responses
									.
									get
									(
									1
									);
									
            }
									

            @Override
									
            public
									 void
									 onCompleted
									()
									 {
									
                processData
									(
									response1
									,
									 response2
									);
									
            }
									

            @Override
									
            public
									 void
									 onError
									(
									Throwable
									 e
									)
									 {
									
                processError
									(
									e
									);
									
            }
									

        });
									
    }
									

    private
									 void
									 processData
									(
									HttpResponse1
									 data1
									,
									 HttpResponse2
									 data2
									)
									 {
									
        // TODO: Cập nhật giao diện người dùng789 Club, hiển thị dữ liệu. Mã bỏ qua ở đây
    }
									

    private
									 void
									 processError
									(
									Throwable
									 e
									)
									 {
									
        // TODO: Cập nhật giao diện người dùngbacarat, hiển thị lỗi. Mã bỏ qua ở đây
    }
									

								

chuyển đổi HttpService thành Observable

thực hiện đồng thời nhưng ưu tiên một bên

thực thi tuần tự liên tiếp


Bài viết này lần lượt ba loại mối quan hệ hợp tác giữa các tác vụ bất đồng bội9bet.com nhận 100k, và cuối cùng không đưa ra kết luận rằng việc chuyển đổi tất cả các tác vụ bất đồng bộ thành chế độ thực hiện tuần tự sẽ làm đơn giản hóa logic xử lý. Việc lựa chọn vẫn thuộc về chính các nhà phát triển. Trong thực tế, mỗi cách tiếp cận đều có ưu nhược điểm riêng. Khi chuyển sang thực thi tuần tự, chúng ta có thể giảm thiểu các vấn đề về tính nhất quán hoặc xung đột dữ liệu, nhưng đồng thời cũng có nguy cơ làm tăng thời gian chờ đợi và giảm hiệu suất tổng thể của ứng dụng. Ngược lại, nếu duy trì chế độ thực thi song song, dù mang lại lợi ích về hiệu suất, nó cũng đòi hỏi các nhà phát triển phải thiết kế logic một cách cẩn trọng để tránh những rủi ro không đáng có. Do đó, không có công thức cố định nào áp dụng cho mọi trường hợp. Điều quan trọng là các nhà phát triển cần đánh giá kỹ lưỡng yêu cầu cụ thể của từng dự án và cân nhắc kỹ trước khi đưa ra quyết định cuối cùng. Sự linh hoạt trong tư duy và khả năng điều chỉnh phương pháp tiếp cận phù hợp với hoàn cảnh là yếu tố then chốt giúp tối ưu hóa hiệu quả của việc xử lý các tác vụ bất đồng bộ.

Bên cạnh đóbacarat, một vấn đề không thể bỏ qua là trong nhiều trường hợp, quyền lựa chọn không nằm trong tay chúng ta. Có thể code architecture mà chúng ta nhận được đã tạo ra các mối quan hệ cộng tác giữa các tác vụ bất đồng bộ phức tạp và đa dạng. Điều cần làm ở đây là khi tình huống như vậy xảy ra, chúng ta luôn phải giữ bình tĩnh, từ đó phân tích và hiểu rõ logic code để xác định chính xác mình đang đối mặt với loại tình huống nào. Đôi khi, việc tiếp cận những đoạn mã phức tạp này có thể khiến người lập trình cảm thấy bối rối và mất phương hướng. Tuy nhiên, kỹ năng quan trọng nhất không chỉ là khả năng đọc hiểu code mà còn là khả năng suy luận logic để đưa ra phán đoán đúng đắn. Khi đã hiểu rõ bản chất của vấn đề, việc điều chỉnh hoặc sửa đổi sẽ trở nên dễ dàng hơn nhiều. Hãy nhớ rằng, sự tỉnh táo và kiên nhẫn chính là chìa khóa dẫn đến thành công trong bất kỳ dự án phần mềm nào.

(Kết thúc)

Các bài viết được chọn lọc khác


Bài viết gốci9bet.com nhận 100k, xin vui lòng trích dẫn nguồn và bao gồm mã QR bên dưới! Nếu không, từ chối tái bản!
Liên kết bài viết: /9ejfxdmx.html
Hãy theo dõi tài khoản Weibo cá nhân của tôi: Tìm kiếm tên tôi "Trương Thiết Lệ" trên Weibo.
Tài khoản WeChat của tôi: tielei-blog (Trương Thiết Lệ)
Bài trước: Xử lý bất đồng bộ trong phát triển Android và iOS (hai) —— Quay lại tác vụ bất đồng bộ
Bài sau: Phép thuật của trẻ nhỏ

Bài viết mới nhất