คำขอที่แอปของคุณสร้างกับเครือข่ายเป็นสาเหตุหลักที่ทำให้แบตเตอรี่หมดเร็ว เพราะอุปกรณ์เหล่านั้นเปิดวิทยุมือถือหรือ Wi-Fi ที่ใช้พลังงานมาก เหนือชั้น ที่จำเป็นต่อการส่งและรับแพ็กเก็ต วิทยุเหล่านี้จะใช้พลังงานเพิ่มเติม เปิดและปลุกระบบค้างไว้ การดำเนินการง่ายๆ อย่างคำขอเครือข่ายทุกๆ 15 วินาทีอาจทำให้วิทยุมือถือเปิดอยู่อย่างต่อเนื่องและทำให้แบตเตอรี่หมดเร็ว
การอัปเดตเป็นประจำมีด้วยกัน 3 ประเภท ดังนี้
- เริ่มต้นโดยผู้ใช้ ทำการอัปเดตตามพฤติกรรมบางอย่างของผู้ใช้ เช่น ท่าทางสัมผัสเพื่อรีเฟรช
- เริ่มต้นโดยแอป ทำการอัปเดตเป็นประจำ
- เริ่มต้นจากเซิร์ฟเวอร์ ทำการอัปเดตตามการแจ้งเตือนจาก เซิร์ฟเวอร์
หัวข้อนี้จะอธิบายถึงสิ่งเหล่านี้และพูดถึงวิธีอื่นๆ ที่สามารถ ได้รับการเพิ่มประสิทธิภาพเพื่อลด การใช้แบตเตอรี่
เพิ่มประสิทธิภาพคำขอที่เริ่มต้นโดยผู้ใช้
โดยปกติแล้ว คำขอที่ผู้ใช้เริ่มจะตอบสนองต่อพฤติกรรมบางอย่างของผู้ใช้ เช่น แอปที่ใช้อ่านบทความข่าวล่าสุดอาจอนุญาตให้ผู้ใช้ใช้ท่าทางสัมผัส "ปัดเพื่อรีเฟรช" เพื่อตรวจหาบทความใหม่ คุณสามารถใช้ ในการตอบสนองต่อคำขอที่เริ่มโดยผู้ใช้ในขณะที่เพิ่มประสิทธิภาพ การใช้งานเครือข่าย
ควบคุมคำขอของผู้ใช้
คุณไม่จำเป็นต้องสนใจคำขอที่เริ่มต้นโดยผู้ใช้ในกรณีที่ไม่จำเป็นต้อง เช่น ท่าทางสัมผัสการดึงเพื่อรีเฟรชหลายๆ แบบในช่วงเวลาสั้นๆ เพื่อ ตรวจสอบข้อมูลใหม่ขณะที่ข้อมูลปัจจุบันยังใหม่อยู่ การดำเนินการในแต่ละ คำขออาจทำให้สิ้นเปลืองพลังงานมากเนื่องจากเปิดวิทยุค้างไว้ ต วิธีที่มีประสิทธิภาพมากกว่าคือควบคุมคำขอที่เริ่มโดยผู้ใช้เพื่อให้ คุณจะส่งคำขอได้เพียงรายการเดียวในช่วงเวลาหนึ่งเท่านั้น ซึ่งจะลดความถี่ที่ ของวิทยุ
ใช้แคช
การแคชข้อมูลของแอปจะเป็นการสร้างสำเนาข้อมูลในเครื่อง ที่แอปของคุณต้องอ้างอิง จากนั้นแอปของคุณจะสามารถเข้าถึง สำเนาข้อมูลหลายครั้งโดยไม่ต้องเปิดเครือข่าย เพื่อสร้างคำขอใหม่
คุณควรแคชข้อมูลให้มากที่สุดเท่าที่จะเป็นไปได้ รวมทั้งข้อมูลคงที่ ทรัพยากรและการดาวน์โหลดแบบออนดีมานด์ เช่น ภาพขนาดเต็ม คุณใช้ HTTP ได้ ส่วนหัวของแคชเพื่อให้มั่นใจว่ากลยุทธ์การแคชของคุณจะไม่ส่งผลกับแอป เพื่อแสดงข้อมูลที่ไม่มีการอัปเดต ดูข้อมูลเพิ่มเติมเกี่ยวกับการแคชการตอบกลับของเครือข่ายได้ที่ หลีกเลี่ยงการซ้ำซ้อน ที่ดาวน์โหลด
ใน Android 11 ขึ้นไป แอปของคุณจะใช้ชุดข้อมูลขนาดใหญ่เดียวกันกับที่แอปอื่นๆ ใช้สำหรับกรณีการใช้งาน เช่น แมชชีนเลิร์นนิงและการเล่นสื่อ เมื่อ แอปจำเป็นต้องเข้าถึงชุดข้อมูลที่แชร์ โดยจะตรวจหาเวอร์ชันที่แคชไว้ก่อน ก่อนที่จะพยายามดาวน์โหลดสำเนาใหม่ ดูข้อมูลเพิ่มเติมเกี่ยวกับชุดข้อมูลที่แชร์ได้ที่เข้าถึงชุดข้อมูลที่แชร์
ใช้แบนด์วิดท์มากขึ้นเพื่อดาวน์โหลดข้อมูลมากขึ้นแต่ให้นานขึ้น
เมื่อเชื่อมต่อผ่านวิทยุไร้สาย แบนด์วิดท์ที่สูงกว่ามักจะอยู่ที่ ต้นทุนแบตเตอรี่สูงขึ้น ซึ่งหมายความว่าโดยทั่วไปแล้ว 5G จะใช้พลังงานมากกว่า LTE ซึ่งทำให้ราคาแพงกว่า 3G
ซึ่งหมายความว่าแม้ว่าสถานะวิทยุพื้นฐานจะแตกต่างกันไปตามเทคโนโลยีวิทยุ แต่โดยทั่วไปแล้วผลกระทบของแบตเตอรี่แบบสัมพัทธ์ของเวลาสิ้นสุดการเปลี่ยนแปลงสถานะจะมากกว่าสำหรับวิทยุที่มีแบนด์วิดท์สูงกว่า สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ เวลาพิเศษ ให้ดูที่สถานะวิทยุ เครื่อง
ในขณะเดียวกัน แบนด์วิดท์ที่สูงขึ้นก็หมายความว่าคุณสามารถดึงข้อมูลล่วงหน้าได้มากขึ้น ในเชิงรุก การดาวน์โหลดข้อมูลให้มากขึ้นพร้อมๆ กัน สิ่งหนึ่งที่อาจไม่ค่อยชัดเจนนักคือ เนื่องจากต้นทุนแบตเตอรี่ในช่วงท้ายค่อนข้างสูงกว่า การเปิดใช้วิทยุเป็นเวลานานขึ้นในช่วงเซสชันการโอนแต่ละครั้งจึงมีประสิทธิภาพมากกว่าเพื่อลดความถี่ในการอัปเดต
ตัวอย่างเช่น หากวิทยุ LTE มีแบนด์วิดท์เป็น 2 เท่าและมีค่าพลังงานเพิ่มขึ้น 2 เท่า ของ 3G คุณควรดาวน์โหลดข้อมูลมากกว่า 4 เท่าในแต่ละเซสชัน หรือ อาจมากถึง 10 MB เมื่อดาวน์โหลดข้อมูลปริมาณมากนี้ คุณควร ลองพิจารณาผลกระทบของการดึงข้อมูลล่วงหน้าที่มีต่อพื้นที่เก็บข้อมูลในเครื่องและการล้างออก เพื่อเรียกข้อมูลแคชล่วงหน้าเป็นประจำ
คุณสามารถใช้
ConnectivityManager
เพื่อลงทะเบียน
Listener สำหรับเครือข่ายเริ่มต้นและ
TelephonyManager
เพื่อลงทะเบียน
PhoneStateListener
ถึง
ระบุประเภทการเชื่อมต่ออุปกรณ์ปัจจุบัน เมื่อทราบประเภทการเชื่อมต่อแล้ว คุณก็แก้ไขกิจวัตรการอ่านล่วงหน้าได้ตามต้องการ ดังนี้
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; }
เพิ่มประสิทธิภาพคำขอที่เริ่มต้นโดยแอป
คำขอที่เริ่มต้นโดยแอปมักเกิดขึ้นตามกำหนดเวลา เช่น แอปที่ส่ง บันทึกหรือข้อมูลวิเคราะห์ไปยังบริการแบ็กเอนด์ เมื่อจัดการกับคำขอที่เริ่มต้นโดยแอป ให้พิจารณาลำดับความสำคัญของคำขอเหล่านั้น พิจารณาว่าจะรวมคำขอเหล่านั้นเข้าด้วยกันได้หรือไม่ และพิจารณาว่าจะเลื่อนคำขอออกไปจนกว่าอุปกรณ์จะชาร์จหรือเชื่อมต่อกับเครือข่ายที่ไม่มีบริการวัดปริมาณอินเทอร์เน็ตหรือไม่ คำขอเหล่านี้สามารถเพิ่มประสิทธิภาพได้ด้วยความระมัดระวัง การสร้างกำหนดการและใช้ไลบรารี เช่น WorkManager
คำขอเครือข่ายเป็นกลุ่ม
ขั้นตอนการเปิดวิทยุ การเชื่อมต่อ และการเปิดวิทยุค้างไว้ จะใช้พลังงานมาก ด้วยเหตุนี้ การประมวลผลคำขอแต่ละรายการในเวลาแบบสุ่มอาจใช้พลังงานมาก และลดอายุการใช้งานแบตเตอรี่ วิธีที่มีประสิทธิภาพมากกว่าคือการจัดคิวชุดเครือข่าย คำขอและประมวลผลพร้อมกัน วิธีนี้จะช่วยให้ระบบจ่ายไฟ ค่าใช้จ่ายในการเปิดวิทยุเพียงครั้งเดียว และยังได้รับข้อมูลทั้งหมดตามที่ขอโดย แอป
ใช้ WorkManager
คุณสามารถใช้ไลบรารี WorkManager
เพื่อทำงานตามกำหนดเวลาที่มีประสิทธิภาพได้
ที่พิจารณาว่าเป็นไปตามเงื่อนไขเฉพาะหรือไม่ เช่น ความพร้อมใช้งานของเครือข่าย
และสถานะพลังงาน ตัวอย่างเช่น สมมติว่าคุณมี
เรียกคลาสย่อย Worker
แล้ว
DownloadHeadlinesWorker
ที่ดึงพาดหัวข่าวล่าสุด ผู้ปฏิบัติงานนี้
สามารถตั้งเวลาให้ทํางานทุกชั่วโมงได้ในกรณีที่อุปกรณ์เชื่อมต่อกับ
เครือข่ายที่ไม่มีการตรวจวัดและแบตเตอรี่ของอุปกรณ์เหลือน้อย โดยมีกลยุทธ์การลองอีกครั้งที่กำหนดเอง
หากมีปัญหาใดๆ ในการเรียกข้อมูล ตามที่แสดงด้านล่าง:
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);
นอกเหนือจาก WorkManager แพลตฟอร์ม Android ยังมีเครื่องมืออื่นๆ อีกมากมาย เพื่อช่วยสร้างกำหนดการที่มีประสิทธิภาพในการทำงานด้านเครือข่ายให้สำเร็จ เช่น ว่าเป็นแบบสำรวจ ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้เครื่องมือเหล่านี้ได้ที่ คำแนะนำเกี่ยวกับการประมวลผลในเบื้องหลัง
เพิ่มประสิทธิภาพคำขอที่เริ่มต้นโดยเซิร์ฟเวอร์
คำขอที่เริ่มต้นโดยเซิร์ฟเวอร์มักเกิดขึ้นเพื่อตอบสนองต่อการแจ้งเตือนจาก เซิร์ฟเวอร์ ตัวอย่างเช่น แอปพลิเคชันที่ใช้อ่านบทความข่าวล่าสุดอาจได้รับ การแจ้งเตือนเกี่ยวกับบทความกลุ่มใหม่ที่ตรงกับ ค่ากำหนดการปรับเปลี่ยนในแบบของคุณ ซึ่งระบบจะดาวน์โหลดมาต่อ
ส่งการอัปเดตเซิร์ฟเวอร์ด้วย Firebase Cloud Messaging
การรับส่งข้อความในระบบคลาวด์ของ Firebase (FCM) เป็นโซลูชันที่ใช้งานง่าย ที่ใช้ส่งข้อมูลจากเซิร์ฟเวอร์ไปยังอินสแตนซ์ของแอปหนึ่งๆ เมื่อใช้ FCM เซิร์ฟเวอร์ของคุณสามารถแจ้งเตือนแอปของคุณที่ทำงานอยู่บนอุปกรณ์หนึ่งๆ ที่ มีข้อมูลใหม่พร้อมใช้งาน
เมื่อเทียบกับแบบสำรวจที่แอปต้องใช้คำสั่ง ping กับเซิร์ฟเวอร์เป็นประจำเพื่อค้นหา ข้อมูลใหม่ รูปแบบที่ขับเคลื่อนด้วยเหตุการณ์นี้ช่วยให้แอปของคุณสร้างการเชื่อมต่อใหม่ได้ เฉพาะเมื่อรู้ว่ามีข้อมูลให้ดาวน์โหลด โมเดลลดความจำเป็น การเชื่อมต่อและลดเวลาในการตอบสนองเมื่ออัปเดตข้อมูลภายในแอป
FCM มีการใช้งานโดยใช้การเชื่อมต่อ TCP/IP แบบถาวร วิธีนี้ช่วยลดจำนวนการเชื่อมต่อแบบถาวรและช่วยให้แพลตฟอร์มเพิ่มประสิทธิภาพแบนด์วิดท์และลดผลกระทบต่ออายุการใช้งานแบตเตอรี่