Các yêu cầu mà ứng dụng của bạn gửi đến mạng là nguyên nhân chính gây tiêu hao pin vì họ bật đài di động hoặc đài Wi-Fi tốn điện. Ngoài nguồn điện cần thiết để gửi và nhận gói, các đài này còn tiêu tốn thêm nguồn điện chỉ để bật và duy trì trạng thái thức. Việc gì đó đơn giản như một yêu cầu mạng cứ 15 giây có thể giữ cho đài phát trên thiết bị di động ở trạng thái bật liên tục và nhanh chóng làm tiêu hao pin nguồn.
Có 3 loại nội dung cập nhật định kỳ chung:
- Do người dùng khởi tạo. Cập nhật dựa trên một số hành vi của người dùng, chẳng hạn như cử chỉ kéo để làm mới.
- Do ứng dụng khởi tạo. Thực hiện cập nhật định kỳ.
- Do máy chủ khởi tạo. Cập nhật theo thông báo từ một máy chủ.
Chủ đề này xem xét từng phương pháp và thảo luận thêm về các cách khác có thể được được tối ưu hoá để giảm hiện tượng tiêu hao pin.
Tối ưu hoá các yêu cầu do người dùng khởi tạo
Các yêu cầu do người dùng đưa ra thường xuất hiện để phản hồi một số hành vi của người dùng. Ví dụ: một ứng dụng dùng để đọc các bài viết tin tức mới nhất có thể cho phép người dùng thực hiện cử chỉ kéo để làm mới để kiểm tra các bài viết mới. Bạn có thể sử dụng các kỹ thuật sau để phản hồi các yêu cầu do người dùng khởi tạo trong khi tối ưu hoá việc sử dụng mạng.
Hạn chế yêu cầu của người dùng
Bạn nên bỏ qua một số yêu cầu do người dùng đưa ra nếu không cần thiết chẳng hạn như nhiều cử chỉ kéo để làm mới trong một khoảng thời gian ngắn để kiểm tra dữ liệu mới trong khi dữ liệu hiện tại vẫn còn mới. Việc xử lý từng yêu cầu có thể làm lãng phí một lượng lớn năng lượng bằng cách giữ cho đài luôn thức. Một phương pháp hiệu quả hơn là điều tiết các yêu cầu do người dùng khởi tạo để chỉ có thể thực hiện một yêu cầu trong một khoảng thời gian, giảm tần suất sử dụng đài.
Sử dụng bộ nhớ đệm
Bằng cách lưu dữ liệu của ứng dụng vào bộ nhớ đệm, bạn đang tạo một bản sao thông tin trên thiết bị mà ứng dụng của bạn cần tham chiếu. Sau đó, ứng dụng của bạn có thể truy cập vào cùng một bản sao cục bộ của thông tin nhiều lần mà không cần phải mở kết nối mạng để tạo các yêu cầu mới.
Bạn nên lưu dữ liệu vào bộ nhớ đệm càng nhiều càng tốt, bao gồm cả dữ liệu tĩnh và các tệp tải xuống theo yêu cầu như hình ảnh với kích thước đầy đủ. Bạn có thể sử dụng HTTP tiêu đề bộ nhớ đệm để đảm bảo rằng chiến lược lưu vào bộ nhớ đệm không làm ứng dụng của bạn hiển thị dữ liệu cũ. Để biết thêm thông tin về cách lưu các phản hồi của mạng vào bộ nhớ đệm, hãy xem Tránh thừa nội dung tải xuống.
Trên Android 11 trở lên, ứng dụng của bạn có thể dùng cùng các tập dữ liệu lớn mà ứng dụng cho các trường hợp sử dụng như học máy và phát nội dung đa phương tiện. Khi ứng dụng cần truy cập vào một tập dữ liệu dùng chung. Trước tiên, ứng dụng này có thể kiểm tra phiên bản đã lưu vào bộ nhớ đệm trước khi thử tải bản sao mới xuống. Để tìm hiểu thêm về tập dữ liệu dùng chung, hãy xem phần Truy cập tập dữ liệu được chia sẻ.
Sử dụng băng thông lớn hơn để tải nhiều dữ liệu xuống ít thường xuyên hơn
Khi được kết nối qua vô tuyến không dây, băng thông cao hơn thường đạt đến chi phí pin cao hơn, nghĩa là 5G thường tốn nhiều năng lượng hơn so với LTE, nhưng mạng này lại đắt hơn 3G.
Điều này có nghĩa là mặc dù trạng thái vô tuyến cơ bản thay đổi tuỳ theo công nghệ vô tuyến, nhưng nói chung, tác động tương đối của pin đối với thời gian kết thúc thay đổi trạng thái sẽ lớn hơn đối với các đài phát có băng thông cao hơn. Để biết thêm thông tin về xem Trạng thái đài phát thanh máy tính bảng.
Đồng thời, băng thông cao hơn đồng nghĩa với việc bạn có thể tìm nạp trước được nhiều hơn tải xuống nhiều dữ liệu hơn trong cùng một thời điểm. Có thể ít hơn trực quan, vì chi phí pin tại thời điểm cuối tương đối cao hơn, nên hiệu quả hơn để duy trì đài phát hoạt động lâu hơn trong mỗi lần chuyển đài để giảm tần suất cập nhật.
Ví dụ: nếu một đài LTE có băng thông gấp đôi và chi phí năng lượng tăng gấp đôi của mạng 3G, bạn nên tải dữ liệu xuống nhiều gấp 4 lần trong mỗi phiên—hoặc có thể lên tới 10MB. Khi tải lượng dữ liệu lớn này xuống, bạn phải xem xét tác động của việc tìm nạp trước đối với dung lượng bộ nhớ cục bộ còn trống và kích hoạt thường xuyên tìm nạp trước bộ nhớ đệm.
Bạn có thể sử dụng
ConnectivityManager
để đăng ký
trình nghe cho mạng mặc định và
TelephonyManager
để đăng ký
PhoneStateListener
đến
xác định loại kết nối hiện tại của thiết bị. Sau khi xác định được loại kết nối,
bạn có thể sửa đổi quy trình tìm nạp trước cho phù hợp:
Kotlin
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val tm = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager private var hasWifi = false private var hasCellular = false private var cellModifier: Float = 1f private val networkCallback = object : ConnectivityManager.NetworkCallback() { // Network capabilities have changed for the network override fun onCapabilitiesChanged( network: Network, networkCapabilities: NetworkCapabilities ) { super.onCapabilitiesChanged(network, networkCapabilities) hasCellular = networkCapabilities .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) hasWifi = networkCapabilities .hasTransport(NetworkCapabilities.TRANSPORT_WIFI) } } private val phoneStateListener = object : PhoneStateListener() { override fun onPreciseDataConnectionStateChanged( dataConnectionState: PreciseDataConnectionState ) { cellModifier = when (dataConnectionState.networkType) { TelephonyManager.NETWORK_TYPE_LTE or TelephonyManager.NETWORK_TYPE_HSPAP -> 4f TelephonyManager.NETWORK_TYPE_EDGE or TelephonyManager.NETWORK_TYPE_GPRS -> 1/2f else -> 1f } } private class NetworkState { private var defaultNetwork: Network? = null private var defaultCapabilities: NetworkCapabilities? = null fun setDefaultNetwork(network: Network?, caps: NetworkCapabilities?) = synchronized(this) { defaultNetwork = network defaultCapabilities = caps } val isDefaultNetworkWifi get() = synchronized(this) { defaultCapabilities?.hasTransport(TRANSPORT_WIFI) ?: false } val isDefaultNetworkCellular get() = synchronized(this) { defaultCapabilities?.hasTransport(TRANSPORT_CELLULAR) ?: false } val isDefaultNetworkUnmetered get() = synchronized(this) { defaultCapabilities?.hasCapability(NET_CAPABILITY_NOT_METERED) ?: false } var cellNetworkType: Int = TelephonyManager.NETWORK_TYPE_UNKNOWN get() = synchronized(this) { field } set(t) = synchronized(this) { field = t } private val cellModifier: Float get() = synchronized(this) { when (cellNetworkType) { TelephonyManager.NETWORK_TYPE_LTE or TelephonyManager.NETWORK_TYPE_HSPAP -> 4f TelephonyManager.NETWORK_TYPE_EDGE or TelephonyManager.NETWORK_TYPE_GPRS -> 1 / 2f else -> 1f } } val prefetchCacheSize: Int get() = when { isDefaultNetworkWifi -> MAX_PREFETCH_CACHE isDefaultNetworkCellular -> (DEFAULT_PREFETCH_CACHE * cellModifier).toInt() else -> DEFAULT_PREFETCH_CACHE } } private val networkState = NetworkState() private val networkCallback = object : ConnectivityManager.NetworkCallback() { // Network capabilities have changed for the network override fun onCapabilitiesChanged( network: Network, networkCapabilities: NetworkCapabilities ) { networkState.setDefaultNetwork(network, networkCapabilities) } override fun onLost(network: Network?) { networkState.setDefaultNetwork(null, null) } } private val telephonyCallback = object : TelephonyCallback(), TelephonyCallback.PreciseDataConnectionStateListener { override fun onPreciseDataConnectionStateChanged(dataConnectionState: PreciseDataConnectionState) { networkState.cellNetworkType = dataConnectionState.networkType } } connectivityManager.registerDefaultNetworkCallback(networkCallback) telephonyManager.registerTelephonyCallback(telephonyCallback) private val prefetchCacheSize: Int get() { return when { hasWifi -> MAX_PREFETCH_CACHE hasCellular -> (DEFAULT_PREFETCH_CACHE * cellModifier).toInt() else -> DEFAULT_PREFETCH_CACHE } } }
Java
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); private boolean hasWifi = false; private boolean hasCellular = false; private float cellModifier = 1f; private ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onCapabilitiesChanged( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities ) { super.onCapabilitiesChanged(network, networkCapabilities); hasCellular = networkCapabilities .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); hasWifi = networkCapabilities .hasTransport(NetworkCapabilities.TRANSPORT_WIFI); } }; private PhoneStateListener phoneStateListener = new PhoneStateListener() { @Override public void onPreciseDataConnectionStateChanged( @NonNull PreciseDataConnectionState dataConnectionState ) { switch (dataConnectionState.getNetworkType()) { case (TelephonyManager.NETWORK_TYPE_LTE | TelephonyManager.NETWORK_TYPE_HSPAP): cellModifier = 4; Break; case (TelephonyManager.NETWORK_TYPE_EDGE | TelephonyManager.NETWORK_TYPE_GPRS): cellModifier = 1/2.0f; Break; default: cellModifier = 1; Break; } } }; cm.registerDefaultNetworkCallback(networkCallback); tm.listen( phoneStateListener, PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE ); public int getPrefetchCacheSize() { if (hasWifi) { return MAX_PREFETCH_SIZE; } if (hasCellular) { return (int) (DEFAULT_PREFETCH_SIZE * cellModifier); } return DEFAULT_PREFETCH_SIZE; }
Tối ưu hoá các yêu cầu do ứng dụng khởi tạo
Các yêu cầu do ứng dụng đưa ra thường diễn ra theo lịch biểu, chẳng hạn như một ứng dụng gửi nhật ký hoặc số liệu phân tích vào dịch vụ phụ trợ. Khi xử lý các yêu cầu do ứng dụng khởi tạo, hãy xem xét mức độ ưu tiên của các yêu cầu đó, liệu có thể gộp các yêu cầu đó lại với nhau hay không và liệu có thể trì hoãn các yêu cầu đó cho đến khi thiết bị sạc hoặc kết nối với mạng không đo lượng dữ liệu hay không. Bạn có thể tối ưu hoá các yêu cầu này bằng cách lên lịch cẩn thận và sử dụng các thư viện như WorkManager.
Yêu cầu mạng theo lô
Trên thiết bị di động, quá trình bật đài, tạo kết nối, và để đài ở chế độ bật sẽ tốn một lượng lớn năng lượng. Vì lý do này, việc xử lý từng yêu cầu riêng lẻ tại những thời điểm ngẫu nhiên có thể tốn nhiều năng lượng và giảm thời lượng pin. Một phương pháp hiệu quả hơn là đưa một tập hợp mạng vào hàng đợi yêu cầu và xử lý chúng cùng nhau. Điều này cho phép hệ thống chỉ trả chi phí điện năng khi bật đài phát một lần và vẫn nhận được tất cả dữ liệu mà ứng dụng yêu cầu.
Sử dụng WorkManager
Bạn có thể sử dụng thư viện WorkManager
để thực hiện công việc theo một lịch biểu hiệu quả
xem xét liệu có đáp ứng các điều kiện cụ thể hay không, chẳng hạn như khả năng sử dụng mạng
và trạng thái nguồn điện. Ví dụ: giả sử bạn có một lớp con Worker
có tên là DownloadHeadlinesWorker
để truy xuất các dòng tiêu đề tin tức mới nhất. Trình chạy này
có thể được lên lịch chạy mỗi giờ, miễn là thiết bị được kết nối với
mạng không đo lượng dữ liệu và pin của thiết bị không yếu, nhờ chiến lược thử lại tuỳ chỉnh
nếu có bất kỳ vấn đề nào khi truy xuất dữ liệu, như được hiển thị dưới đây:
Kotlin
val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) .setRequiresBatteryNotLow(true) .build() val request = PeriodicWorkRequestBuilder<DownloadHeadlinesWorker>(1, TimeUnit.HOURS) .setConstraints(constraints) .setBackoffCriteria(BackoffPolicy.LINEAR, 1L, TimeUnit.MINUTES) .build() WorkManager.getInstance(context).enqueue(request)
Java
Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) .setRequiresBatteryNotLow(true) .build(); WorkRequest request = new PeriodicWorkRequest.Builder(DownloadHeadlinesWorker.class, 1, TimeUnit.HOURS) .setBackoffCriteria(BackoffPolicy.LINEAR, 1L, TimeUnit.MINUTES) .build(); WorkManager.getInstance(this).enqueue(request);
Ngoài WorkManager, nền tảng Android còn cung cấp một số công cụ khác giúp bạn tạo lịch biểu hiệu quả để hoàn thành các tác vụ liên quan đến mạng, chẳng hạn như thăm dò ý kiến. Để tìm hiểu thêm về cách sử dụng các công cụ này, hãy xem Hướng dẫn xử lý ở chế độ nền.
Tối ưu hoá các yêu cầu do máy chủ khởi tạo
Các yêu cầu do máy chủ khởi tạo thường xảy ra để phản hồi thông báo từ máy chủ. Ví dụ: một ứng dụng dùng để đọc các tin bài mới nhất có thể nhận được thông báo về một loạt bài viết mới phù hợp với các lựa chọn ưu tiên của cá nhân hoá, sau đó hệ thống sẽ tải dữ liệu này xuống.
Gửi bản cập nhật máy chủ bằng Giải pháp gửi thông báo qua đám mây của Firebase
Giải pháp gửi thông báo qua đám mây của Firebase (FCM) là một cơ chế gọn nhẹ dùng để truyền dữ liệu từ máy chủ đến một thực thể ứng dụng cụ thể. Bằng cách sử dụng FCM, máy chủ có thể thông báo cho ứng dụng đang chạy trên một thiết bị cụ thể rằng có dữ liệu mới cho nó.
So với việc thăm dò ý kiến, trong đó ứng dụng của bạn phải thường xuyên ping máy chủ để truy vấn dữ liệu mới, mô hình dựa trên sự kiện này cho phép ứng dụng của bạn tạo kết nối mới chỉ khi biết có dữ liệu để tải xuống. Mô hình giảm thiểu kết nối và giảm độ trễ khi cập nhật thông tin trong ứng dụng.
FCM được triển khai bằng kết nối TCP/IP liên tục. Điều này giúp giảm thiểu số lượng kết nối liên tục và cho phép nền tảng tối ưu hoá băng thông cũng như giảm thiểu tác động liên quan đến thời lượng pin.