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

Xử lý bất đồng bộ trong Android và iOS (phần một) — Bài mở đầu


Xử lý bất đồng bộ trong phát triển Android và iOS

Kể từ năm 2012keo nha cai hom nay, tôi bắt đầu phát triển. MicroLove Kể từ khi bản phát hành đầu tiên của ứng dụng cho iOSkeo nha cai hom nay, tôi và toàn bộ đội ngũ đã có 4 năm kinh nghiệm làm việc với cả iOS và Android. Nếu nhìn lại chặng đường này, liệu có sự khác biệt nào giữa việc phát triển cho hai nền tảng này so với các lĩnh vực khác không? Và một nhà phát triển iOS hoặc Android chuyên nghiệp cần phải trang bị những kỹ năng gì để thành công? Trước hết, mỗi hệ điều hành đều có đặc điểm riêng khiến nó trở nên độc đáo. Đối với iOS, việc tuân thủ chặt chẽ các quy tắc thiết kế của Apple mang đến trải nghiệm người dùng mượt mà nhưng cũng đòi hỏi sự cẩn trọng cao trong việc xây dựng ứng dụng. Trong khi đó, Android lại linh hoạt hơn, cho phép người dùng tùy chỉnh sâu hơn và tạo ra nhiều biến thể phần cứng khác nhau. Điều này có nghĩa là một nhà phát triển Android cần phải hiểu rõ cách ứng dụng hoạt động trên nhiều loại thiết bị, từ điện thoại giá rẻ đến máy tính bảng cao cấp. Ngoài ra, một nhà phát triển iOS hoặc Android thực thụ cần phải có khả năng lập trình vững chắc cùng kiến thức sâu về ngôn ngữ như Swift (cho iOS) và Kotlin/Java (cho Android). Bên cạnh đó, họ cũng cần am hiểu về API, SDK và các công cụ phát triển liên quan. Đặc biệt, khả năng tối ưu hóa hiệu suất ứng dụng, đảm bảo tính ổn định và an toàn thông tin cũng là những yếu tố quan trọng không thể thiếu. Điều thú vị là dù làm việc trên nền tảng nào, sự sáng tạo và khả năng giải quyết vấn đề vẫn luôn là những phẩm chất quan trọng nhất. Việc liên tục học hỏi và cập nhật xu hướng mới trong ngành cũng là chìa khóa giúp các nhà phát triển duy trì được lợi thế cạnh tranh trong thị trường công nghệ đầy thách thức này.

Nếu phân tích kỹ lưỡngkeo nha cai hom nay, công việc phát triển ứng dụng cho iOS và Android vẫn có thể được chia thành hai phần chính là "phần frontend" và "phần backend" (giống như việc phát triển trên máy chủ cũng có thể được phân chia thành frontend và backend). Mỗi phần đều đóng vai trò quan trọng trong việc tạo ra một sản phẩm hoàn chỉnh và hiệu quả. Trong đó, phần frontend chịu trách nhiệm về giao diện người dùng và trải nghiệm, còn phần backend đảm nhiệm các hoạt động xử lý dữ liệu và tương tác với hệ thống cơ sở dữ liệu. Đây là hai yếu tố không thể thiếu để đảm bảo ứng dụng vận hành trơn tru và đáp ứng nhu cầu của người sử dụng.

công việc phía trước

  • Vẽ và hiển thị (giải quyết vấn đề nội dung hiển thị).
  • Layout (giải quyết vấn đề kích thước và vị trí hiển thị).
  • Xử lý sự kiện (giải quyết vấn đề tương tác).

Phần việc "backend" thì lại là những thứ ẩn sau giao diện người dùng (UI). Ví dụ như thao tác và sắp xếp dữ liệuđánh bài online, cơ chế bộ nhớ đệm, hàng đợi gửi dữ liệu, thiết kế và quản lý chu kỳ sống, lập trình mạng, cũng như các vấn đề về đẩy thông báo và lắng nghe sự kiện. Tất cả những công việc này, về bản chất, đều xoay quanh việc giải quyết các vấn đề ở tầng "logic". Chúng không phải là những thứ đặc thù riêng cho hệ điều hà Tuy nhiên, một vấn đề lớn chiếm phần lớn trong việc phát triển backend chính là cách để xử lý các tác vụ "không đồng bộ" theo cách "không đồng bộ". Đây thực sự là một thách thức lớn và cần có chiến lược rõ ràng để đảm bảo hiệu suất và độ ổn định của ứng dụng.

xử lý bất đồng bộ

Xử lý bất đồng bộ trong lập trình Android và iOS

xử lý bất đồng bộ

Trong quá trình lập trìnhđánh bài online, chúng ta thường cần thực hiện một số tác vụ bất đồng bộ. Khi các tác vụ này được khởi động, người gọi không cần phải chờ đợi cho đến khi tác vụ hoàn thành mà có thể tiếp tục làm những việc khác, và thời điểm tác vụ kết thúc là không xác định, không thể dự đoán trước. Bài viết hôm nay sẽ tập trung tất cả các khía cạnh liên quan đến việc xử lý những tác vụ bất đồng bộ này, từ cách thức quản lý đến những thách thức và giải pháp tiềm năng mà chúng ta có thể gặp phải trong quá trình làm việc.

Để nội dung thảo luận rõ ràng hơnkeo nha cai hom nay, trước tiên hãy liệt kê dàn ý như sau:

  • (Một) Tóm tắt — Giới thiệu các nhiệm vụ bất đồng bộ phổ biến và tại sao chủ đề này lại quan trọng như vậy.

  • (Hai) Sự trả về của nhiệm vụ bất đồng bộ. Trong cuộc trò chuyện nàykeo 88, chúng ta sẽ cùng nhau khám phá nhiều khía cạnh liên quan đến giao diện callback, chẳng hạn như cách xử lý lỗi, mô hình đa luồng, việc truyền tham số qua lại, cũng như thứ tự mà các callback được thực thi. Tất cả những yếu tố này đều đóng vai trò quan trọng trong việc tối ưu hóa hiệu suất và đảm bảo tính ổn định của ứng dụng.

  • (Ba) Hợp tác giữa nhiều nhiệm vụ bất đồng bộ.

  • (Tư) Nhiệm vụ bất đồng bộ và hàng đợi.

  • (5) Hủy bỏ và tạm dừng tác vụ bất đồng bộđánh bài online, cũng như start ID - Việc thực sự hủy bỏ một tác vụ bất đồng bộ đang được thực hiện là vô cùng khó khăn. Việc quản lý các tác vụ bất đồng bộ trong hệ thống thường là một thách thức lớn. Khi một tác vụ đang chạy, việc cố gắng ngắt nó có thể dẫn đến các vấn đề phức tạp. Không chỉ cần đảm bảo rằng tất cả các tài nguyên đã được giải phóng, mà còn phải tránh những hậu quả không mong muốn, chẳng hạn như lỗi hoặc mất dữ liệu. Start ID, mặc dù giúp xác định chính xác tác vụ cần xử lý, nhưng cũng không phải là chìa khóa duy nhất để giải quyết vấn đề này. Ngay cả khi bạn biết chính xác ID của tác vụ, việc can thiệp vào quá trình đó vẫn đòi hỏi sự cẩn trọng đặc biệt và kỹ năng cao.

  • (Sáu) Về việc khóa màn hình và không khóa màn hình.

  • (Thứ Bảy) Phân tích ví dụ về Android Service - Dịch vụ Android cung cấp một khung làm việc chặt chẽ để thực hiện các tác vụ bất đồng bộ (trong tương lai có thể sẽ bổ sung thêm nhiều ví dụ phân tích khác để mở rộng series này).

Rõ ràngkeo nha cai hom nay, bài viết này sẽ thảo luận về phần (một) trong dàn ý trên.

Để làm rõ thêmkeo nha cai hom nay, mã nguồn trong loạt bài viết này đã được sắp xếp và lưu trữ trên GitHub (liên tục cập nhật), với đường dẫn kho lưu trữ như sau:

Trong bài viết nàykeo nha cai hom nay, mã nguồn Java được tìm thấy trong package có tên là `com. demos.async. introduction`. Về phần mã nguồn của iOS, nó nằm trong một thư mục riêng biệt có tên là `iOSDemos`.

Bây giờđánh bài online, hãy bắt đầu từ một ví dụ cụ thể nhỏ: việc kết nối vớ Trong thế giới lập trình Android, việc sử dụng Service là một phần quan trọng để thực hiện các tác vụ nền mà không cần tương tác trực tiếp với giao diện người dùng. Khi nói đến việc "kết nối" với một Service, chúng ta đang đề cập đến việc tạo ra một mối liên kết giữa ứng dụng của mình và dịch vụ đang chạy, cho phép chúng ta gửi yêu cầu hoặc nhận dữ liệu từ nó một cách hiệu quả. Để minh họa rõ hơn, hãy tưởng tượng bạn đang phát triển một ứng dụng chơi nhạc. Trong trường hợp này, bạn có thể sử dụng một Service để xử lý tất cả các hoạt động liên quan đến âm thanh như tải bài hát, điều khiển phát lại hoặc tạm dừng. Việc "kết nối" với Service này sẽ giúp bạn kiểm soát toàn diện quá trình chơi nhạc mà không làm gián đoạn trải nghiệm người dùng. Vậy, tại sao lại cần phải "kết nối" với Service? Điều này cho phép ứng dụng của bạn tương tác mượt mà hơn với các tác vụ phức tạp mà không cần phải lo lắng về việc quản lý tài nguyên hoặc lifecycle của Service.

								
									
										public
									 class
									 ServiceBindingDemoActivity
									 extends
									 Activity
									 {
									
    private
									 ServiceConnection
									 serviceConnection
									 =
									 new
									 ServiceConnection
									()
									 {
									
        @Override
									
        public
									 void
									 onServiceDisconnected
									(
									ComponentName
									 name
									)
									 {
									
            //Giải phóng tham chiếu và mối liên kết lắng nghe giữa Activity và Service.
            ...
									
        }
									

        @Override
									
        public
									 void
									 onServiceConnected
									(
									ComponentName
									 name
									,
									 IBinder
									 service
									)
									 {
									
            //Thiết lập tham chiếu và mối liên kết lắng nghe giữa Activity và Service.
            ...
									
        }
									
    };
									

    @Override
									
    public
									 void
									 onResume
									()
									 {
									
        super
									.
									onResume
									();
									

        Intent
									 intent
									 =
									 new
									 Intent
									(
									this
									,
									 SomeService
									.
									class
									);
									
        bindService
									(
									intent
									,
									 serviceConnection
									,
									 Context
									.
									BIND_AUTO_CREATE
									);
									
    }
									

    @Override
									
    public
									 void
									 onPause
									()
									 {
									
        super
									.
									onPause
									();
									

        //Giải phóng tham chiếu và mối liên kết lắng nghe giữa Activity và Service.
        ...
									

        unbindService
									(
									serviceConnection
									);
									
    }
									
}
									

								

Ví dụ trên minh họa cách một Activity và một Service thường xuyên giao tiếp với nhau. Khi Activity được kích hoạt trở lại trong trạng thái onResumeđánh bài online, nó sẽ tiến hành kết nối với Service. Ngược lại, khi chuyển sang trạng thái onPause, Activity sẽ ngắt kết nối khỏi Service. Sau khi quá trình kết nối thành công, phương thức onServiceConnected sẽ được gọi. Tại thời điểm này, Activity nhận được một phiên bản IBinder từ tham số service và có thể bắt đầu giao tiếp với Service thông qua các phương thức gọi (dù là trong cùng tiến trình hay giữa các tiến trình khác nhau). Ví dụ như trong phương thức onServiceConnected, các tác vụ phổ biến mà Activity thường thực hiện bao gồm: lưu trữ IBinder vào biến thành viên của Activity để sử dụng trong tương lai; gọi phương thức của IBinder để lấy trạng thái hiện tại của Service; thiết lập các phương thức trả về để theo dõi sự kiện thay đổi tiếp theo của Service; và nhiều tác vụ khác tùy thuộc vào yêu cầu cụ thể. Trong trường hợp cần thiết, Activity cũng có thể gửi dữ liệu hoặc yêu cầu tới Service bằng cách sử dụng IBinder, chẳng hạn như yêu cầu Service thực hiện một tác vụ nào đó, gửi trạng thái mới hoặc cung cấp thông tin cập nhật cho người dùng. Điều này giúp duy trì sự linh hoạt và hiệu quả trong việc quản lý các luồng xử lý giữa Activity và Service, đồng thời đảm bảo rằng cả hai vẫn giữ liên lạc chặt chẽ mà không gây ra lỗi hoặc xung đột trong hoạt động hệ thống.

Quá trình này dường như hoàn hảo khi nhìn từ bên ngoàiđánh bài online, nhưng nếu xem xét kỹ hơn, việc sử dụng hàm bindService sẽ phơi bày một lỗ hổng logic. Lý do là vì bindService là một lệnh "không đồng bộ", tức là nó chỉ khởi động quá trình liên kết mà không chờ quá trình đó kết thúc trước khi trả về. Thời điểm quá trình liên kết hoàn tất (tức là phương thức onServiceConnected được gọi), hoàn toàn không thể dự đoán trước. Điều này phụ thuộc vào tốc độ của quá trình liên kết. Trong khi đó, theo chu kỳ sống của một Activity, sau khi onResumed được thực thi, phương thức onPause có thể xảy ra bất kỳ lúc nào. Điều này có nghĩa là, sau khi bindService hoàn thành, phương thức onServiceConnected có thể được gọi trước khi onPause thực hiện, hoặc ngược lại, onPause có thể chạy trước khi onServiceConnected được kích hoạt. Sự không chắc chắn này khiến mã nguồn trở nên rủi ro và khó kiểm soát.

Thông thườngđánh bài online, hàm onPause không được kích hoạt ngay lập tức, do đó hàm onServiceConnected thường sẽ chạy trước khi onPause được thực hiện. Tuy nhiên, xét về mặt logic, chúng ta không thể hoàn toàn bỏ qua khả năng khác có thể xảy ra. Thực tế, điều này hoàn toàn có thể xảy ra trong một số trường hợp đặc biệt, ví dụ như khi người dùng vừa mở trang và ngay lập tức chuyển ứng dụng sang chế độ nền. Dù xác suất xảy ra là rất thấp, nhưng nếu điều này xảy ra, hàm onServiceConnected cuối cùng sẽ thiết lập mối liên kết và lắng nghe giữa Activity và Service. Khi đó, ứng dụng có thể đang ở trạng thái nền, nhưng Activity và IBinder vẫn có thể tiếp tục tham chiếu lẫn nhau. Điều này có thể dẫn đến việc các đối tượng Java không được giải phóng trong thời gian dài và gây ra nhiều vấn đề bất thường khác. Ngoài ra, cần lưu ý rằng việc quản lý tài nguyên trong các trường hợp như thế này là rất quan trọng. Nếu không xử lý cẩn thận, nó có thể ảnh hưởng nghiêm trọng đến hiệu suất và độ ổn định của ứng dụng. Vì vậy, việc kiểm tra và đảm bảo rằng mọi thứ đều hoạt động đúng cách trong những tình huống khó dự đoán là vô cùng cần thiết.

Điều này dẫn đến một chi tiết quan trọng: hiệu quả cuối cùng thực sự phụ thuộc vào cách hệ thống triển khai phương thứ Khi hàm onPause được gọi trước hàm onServiceConnectedđánh bài online, thì onPause sẽ là nơi đầu tiên gọi đế Nếu unbindService có thể chắc chắn rằng sau khi nó được gọi, các callback của ServiceConnection sẽ không còn xảy ra nữa, thì vấn đề về việc Activity và IBinder giữ lẫn nhau sẽ không xảy ra. Tuy nhiên, có vẻ như unbindService không đưa ra bất kỳ cam kết nào về điều này. Theo kinh nghiệm cá nhân, hành vi của unbindService trong việc đảm bảo tính ổn định này lại khác nhau tùy thuộc vào phiên bản Android mà bạn đang sử dụng. Điều này có thể dẫn đến sự không nhất quán trong các tác vụ quản lý service giữa các nền tảng, khiến việc phát triển ứng dụng trở nên phức tạp hơn.

Giống như phân tích ở trênkeo nha cai hom nay, một khi chúng ta đã nắm rõ tất cả các tình huống có thể xảy ra khi sử dụng phương thức asynchrone bindService, việc nghĩ ra những giải pháp tương tự sẽ không còn quá khó khăn. Chúng ta có thể bắt đầu bằng cách tìm hiểu kỹ hơn về từng trường hợp cụ thể và từ đó đưa ra cách xử lý phù hợp để đảm bảo ứng dụng hoạt động ổn định trong mọi tình huống.

								
									
										public
									 class
									 ServiceBindingDemoActivity
									 extends
									 Activity
									 {
									
    /** * Cho biết Activity này có đang ở trạng thái chạy (running) hay không: * Khi phương thức onResume được gọikeo 88, Activity sẽ chuyển sang trạng thái chạy. */
    private
									 boolean
									 running
									;
									

    private
									 ServiceConnection
									 serviceConnection
									 =
									 new
									 ServiceConnection
									()
									 {
									
        @Override
									
        public
									 void
									 onServiceDisconnected
									(
									ComponentName
									 name
									)
									 {
									
            //Giải phóng tham chiếu và mối liên kết lắng nghe giữa Activity và Service.
            ...
									
        }
									

        @Override
									
        public
									 void
									 onServiceConnected
									(
									ComponentName
									 name
									,
									 IBinder
									 service
									)
									 {
									
            if
									 (
									running
									)
									 {
									
                //Thiết lập tham chiếu và mối liên kết lắng nghe giữa Activity và Service.
                ...
									                
            }
									
        }
									​​
									
    };
									

    @Override
									
    public
									 void
									 onResume
									()
									 {
									
        super
									.
									onResume
									();
									
        running
									 =
									 true
									;
									

        Intent
									 intent
									 =
									 new
									 Intent
									(
									this
									,
									 SomeService
									.
									class
									);
									
        bindService
									(
									intent
									,
									 serviceConnection
									,
									 Context
									.
									BIND_AUTO_CREATE
									);
									
    }
									

    @Override
									
    public
									 void
									 onPause
									()
									 {
									
        super
									.
									onPause
									();
									
        running
									 =
									 false
									;
									

        //Giải phóng tham chiếu và mối liên kết lắng nghe giữa Activity và Service.
        ...
									

        unbindService
									(
									serviceConnection
									);
									

    }
									
}
									

								

Bây giờ chúng ta hãy xem qua một ví dụ nhỏ về iOS.

Giả sử chúng ta cần duy trì một kết nối TCP dài hạn giữa client và server. Kết nối này sẽ tự động thực hiện thao tác kết nối lại khi trạng thái mạng thay đổi. Trước tiênkeo 88, chúng ta cần một lớp có khả năng theo dõi các thay đổi về trạng thái mạng, lớp này được gọi là Reachability, mã nguồn của nó như sau: ```csharp using System; Net.NetworkInformation; public class Reachability { private Timer timer; // Timer để kiểm tra trạng thái mạng định kỳ private bool isConnected = false; // Biến lưu trữ trạng thái kết nối public event Action OnNetworkChange; // Sự kiện được kích hoạt khi trạng thái mạng thay đổi public Reachability() { // Thiết lập thời gian kiểm tra mạng mỗi 5 giây timer = new Timer(CheckConnection, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); } private void CheckConnection(object state) { try { Ping ping = new Ping(); PingReply reply = ping.Send("8.8.8.8"); // Kiểm tra kết nối với Google DNS bool newStatus = reply.Status == IPStatus.Success; if ( = isConnected) { isConnected = newStatus; .Invoke(); // Gọi sự kiện khi trạng thái mạng thay đổi } } catch { isConnected = false; .Invoke(); } } } ``` Lớp Reachability trên sẽ giúp chúng ta theo dõi tình trạng kết nối mạng liên tục và thông báo cho hệ thống khi có sự thay đổi xảy ra.

								
									
										//
									
//  Reachability.h
									
//
									
#import <foundation foundation.h="">
#import <systemconfiguration systemconfiguration.h=""></systemconfiguration></foundation>
extern
									 NSString
									 *
									const
									 networkStatusNotificationInfoKey
									;
									
extern
									 NSString
									 *
									const
									 kReachabilityChangedNotification
									;
									

typedef
									 NS_ENUM
									(
									uint32_t
									,
									 NetworkStatus
									)
									 {
									
    NotReachable
									 =
									 0
									,
									
    ReachableViaWiFi
									 =
									 1
									,
									
    ReachableViaWWAN
									 =
									 2
									
};
									

@interface
									 Reachability
									 :
									 NSObject
									 {
									
@private
									
    SCNetworkReachabilityRef
									 reachabilityRef
									;
									
}
									

/**
-
									 (
									BOOL
									)
									startNetworkMonitoring
									;
									
/**
-
									 (
									BOOL
									)
									stopNetworkMonitoring
									;
									
/**
-
									 (
									NetworkStatus
									)
									 currentNetworkStatus
									;
									
@end
									

//
									
//  Reachability.m
									
//
									
#import "Reachability.h"
#import <sys socket.h="">
#import <netinet in.h=""></netinet></sys>
NSString
									 *
									const
									 networkStatusNotificationInfoKey
									 =
									 @"networkStatus"
									;
									
NSString
									 *
									const
									 kReachabilityChangedNotification
									 =
									 @"NetworkReachabilityChangedNotification"
									;
									

@implementation
									 Reachability
									

-
									 (
									instancetype
									)
									init
									 {
									
    self
									 =
									 [
									super
									 init
									];
									
    if
									 (
									self
									)
									 {
									
        struct
									 sockaddr_in
									 zeroAddress
									;
									
        memset
									(
									&
									zeroAddress
									,
									 0
									,
									 sizeof
									(
									zeroAddress
									));
									
        zeroAddress
									.
									sin_len
									 =
									 sizeof
									(
									zeroAddress
									);
									
        zeroAddress
									.
									sin_family
									 =
									 AF_INET
									;
									
        
        reachabilityRef
									 =
									 SCNetworkReachabilityCreateWithAddress
									(
									kCFAllocatorDefault
									,
									 (
									const
									 struct
									 sockaddr
									*
									)
									&
									zeroAddress
									);
									

    }
									
    
    return
									 self
									;
									
}
									

-
									 (
									void
									)
									dealloc
									 {
									
    if
									 (
									reachabilityRef
									)
									 {
									
        CFRelease
									(
									reachabilityRef
									);
									
    }
									
}
									

static
									 void
									 ReachabilityCallback
									(
									SCNetworkReachabilityRef
									 target
									,
									 SCNetworkReachabilityFlags
									 flags
									,
									 void
									*
									 info
									)
									 {
									
    
    Reachability
									 *
									reachability
									 =
									 (
									__bridge
									 Reachability
									 *
									)
									 info
									;
									
    
    @autoreleasepool
									 {
									
        NetworkStatus
									 networkStatus
									 =
									 [
									reachability
									 currentNetworkStatus
									];
									
        [[
									NSNotificationCenter
									 defaultCenter
									]
									 postNotificationName
									:
									kReachabilityChangedNotification
									 object
									:
									reachability
									 userInfo
									:
									@{
									networkStatusNotificationInfoKey
									 :
									 @
									(
									networkStatus
									)}];
									
    }
									
}
									

-
									 (
									BOOL
									)
									startNetworkMonitoring
									 {
									
    SCNetworkReachabilityContext
									 context
									 =
									 {
									0
									,
									 (
									__bridge
									 void
									 *
									 _Nullable
									)(
									self
									),
									 NULL
									,
									 NULL
									,
									 NULL
									};
									
    
    if
									(
									SCNetworkReachabilitySetCallback
									(
									reachabilityRef
									,
									 ReachabilityCallback
									,
									 &
									context
									))
									 {
									
        if
									(
									SCNetworkReachabilityScheduleWithRunLoop
									(
									reachabilityRef
									,
									 CFRunLoopGetCurrent
									(),
									 kCFRunLoopDefaultMode
									))
									 {
									
            return
									 YES
									;
									
        }
									
        
    }
									
    
    return
									 NO
									;
									
}
									

-
									 (
									BOOL
									)
									stopNetworkMonitoring
									 {
									
    return
									 SCNetworkReachabilityUnscheduleFromRunLoop
									(
									reachabilityRef
									,
									 CFRunLoopGetCurrent
									(),
									 kCFRunLoopDefaultMode
									);
									
}
									

-
									 (
									NetworkStatus
									)
									 currentNetworkStatus
									 {
									
    //Mã ở đây bị bỏ qua...
}
									

@end
									

								

Mã trên đã đóng gói giao diện của lớ Khi người gọi muốn bắt đầu theo dõi trạng thái mạngkeo 88, họ sẽ gọi phương thức startNetworkMonitoring; khi hoàn thành việc theo dõi, họ sẽ gọ Chúng ta có thể tưởng tượng rằng đối với kết nối dài hạn mà chúng ta đang phát triển, cần phải tạo và sử dụng đối tượng Reachability để xử lý các thay đổi về trạng thái mạng. Phần mã liên quan của nó có thể trông như sau (tên lớp: ServerConnection; mã trong file header được bỏ qua): ```objc @interface ServerConnection : NSObject - (void)startNetworkMonitoring; - (void)stopNetworkMonitoring; @end @implementation ServerConnection - (void)startNetworkMonitoring { // Khởi tạo và bắt đầu theo dõi sự thay đổi trạng thái mạ reachability = [Reachability reachabilityForInternetConnection]; [self.reachability startNotifier]; } - (void)stopNetworkMonitoring { // Dừng theo dõi sự thay đổi trạng thái mạng [self.reachability stopNotifier]; self.reachability = nil; } @end ``` Trong ví dụ trên, chúng tôi thêm vào cách khởi tạo và cấu hình Reachability để đảm bảo rằng mọi thay đổi về kết nối mạng đều được giám sát một cách hiệu quả. Điều này giúp cho hệ thống của bạn luôn cập nhật tình trạng mạng và điều chỉnh hoạt động dựa trên đó.

								
									
										//
									
//  ServerConnection.m
									
//
									
#import "ServerConnection.h"
#import "Reachability.h"

									
@interface
									 ServerConnection
									()
									 {
									
    //Hàng đợi GCD cho phép người dùng thực hiện thao tác socket.
    dispatch_queue_t
									 socketQueue
									;
									
    Reachability
									 *
									reachability
									;
									
}
									
@end
									

@implementation
									 ServerConnection
									

-
									 (
									instancetype
									)
									init
									 {
									
    self
									 =
									 [
									super
									 init
									];
									
    if
									 (
									self
									)
									 {
									
        socketQueue
									 =
									 dispatch_queue_create
									(
									"SocketQueue"
									,
									 NULL
									);
									
        
        reachability
									 =
									 [[
									Reachability
									 alloc
									]
									 init
									];
									
        [[
									NSNotificationCenter
									 defaultCenter
									]
									 addObserver
									:
									self
									 selector
									:
									@selector
									(
									networkStateChanged
									:
									)
									 name
									:
									kReachabilityChangedNotification
									 object
									:
									reachability
									];
									
        [
									reachability
									 startNetworkMonitoring
									];
									
    }
									
    return
									 self
									;
									
}
									

-
									 (
									void
									)
									dealloc
									 {
									
    [
									reachability
									 stopNetworkMonitoring
									];
									
    [[
									NSNotificationCenter
									 defaultCenter
									]
									 removeObserver
									:
									self
									];
									
}
									


-
									 (
									void
									)
									networkStateChanged
									:(
									NSNotification
									 *
									)
									notification
									 {
									
    NetworkStatus
									 networkStatus
									 =
									 [
									notification
									.
									userInfo
									[
									networkStatusNotificationInfoKey
									]
									 unsignedIntValue
									];
									
    if
									 (
									networkStatus
									 !=
									 NotReachable
									)
									 {
									
        //Thay đổi mạngkeo 88, tái kết nối.
        dispatch_async
									(
									socketQueue
									,
									 ^
									{
									
            [
									self
									 reconnect
									];
									
        });
									
    }
									
}
									

-
									 (
									void
									)
									reconnect
									 {
									
    //Mã ở đây bị bỏ qua...
}
									
@end
									

								

Khi khởi tạo đối tượng ServerConnectionđánh bài online, kết nối dài hạn sẽ tạo ra một instance của Reachability và bắt đầu theo dõi (gọi phương thức startNetworkMonitoring). Quá trình này sử dụng các thông báo hệ thống để thiết lập phương pháp lắng nghe (networkStateChanged:). Khi đối tượng ServerConnection bị hủy bỏ (phương thức dealloc được gọi), việc theo dõi sẽ dừng lại (gọi stopNetworkMonitoring) để giải phóng tài nguyên. Trong quá trình hoạt động, ServerConnection cũng có thể kiểm tra trạng thái mạng liên tục để đảm bảo rằng ứng dụng luôn hoạt động ổn định trong mọi điều kiện kết nối. Điều này giúp cải thiện khả năng xử lý sự cố và tối ưu hóa hiệu suất khi mạng thay đổi hoặc gặp vấn đề đột ngột.

Khi trạng thái kết nối mạng thay đổiđánh bài online, hàm networkStateChanged: sẽ được kích hoạt và trạng thái mạng hiện tại sẽ được truyền vào làm tham số. Nếu phát hiện ra rằng mạng đã trở nên khả dụng (không còn ở trạng thái NotReachable), thì quá trình tái kết nối sẽ được thực hiện một cách bất đồng (asynchronously) để đảm bảo ứng dụng tiếp tục hoạt động mà không bị gián đoạn.

Quy trình này có vẻ hợp lý. Nhưng nó ẩn chứa một vấn đề nghiêm trọng bên trong.

Khi tiến hành thao tác kết nối lại (reconnect)đánh bài online, chúng ta đã sử dụng `dispatch_async` để khởi động một nhiệm vụ không đồng bộ (asynchronous task). Thời điểm nhiệm vụ này hoàn thành là không thể dự đoán trước, điều này phụ thuộc vào tốc độ thực hiện thao tá Giả sử rằng thao tác reconnect diễn ra chậm hơn bình thường (điều này khá phổ biến trong các hoạt động liên quan đến mạng), có thể xảy ra tình huống như sau: quá trình reconnect vẫn đang tiếp tục thực thi, nhưng đối tượng ServerConnection sắp bị hủy (dealloc). Nói cách khác, tất cả các đối tượng khác trong hệ thống đã giải phóng mọi tham chiếu đến ServerConnection, chỉ còn lại một tham chiếu duy nhất từ khối mã (block) được dispatch_async giữ lại đối với self. Trong trường hợp này, khi block không đồng bộ cố gắng thực hiện tác vụ của mình, nó sẽ tìm thấy ServerConnection không còn tồn tại nữa. Điều này có thể dẫn đến lỗi hoặc hành vi không mong muốn trong ứng dụng. Vì vậy, việc quản lý vòng đời của các đối tượng trong bối cảnh có sự tham gia của các tác vụ không đồng bộ rất quan trọng. Chúng ta cần đảm bảo rằng block không cố gắng truy cập vào đối tượng đã bị hủy, hoặc nếu cần thiết, phải kiểm tra xem đối tượng đó có còn hợp lệ hay không trước khi sử dụng. Điều này giúp tránh các vấn đề về hiệu suất và ổn định của ứng dụng.

Điều đó sẽ dẫn đến hậu quả gì?

Điều này sẽ dẫn đến việc khi phương thức reconnect hoàn tấtkeo nha cai hom nay, đối tượng ServerConnection mới thực sự được giải phóng. Tuy nhiên, phương thức dealloc của nó không được thực hiện trên dòng chính (main thread), mà thay vào đó sẽ được thực thi trên hàng đợ Thêm vào đó, điều này có thể gây ra những vấn đề về đồng bộ hóa nếu các tài nguyên liên quan đến đối tượng chưa được giải phóng một cách cẩn thận trước khi dealloc được gọi. Điều quan trọng là phải đảm bảo rằng bất kỳ tác vụ nào cũng cần kết thúc trên cùng một hàng đợi trước khi đối tượng bị xóa khỏi bộ nhớ.

Và tiếp theo sẽ xảy ra điều gì? Điều này phụ thuộc vào cách thức thực hiện củ

Chúng ta hãy cùng phân tích lại mã nguồn của Reachability để hiểu rõ tác động cuối cùng khi sự việc này xảy ra. Khi tình huống này xuất hiệnkeo 88, phương thức stopNetworkMonitoring đã được gọi từ một luồng khác không phải là luồng chính. Ngược lại, lúc startNetworkMonitoring được kích hoạt ban đầu, nó lại nằm trong luồng chính. Hiện tại, chúng ta nhận thấy rằng nếu startNetworkMonitoring và stopNetworkMonitoring không thực thi trên cùng một luồng, thì hàm CFRunLoopGetCurrent() trong phần cài đặt của chúng sẽ không còn ám chỉ cùng một vòng lặp chạy (Run Loop). Điều này dẫn đến một lỗi logic đáng chú ý. Sau khi sai sót này xảy ra, trong hàm stopNetworkMonitoring, hàm SCNetworkReachabilityUnscheduleFromRunLoop không thể gỡ bỏ thành công đối tượng Reachability khỏi vòng lặp chạy mà nó đã được đăng ký trước đó trong luồng chính. Điều này có nghĩa là, sau đó, nếu trạng thái mạng thay đổi, ReachabilityCallback vẫn sẽ được thực thi, nhưng vào thời điểm này, đối tượng Reachability ban đầu đã bị hủy (do quá trình hủy bỏ của ServerConnection). Theo cách hiện tại mà mã nguồn đang hoạt động, tham số info trong ReachabilityCallback sẽ trỏ tới một đối tượng Reachability đã bị giải phóng bộ nhớ. Vì vậy, việc ứng dụng gặp lỗi sập hoàn toàn trở nên dễ hiểu hơn.

nhảy weak-strong

								
									__weak
									 ServerConnection
									 *
									wself
									 =
									 self
									;
									
        dispatch_async
									(
									socketQueue
									,
									 ^
									{
									
            __strong
									 ServerConnection
									 *
									sself
									 =
									 wself
									;
									
            [
									sself
									 reconnect
									];
									
        });
									

								

nhảy weak-strong

Thực tếkeo nha cai hom nay, ngay cả khi thay đổi thành dạng dưới đây, vẫn không có hiệu quả.

								
									__weak
									 ServerConnection
									 *
									wself
									 =
									 self
									;
									
        dispatch_async
									(
									socketQueue
									,
									 ^
									{
									
            [
									wself
									 reconnect
									];
									
        });
									

								

Ngay cả khi bạn sử dụng tham chiếu weak (wself) để gọi phương thức reconnectđánh bài online, một khi nó được thực thi, sẽ vẫn dẫn đến việc tăng số lượng tham chiếu củ Kết quả cuối cùng vẫn là việc phương thức dealloc sẽ được thực hiện trên một luồng không phải là luồng chính. Tuy nhiên, cần lưu ý rằng cách này giúp tránh tình trạng giữ tài nguyên lâu hơn mong muốn, nhưng bạn cũng nên kiểm tra kỹ lưỡng để đảm bảo rằng wself không bị nil trước khi gọi phương thức để tránh các lỗi không đáng có.

Đã giải phóng tài nguyê

								
									
										-
									 (
									void
									)
									dealloc
									 {
									
    dispatch_async
									(
									dispatch_get_main_queue
									(),
									 ^
									{
									
        [
									reachability
									 stopNetworkMonitoring
									];
									
    });
									
    [[
									NSNotificationCenter
									 defaultCenter
									]
									 removeObserver
									:
									self
									];
									
}
									

								

Rõ ràngđánh bài online, việc gọi dispatch_async trong hàm dealloc cũng không phải là một giải pháp khả thi. Khi dealloc được thực hiện, ServerConnection đã bị hủy bỏ và không còn tồn tại nữa. Tuy nhiên, khi block bắt đầu chạy, reachability vẫn đang cố gắng tham chiếu đến một đối tượng ServerConnection đã bị xóa. Kết quả tất yếu là ứng dụng sẽ gặp lỗi sập. Trong trường hợp này, cách tiếp cận an toàn hơn là đảm bảo rằng bất kỳ block nào liên quan đến ServerConnection đều phải được xử lý trước khi đối tượng bị giải phóng. Nếu cần thiết, hãy sử dụng các cơ chế như weak reference để tránh tình trạng tham chiếu sai đối tượng đã bị hủy. Thật vậy, sự phức tạp trong quản lý tài nguyên có thể gây ra nhiều vấn đề nếu không được kiểm soát cẩn thận. Hãy luôn nhớ rằng việc giữ lại hoặc tham chiếu đến một đối tượng đã bị dealloc có thể dẫn đến những hậu quả khó lường.

Xử lý đồng bộ đã hoàn thành.

								
									
										-
									 (
									void
									)
									dealloc
									 {
									
    if
									 (
									!
									[
									NSThread
									 isMainThread
									])
									 {
									
        dispatch_sync
									(
									dispatch_get_main_queue
									(),
									 ^
									{
									
            [
									reachability
									 stopNetworkMonitoring
									];
									
        });
									
    }
									
    else
									 {
									
        [
									reachability
									 stopNetworkMonitoring
									];
									
    }
									

    [[
									NSNotificationCenter
									 defaultCenter
									]
									 removeObserver
									:
									self
									];
									
}
									

								

Sau khi đã “xoay xở” và vá lỗi ở mọi hướngđánh bài online, cuối cùng chúng ta cũng có được một đoạn mã có thể hoạt động ổn định. Tuy nhiên, việc thực hiện các thao tác "đồng bộ" như dispatch_sync trong hàm dealloc – vốn có thể gây ra sự chậm trễ – vẫn khiến nhiều người không khỏi lo lắng và e ngại. Điều này không chỉ tiềm ẩn nguy cơ làm gián đoạn quá trình giải phóng tài nguyên mà còn có thể dẫn đến những vấn đề phức tạp khác nếu không được xử lý cẩn thận.

Vậykeo nha cai hom nay, làm thế nào mới tốt hơn?

Cá nhân tôi cho rằng: Không phải tất cả công việc hủy bỏ đều phù hợp để viết trong dealloc;

Hàm dealloc vốn được thiết kế để giải phóng bộ nhớkeo nha cai hom nay, và nó làm rất tốt nhiệm vụ này – ví dụ như gọi phương thức release cho các biến thành viên (trong ARC, việc này đã được tự động hóa). Tuy nhiên, nếu sử dụng hàm dealloc để quản lý những biến hoặc quy trình có phạm vi ảnh hưởng rộng hơn (bao gồm cả những thứ tồn tại ngoài vòng đời của đối tượng hiện tại), thì đó không phải là một cách tiếp cận khôn ngoan. Lý do ít nhất có hai: Thứ nhất, khi bạn dựa vào dealloc để xử lý các vấn đề phức tạp như vậy, bạn đang đặt niềm tin hoàn toàn vào việc đối tượng sẽ bị hủy đúng thời điểm mà không có bất kỳ sự can thiệp nào từ hệ thống. Điều này có thể dẫn đến những rủi ro không đáng có nếu có lỗi xảy ra trong quá trình giải phóng tài nguyên. Thứ hai, với sự phát triển của công nghệ lập trình hiện đại, có nhiều cách hiệu quả hơn để quản lý trạng thái và tài nguyên theo thời gian, chẳng hạn như sử dụng các block, delegate, hoặc các pattern như Điều này giúp đảm bảo rằng tài nguyên luôn được kiểm soát tốt ngay cả khi đối tượng không còn tồn tại.

  • Thời gian thực thi của dealloc có thể bị trì hoãnđánh bài online, không đảm bảo thời gian thực thi chính xác;
  • Không thể kiểm soát liệu dealloc có được gọi trên luồng chính hay không.

Ví dụ như trong trường hợp của ServerConnectionkeo 88, logic nghiệp vụ chắc chắn biết được thời điểm nào nên dừng việc theo dõi trạng thái mạng thay vì phụ thuộc vào dealloc để thực hiện điều đó. Thực tế, việc dựa vào dealloc để xử lý các tác vụ phức tạp có thể dẫn đến những vấn đề không mong muốn, chẳng hạn như các kết nối chưa được giải phóng hoàn toàn hoặc dữ liệu chưa được xử lý đúng cách trước khi đối tượng bị hủy bỏ. Do đó, tốt nhất là logic nghiệp vụ nên tự quản lý và chủ động đóng các kết nối hoặc ngắt các sự kiện liên quan khi chúng không còn cần thiết nữa.

Ngoài rakeo 88, chúng ta cần đặc biệt chú ý đến việc dealloc có thể được thực hiện trên một luồng bất đồng bộ. Đối với các loại đối tượng khác nhau, chúng ta nên có cách tiếp cận phù hợp. Ví dụ, khi nói đến các đối tượng đóng vai trò là View, thái độ đúng đắn của chúng ta phải là: Không nên cho phép dealloc chạy trên luồng bất đồng bộ. Để ngăn chặn tình trạng này xảy rakeo nha cai hom nay, chúng ta cần cố gắng tránh khởi động nhiệm vụ bất đồng bộ trực tiếp trong View hoặc tránh tạo sự tham chiếu mạnh đến View trong các nhiệm vụ bất đồng bộ có thời gian tồn tại lâu dài. Điều này giúp đảm bảo rằng View không bị khóa hoặc ảnh hưởng bởi các hoạt động bất định từ các tác vụ bên ngoài, từ đó tăng cường tính ổn định và hiệu suất của ứng dụng.

Trong hai ví dụ trênkeo 88, gốc rễ của vấn đề nằm ở các tác vụ bất đồng bộ. Khi suy nghĩ kỹ lưỡng, chúng ta sẽ nhận ra rằng khi nói đến các tác vụ bất đồng bộ, có một vấn đề then chốt mà chúng ta không thể bỏ qua, đó là cách quản lý và xử lý các xung đột tiềm ẩn giữa các tác vụ đang chạ Nếu không giải quyết điều này một cách cẩn thận, hệ thống có thể gặp phải tình trạng mất dữ liệu hoặc hiệu suất giảm đáng kể. Chính vì vậy, việc hiểu rõ cách hoạt động của các tác vụ bất đồng bộ và thiết lập cơ chế kiểm soát hợp lý đóng vai trò vô cùng quan trọng trong việc đảm bảo tính ổn định và hiệu quả của toàn bộ hệ thống. Vấn đề mất hiệu lực điều kiện. Đương nhiênkeo 88, đây là một vấn đề khá rõ ràng: khi một tác vụ bất đồng bộ thực sự được thực hiện (hoặc một sự kiện bất đồng bộ thực sự xảy ra), tình hình có thể đã thay đổi hoàn toàn so với lúc nó được lên kế hoạch. Nói cách khác, các điều kiện mà nó phụ thuộc vào khi đó để hoạt động hoặc diễn ra có thể đã không còn hiệu lực nữa. Điều này đặt ra thách thức lớn cho việc đảm bảo tính chính xác và hiệu quả trong quy trình xử lý bất đồng bộ.

Trong ví dụ đầu tiên về Service Bindingkeo nha cai hom nay, khi quá trình liên kết bất đồng bộ được bắt đầu (khi phương thức bindService được gọi), Hoạt động (Activity) vẫn đang ở trạng thái Đang chạy (đang thực hiện phương thức onResume). Tuy nhiên, khi quá trình liên kết kết thúc (khi phương thức onServiceConnected được gọi), Hoạt động đã rời khỏi trạng thái Đang chạy (đã thực hiện xong phương thức onPause và thậm chí đã hủy liên kết trước đó). Điều này cho thấy rằng việc quản lý vòng đời của Activity và quá trình liên kết dịch vụ cần phải được xử lý một cách cẩn thận để tránh các vấn đề không mong muốn trong hoạt động của ứng dụng.

Trong ví dụ thứ hai về việc theo dõi mạngkeo nha cai hom nay, khi nhiệm vụ kết nối lại bất đồng bộ hoàn thành, tham chiếu của bên ngoài đến instance ServerConnection đã không còn tồn tại nữa. Instance này sắp bước vào quá trình giải phóng tài nguyên. Điều này dẫn đến việc vòng lặp chạy (Run Loop) khi dừng việc theo dõi sẽ không còn là cùng một vòng lặp như ban đầu nữa. Tình huống này có thể gây ra các vấn đề phức tạp trong quản lý tài nguyên và trạng thái của hệ thống. Vì vậy, cần phải cẩn thận để đảm bảo rằng mọi thay đổi trạng thái đều được xử lý một cách chính xác và an toàn trước khi thực hiện việc giải phóng đối tượng hoặc dừng các hoạt động liên quan.

Trước khi bước vào phần thảo luận chính thức về các tác vụ bất đồng bộ trong chương tiếp theokeo nha cai hom nay, chúng ta cần dành một chút thời gian để tổng kết lại những gì liên quan đến các tác vụ bất đồng bộ thường gặp trong cả hai hệ điều hành iOS và Android. Hai nền tảng này đều có cách tiếp cận độc đáo để xử lý vấn đề này, do đó việc hiểu rõ những điểm khác biệt và tương đồng sẽ giúp ích rất nhiều cho quá trình phát triển ứng dụng.

  1. Khi thực hiện yêu cầu mạngđánh bài online, do thời gian thực thi yêu cầu mạng thường khá lâu, nên các phương thức tiếp nhận yêu cầu mạng thường được thiết kế để chạy ở chế độ bất đồng bộ (như NSURLConnection trong iOS hoặc Volley trong Android). Thông thường, chúng ta sẽ bắt đầu một yêu cầu mạng từ luồng chính và sau đó chỉ ngồi chờ sự kiện trả về kết quả thành công hay thất bại của yêu cầu (cũng có nghĩa là nhiệm vụ bất đồng bộ đã hoàn thành). Cuối cùng, dựa trên kết quả trả về, chúng ta sẽ cập nhật giao diện người dùng. Thời gian từ khi khởi động yêu cầu mạng cho đến khi nhận được kết quả rõ ràng (thành công hoặc thất bại) là không xác định trước. Thêm vào đó, việc sử dụng yêu cầu mạng bất đồng bộ giúp giữ cho luồng chính của ứng dụng luôn mượt mà, tránh làm gián đoạn trải nghiệm người dùng. Tuy nhiên, điều này cũng đòi hỏi lập trình viên phải cẩn trọng trong việc quản lý trạng thái và tránh xảy ra tình huống như việc mất tham chiếu hoặc xử lý không đúng cách khi yêu cầu mạng hoàn tất.

  2. Các tác vụ bất đồng bộ được tạo ra chủ động thông qua cơ chế pool luồng. Đối với những tác vụ cần thời gian dài để thực hiện đồng bộ (như thao tác đọc tệp từ ổ đĩađánh bài online, vốn có độ trễ cao, hoặc thực hiện các tác vụ tính toán lớn), chúng ta thường dựa vào các cơ chế pool luồng do hệ thống cung cấp để phân phối những tác vụ này sang các luồng bất đồng bộ, giúp tiết kiệm thời gian xử lý quý giá của luồng chính. Về các cơ chế pool luồng này, trong iOS, chúng ta có GCD (dispatch_async) và NSOperationQueue; còn trong Android, chúng ta có ExecutorService từ JDK truyền thống và AsyncTask từ Dù ở định dạng nào đi chăng nữa, chúng ta đều tạo ra vô số các tác vụ bất đồng bộ để tối ưu hóa hiệu suất ứng dụng. Ví dụ như trong trường hợp đọc một tập tin lớn, nếu không sử dụng thread pool, ứng dụng sẽ bị khóa và người dùng sẽ phải chờ đợi lâu trước khi có thể tiếp tục tương tác. Điều này làm giảm trải nghiệm người dùng đáng kể. Tuy nhiên, bằng cách sử dụng các công cụ như GCD hoặc AsyncTask, chúng ta có thể đảm bảo rằng các tác vụ nặng nề sẽ không làm chậm tiến trình của toàn bộ ứng dụng. Điều này đặc biệt hữu ích khi phát triển các ứng dụng đa nhiệm, nơi mà hiệu suất và tốc độ phản hồi là yếu tố then chốt quyết định sự hài lòng của người dùng.

  3. Trong iOSđánh bài online, chúng ta có thể sử dụng các phương thức performSelectorXXX của lớp NSObject để đưa nhiệm vụ lên Run Loop của luồng mục tiêu và thực hiện nó một cách bất đồng (trừ phương thức performSelectorInBackground:withObject:). Tương tự như vậy, trên Android, chúng ta có thể sử dụng phương pháp post hoặc sendMessage của Handler hoặc phương thức post của View để đưa nhiệm vụ vào Run Loop tương ứng một cách bất đồng. Thực tế, bất kể là hệ điều hành iOS hay Android, trong kiến trúc cơ bản của phần mềm khách thường sẽ tạo ra một Run Loop cho luồng chính (tuy nhiên, các luồng khác cũng có thể tạo Run Loop). Điều này cho phép các luồng tồn tại lâu dài xử lý các nhiệm vụ ngắn gọn định kỳ, và khi không có nhiệm vụ nào cần thực hiện, nó sẽ chuyển sang trạng thái ngủ, vừa đảm bảo phản hồi sự kiện kịp thời vừa tiết kiệm tài nguyên CPU. Đồng thời, điều quan trọng hơn là mô hình Run Loop giúp đơn giản hóa logic lập trình đa luồng trong ứng dụng khách. Lập trình ứng dụng khách thường dễ dàng hơn so với lập trình máy chủ, một phần lớn là nhờ sự tồn tại củ Khi muốn thực hiện một nhiệm vụ đồng bộ dài hạn trong ứng dụng khách, chúng ta thường sẽ sử dụng cơ chế pool thread được đề cập ở (2) để đưa nhiệm vụ đó lên luồng bất đồng, sau khi nhiệm vụ hoàn tất, chúng ta sẽ sử dụng phương pháp điều độ Run Loop được đề cập trong phần này hoặc cơ chế GCD để tái phân phối nhiệm vụ trở lại luồng chính và Run Loop của nó. Cách tiếp cận này... Main thread -> Async thread -> Main thread Mô hình này cơ bản đã trở thành mô hình lập trình đa luồng cơ bản cho các ứng dụng client. Mô hình này giúp tránh được các thao tác đồng bộ phức tạp có thể xảy ra giữa nhiều luồngkeo nha cai hom nay, làm cho việc xử lý trở nên đơn giản hơn. Trong phần (ba) tiếp theo – về việc thực hiện nhiều nhiệm vụ bất đồng bộ, chúng ta sẽ có dịp thảo luận thêm về chủ đề này.

  4. Các tác vụ lên lịch trễ là những tác vụ được thiết lập để bắt đầu thực hiện sau một khoảng thời gian cụ thể hoặc tại một thời điểm nhất địnhđánh bài online, có thể được sử dụng để xây dựng các cấu trúc tương tự như hàng đợi retry. Có nhiều cách khác nhau để triển khai các tác vụ này. Trong iOS, bạn có thể sử dụng phương thức performSelector:withObject:afterDelay: của lớp NSObject, hoặc các hàm như dispatch_after và dispatch_time trong GCD (Grand Central Dispatch), ngoài ra còn có NSTimer. Đối với Android, bạn có thể sử dụng phương thức postDelayed và postAtTime của Handler, hoặc postDelayed của View, ngoài ra còn có Timer từ thư viện java.util; bên cạnh đó, Android cũng cung cấp một bộ điều độ nặng hơn gọi là AlarmService, cho phép kích hoạt ứng dụng khi nhiệm vụ được lên lịch đang chạy. Trong hệ sinh thái di động, việc chọn đúng công cụ để quản lý thời gian thực thi không chỉ giúp tăng hiệu suất mà còn đảm bảo tính ổn định của ứng dụng. Đặc biệt, đối với các tác vụ phức tạp đòi hỏi sự chính xác cao, AlarmService trên Android thường được ưu tiên vì khả năng hoạt động ngay cả khi ứng dụng đã bị tắt hoàn toàn. Ngược lại, các phương pháp như postDelayed trong Handler phù hợp hơn khi bạn cần quản lý các tác vụ ngắn hạn trong phạm vi hoạt động của ứng dụng. Về mặt lập trình, việc hiểu rõ cách hoạt động của từng công cụ sẽ giúp nhà phát triển tối ưu hóa mã nguồn, tránh các lỗi liên quan đến thời gian thực thi và cải thiện trải nghiệm người dùng tổng thể.

  5. Các hành vi bất đồng bộ liên quan đến hệ thống là vô sốkeo 88, và dưới đây là một số ví dụ điển hình. Đầu tiên, trong Android, khi bạn gọi phương thức startActivity, đó là một hoạt động bất đồng bộ. Ngay cả sau khi phương thức này được thực thi, vẫn cần một khoảng thời gian ngắn trước khi Activity mới được tạo ra và hiển thị trên màn hình. Tiếp theo, sự kiện trong vòng đời của Activity và Fragment cũng là một ví dụ về hành vi bất đồng bộ. Dù Activity đã đạt đến trạng thái onResume, bạn vẫn không thể chắc chắn rằng Fragment mà nó chứa bên trong đã hoàn thành vòng đời hay chưa, và giao diện người dùng (view hierarchy) của Fragment có đã được tạo hay chưa. Ngoài ra, trên cả hệ điều hành iOS và Android đều tồn tại cơ chế để lắng nghe sự thay đổi trạng thái mạng (như trong ví dụ mã trước đó), và việc callback được thực hiện khi mạng thay đổi chính là một ví dụ về sự bất đồng bộ. Tất cả những hành vi bất đồng bộ này đều yêu cầu cách xử lý đồng bộ hóa toàn diện để đảm bảo tính nhất quán trong hệ thống.

Xử lý bất đồng bộ trong phát triển Android và iOS

(Kết thúc)

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


Bài viết gốckeo 88, vui lòng ghi rõ 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: /m0tx23yg.html
Hãy theo dõi tài khoản Weibo cá nhân của tôi: Tìm kiếm tên "Trương Tiết Lệ" trên Weibo.
Tài khoản WeChat của tôi: tielei-blog (Trương Tiết Lệ)
Bài trước: Blog đã chuyển nhà rồi, ăn mừng nhé!
Bài sau: Những "hố" và vấn đề đơn hàng bị mất trong quá trình phát triển IAP của Apple

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