ลดผลกระทบของการอัปเดตเป็นประจำ

คำขอที่แอปของคุณสร้างกับเครือข่ายเป็นสาเหตุหลักที่ทำให้แบตเตอรี่หมดเร็ว เพราะอุปกรณ์เหล่านั้นเปิดวิทยุมือถือหรือ Wi-Fi ที่ใช้พลังงานมาก มากกว่าแค่ขุมพลัง ที่จำเป็นต่อการส่งและรับแพ็กเก็ต วิทยุเหล่านี้จะใช้พลังงานเพิ่มเติม เปิดและปลุกระบบค้างไว้ ของง่ายๆ อย่างคำขอเครือข่ายทุกๆ 15 ครั้ง วินาทีจะทำให้วิทยุมือถือเปิดได้อย่างต่อเนื่องและใช้แบตเตอรี่จนหมดอย่างรวดเร็ว ขั้นสูง

การอัปเดตเป็นประจำมีด้วยกัน 3 ประเภท ดังนี้

  • เริ่มต้นโดยผู้ใช้ การดำเนินการอัปเดตตามพฤติกรรมบางอย่างของผู้ใช้ เช่น ท่าทางสัมผัสการดึงเพื่อรีเฟรช
  • เริ่มต้นจากแอป ดำเนินการอัปเดตเป็นประจำ
  • เริ่มต้นจากเซิร์ฟเวอร์ ทำการอัปเดตตามการแจ้งเตือนจาก เซิร์ฟเวอร์

หัวข้อนี้จะอธิบายถึงสิ่งเหล่านี้และพูดถึงวิธีอื่นๆ ที่สามารถ ได้รับการเพิ่มประสิทธิภาพเพื่อลด การใช้แบตเตอรี่

เพิ่มประสิทธิภาพคำขอที่เริ่มต้นโดยผู้ใช้

คำขอที่เริ่มต้นโดยผู้ใช้มักเกิดขึ้นเพื่อตอบสนองต่อพฤติกรรมบางอย่างของผู้ใช้ สำหรับ เช่น แอปที่ใช้อ่านบทความข่าวล่าสุดอาจช่วยให้ผู้ใช้ ใช้ท่าทางสัมผัสแบบดึงเพื่อรีเฟรช เพื่อตรวจหาบทความใหม่ๆ คุณสามารถใช้ ในการตอบสนองต่อคำขอที่เริ่มโดยผู้ใช้ในขณะที่เพิ่มประสิทธิภาพ การใช้งานเครือข่าย

ควบคุมคำขอของผู้ใช้

คุณไม่จำเป็นต้องสนใจคำขอที่เริ่มต้นโดยผู้ใช้ในกรณีที่ไม่จำเป็นต้อง เช่น ท่าทางสัมผัสการดึงเพื่อรีเฟรชหลายๆ แบบในช่วงเวลาสั้นๆ เพื่อ ตรวจสอบข้อมูลใหม่ขณะที่ข้อมูลปัจจุบันยังใหม่อยู่ การดำเนินการในแต่ละ คำขออาจทำให้สิ้นเปลืองพลังงานมากเนื่องจากเปิดวิทยุค้างไว้ ต วิธีที่มีประสิทธิภาพมากกว่าคือควบคุมคำขอที่เริ่มโดยผู้ใช้เพื่อให้ คุณจะส่งคำขอได้เพียงรายการเดียวในช่วงเวลาหนึ่งเท่านั้น ซึ่งจะลดความถี่ที่ ของวิทยุ

ใช้แคช

การแคชข้อมูลของแอปจะเป็นการสร้างสำเนาข้อมูลในเครื่อง ที่แอปของคุณต้องอ้างอิง จากนั้นแอปของคุณจะสามารถเข้าถึง สำเนาข้อมูลหลายครั้งโดยไม่ต้องเปิดเครือข่าย เพื่อสร้างคำขอใหม่

คุณควรแคชข้อมูลให้มากที่สุดเท่าที่จะเป็นไปได้ รวมทั้งข้อมูลคงที่ ทรัพยากรและการดาวน์โหลดแบบออนดีมานด์ เช่น ภาพขนาดเต็ม คุณใช้ HTTP ได้ ส่วนหัวของแคชเพื่อให้มั่นใจว่ากลยุทธ์การแคชของคุณจะไม่ส่งผลกับแอป เพื่อแสดงข้อมูลที่ไม่มีการอัปเดต ดูข้อมูลเพิ่มเติมเกี่ยวกับการแคชการตอบกลับของเครือข่ายได้ที่ หลีกเลี่ยงการซ้ำซ้อน ที่ดาวน์โหลด

ใน Android 11 ขึ้นไป แอปสามารถใช้ชุดข้อมูลขนาดใหญ่แบบเดียวกับที่ผู้อื่น แอปสำหรับกรณีการใช้งานต่างๆ เช่น แมชชีนเลิร์นนิงและการเล่นสื่อ เมื่อ แอปจำเป็นต้องเข้าถึงชุดข้อมูลที่แชร์ โดยจะตรวจหาเวอร์ชันที่แคชไว้ก่อน ก่อนที่จะพยายามดาวน์โหลดสำเนาใหม่ หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับชุดข้อมูลที่แชร์ ดูเข้าถึงชุดข้อมูลที่แชร์

ใช้แบนด์วิดท์มากขึ้นเพื่อให้ดาวน์โหลดข้อมูลบ่อยขึ้น

เมื่อเชื่อมต่อผ่านวิทยุไร้สาย แบนด์วิดท์ที่สูงกว่ามักจะอยู่ที่ ต้นทุนแบตเตอรี่สูงขึ้น ซึ่งหมายความว่าโดยทั่วไปแล้ว 5G จะใช้พลังงานมากกว่า LTE ซึ่งทำให้ราคาแพงกว่า 3G

ซึ่งหมายความว่าในขณะที่สถานะของวิทยุเบื้องหลังจะแตกต่างกันไป เทคโนโลยีวิทยุ โดยทั่วไปจะพูดถึงผลกระทบจากแบตเตอรี่ที่สัมพันธ์กับสถานะ เวลาเปลี่ยนนานจะมากกว่าสำหรับวิทยุที่มีแบนด์วิดท์สูง สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ เวลาพิเศษ ให้ดูที่สถานะวิทยุ เครื่อง

ในขณะเดียวกัน แบนด์วิดท์ที่สูงขึ้นก็หมายความว่าคุณสามารถดึงข้อมูลล่วงหน้าได้มากขึ้น ในเชิงรุก การดาวน์โหลดข้อมูลให้มากขึ้นพร้อมๆ กัน อาจจะน้อยกว่า เนื่องจากค่าใช้จ่ายของแบตเตอรี่แบบ Tail-time ค่อนข้างสูงกว่า มีประสิทธิภาพมากขึ้นเพื่อให้วิทยุใช้งานได้เป็นเวลานานขึ้นในระหว่างการโอนแต่ละครั้ง เพื่อลดความถี่ในการอัปเดต

ตัวอย่างเช่น หากวิทยุ 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 แบบถาวร ซึ่งจะช่วยลดเวลา จำนวนการเชื่อมต่อถาวรและทำให้แพลตฟอร์มเพิ่มประสิทธิภาพแบนด์วิดท์ได้ และลดผลกระทบที่เกี่ยวข้องต่ออายุการใช้งานแบตเตอรี่