নিয়মিত আপডেটের প্রভাব কমিয়ে দিন

আপনার অ্যাপের নেটওয়ার্কে করা অনুরোধগুলি ব্যাটারি খরচের একটি প্রধান কারণ কারণ এগুলি বিদ্যুৎ গ্রহণকারী সেলুলার বা ওয়াই-ফাই রেডিও চালু করে। প্যাকেট পাঠানো এবং গ্রহণ করার জন্য প্রয়োজনীয় শক্তির বাইরে, এই রেডিওগুলি কেবল চালু করে এবং জাগ্রত রেখে অতিরিক্ত শক্তি ব্যয় করে। প্রতি 15 সেকেন্ডে নেটওয়ার্ক অনুরোধের মতো সহজ কিছু মোবাইল রেডিওকে ক্রমাগত চালু রাখতে এবং দ্রুত ব্যাটারির শক্তি ব্যবহার করতে পারে।

নিয়মিত আপডেটের তিনটি সাধারণ ধরণ রয়েছে:

  • ব্যবহারকারীর দ্বারা শুরু। কিছু ব্যবহারকারীর আচরণের উপর ভিত্তি করে একটি আপডেট সম্পাদন করা হচ্ছে, যেমন পুল-টু-রিফ্রেশ অঙ্গভঙ্গি।
  • অ্যাপ-ইনিশিয়েটেড। বারবার আপডেট করা হচ্ছে।
  • সার্ভার-ইনিশিয়েটেড। সার্ভার থেকে আসা বিজ্ঞপ্তির প্রতিক্রিয়ায় একটি আপডেট সম্পাদন করা হচ্ছে।

এই বিষয়টি এই প্রতিটি বিষয়ের উপর আলোকপাত করে এবং ব্যাটারির অপচয় কমাতে এগুলোকে অপ্টিমাইজ করার অতিরিক্ত উপায় নিয়ে আলোচনা করে।

ব্যবহারকারীর দ্বারা শুরু করা অনুরোধগুলি অপ্টিমাইজ করুন

ব্যবহারকারীর দ্বারা শুরু করা অনুরোধগুলি সাধারণত কিছু ব্যবহারকারীর আচরণের প্রতিক্রিয়ায় ঘটে। উদাহরণস্বরূপ, সাম্প্রতিক সংবাদ নিবন্ধগুলি পড়ার জন্য ব্যবহৃত একটি অ্যাপ ব্যবহারকারীকে নতুন নিবন্ধগুলি পরীক্ষা করার জন্য একটি পুল-টু-রিফ্রেশ অঙ্গভঙ্গি সম্পাদন করার অনুমতি দিতে পারে। নেটওয়ার্ক ব্যবহার অপ্টিমাইজ করার সময় ব্যবহারকারীর দ্বারা শুরু করা অনুরোধগুলির প্রতিক্রিয়া জানাতে আপনি নিম্নলিখিত কৌশলগুলি ব্যবহার করতে পারেন।

থ্রটল ব্যবহারকারীর অনুরোধ

ব্যবহারকারীর দ্বারা প্রেরিত কিছু অনুরোধের যদি কোনও প্রয়োজন না থাকে, তাহলে আপনি সেগুলো উপেক্ষা করতে পারেন, যেমন বর্তমান ডেটা তাজা থাকাকালীন নতুন ডেটা পরীক্ষা করার জন্য অল্প সময়ের জন্য একাধিক পুল-টু-রিফ্রেশ জেসচার। প্রতিটি অনুরোধের উপর কাজ করলে রেডিওকে জাগ্রত রেখে উল্লেখযোগ্য পরিমাণে শক্তি অপচয় হতে পারে। আরও কার্যকর পদ্ধতি হল ব্যবহারকারীর দ্বারা প্রেরিত অনুরোধগুলিকে থ্রোটল করা যাতে নির্দিষ্ট সময়ের মধ্যে শুধুমাত্র একটি অনুরোধ করা যায়, যার ফলে রেডিও ব্যবহারের ঘন ঘন হ্রাস পায়।

একটি ক্যাশে ব্যবহার করুন

আপনার অ্যাপের ডেটা ক্যাশে করে, আপনি সেই তথ্যের একটি স্থানীয় কপি তৈরি করছেন যা আপনার অ্যাপের রেফারেন্সের জন্য প্রয়োজন। এরপর আপনার অ্যাপটি নতুন অনুরোধ করার জন্য কোনও নেটওয়ার্ক সংযোগ না খুলেই একাধিকবার তথ্যের একই স্থানীয় কপি অ্যাক্সেস করতে পারবে।

আপনার যতটা সম্ভব আক্রমণাত্মকভাবে ডেটা ক্যাশে করা উচিত, যার মধ্যে স্ট্যাটিক রিসোর্স এবং পূর্ণ-আকারের চিত্রের মতো অন-ডিমান্ড ডাউনলোড অন্তর্ভুক্ত রয়েছে। আপনার ক্যাশিং কৌশলের ফলে আপনার অ্যাপটি যাতে পুরানো ডেটা প্রদর্শন না করে তা নিশ্চিত করতে আপনি HTTP ক্যাশ হেডার ব্যবহার করতে পারেন। নেটওয়ার্ক প্রতিক্রিয়া ক্যাশিং সম্পর্কে আরও তথ্যের জন্য, অপ্রয়োজনীয় ডাউনলোডগুলি এড়িয়ে চলুন দেখুন।

Android 11 এবং তার পরবর্তী ভার্সনে, আপনার অ্যাপটি মেশিন লার্নিং এবং মিডিয়া প্লেব্যাকের মতো ব্যবহারের ক্ষেত্রে অন্যান্য অ্যাপগুলি যে বৃহৎ ডেটাসেট ব্যবহার করে, সেই একই বৃহৎ ডেটাসেট ব্যবহার করতে পারে। যখন আপনার অ্যাপের একটি শেয়ার করা ডেটাসেট অ্যাক্সেস করার প্রয়োজন হয়, তখন এটি প্রথমে একটি নতুন কপি ডাউনলোড করার চেষ্টা করার আগে একটি ক্যাশ করা সংস্করণ পরীক্ষা করতে পারে। শেয়ার করা ডেটাসেট সম্পর্কে আরও জানতে, শেয়ার করা ডেটাসেট অ্যাক্সেস করুন দেখুন।

কম ঘন ঘন বেশি ডেটা ডাউনলোড করতে বেশি ব্যান্ডউইথ ব্যবহার করুন

ওয়্যারলেস রেডিওর মাধ্যমে সংযুক্ত হলে, উচ্চ ব্যান্ডউইথ সাধারণত উচ্চ ব্যাটারি খরচের মূল্যে আসে, যার অর্থ 5G সাধারণত LTE এর চেয়ে বেশি শক্তি খরচ করে, যা 3G এর চেয়ে বেশি ব্যয়বহুল।

এর মানে হল যে যদিও অন্তর্নিহিত রেডিও অবস্থা রেডিও প্রযুক্তির উপর ভিত্তি করে পরিবর্তিত হয়, সাধারণভাবে বলতে গেলে, উচ্চ ব্যান্ডউইথ রেডিওর ক্ষেত্রে টেল-টাইমের অবস্থা পরিবর্তনের আপেক্ষিক ব্যাটারি প্রভাব বেশি। টেল-টাইম সম্পর্কে আরও তথ্যের জন্য, রেডিও স্টেট মেশিন দেখুন।

একই সময়ে, উচ্চ ব্যান্ডউইথের অর্থ হল আপনি আরও আক্রমণাত্মকভাবে প্রিফেচ করতে পারবেন, একই সময়ে আরও ডেটা ডাউনলোড করতে পারবেন। সম্ভবত কম স্বজ্ঞাতভাবে, কারণ টেল-টাইম ব্যাটারির খরচ তুলনামূলকভাবে বেশি, আপডেটের ফ্রিকোয়েন্সি কমাতে প্রতিটি ট্রান্সফার সেশনের সময় রেডিওকে দীর্ঘ সময়ের জন্য সক্রিয় রাখা আরও দক্ষ।

উদাহরণস্বরূপ, যদি একটি LTE রেডিওতে ব্যান্ডউইথ দ্বিগুণ এবং 3G এর শক্তি খরচ দ্বিগুণ হয়, তাহলে আপনার প্রতিটি সেশনের সময় চারগুণ বেশি ডেটা ডাউনলোড করা উচিত—অথবা সম্ভাব্যভাবে 10MB পর্যন্ত। এই পরিমাণ ডেটা ডাউনলোড করার সময়, উপলব্ধ স্থানীয় স্টোরেজের উপর আপনার প্রিফেচিংয়ের প্রভাব বিবেচনা করা এবং নিয়মিতভাবে আপনার প্রিফেচ ক্যাশে পরিষ্কার করা গুরুত্বপূর্ণ।

আপনি ডিফল্ট নেটওয়ার্কের জন্য একটি শ্রোতা নিবন্ধন করতে ConnectivityManager ব্যবহার করতে পারেন, এবং বর্তমান ডিভাইস সংযোগের ধরণ নির্ধারণ করতে TelephonyManager একটি PhoneStateListener নিবন্ধন করতে পারেন। সংযোগের ধরণটি জানা হয়ে গেলে, আপনি সেই অনুযায়ী আপনার প্রিফেচিং রুটিনগুলি পরিবর্তন করতে পারেন:

কোটলিন

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
    }
}

}

জাভা

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 লাইব্রেরি ব্যবহার করতে পারেন। উদাহরণস্বরূপ, ধরুন আপনার কাছে DownloadHeadlinesWorker নামক একটি Worker সাবক্লাস আছে যা সর্বশেষ সংবাদ শিরোনামগুলি পুনরুদ্ধার করে। এই কর্মীটি প্রতি ঘন্টায় চালানোর জন্য নির্ধারিত হতে পারে, যদি ডিভাইসটি একটি অ-মিটারযুক্ত নেটওয়ার্কের সাথে সংযুক্ত থাকে এবং ডিভাইসের ব্যাটারি কম না থাকে, তবে ডেটা পুনরুদ্ধারে কোনও সমস্যা হলে একটি কাস্টম পুনরায় চেষ্টা কৌশল ব্যবহার করে, যেমনটি নীচে দেখানো হয়েছে:

কোটলিন

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)

জাভা

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);

ওয়ার্কম্যানেজার ছাড়াও, অ্যান্ড্রয়েড প্ল্যাটফর্মটি পোলিং এর মতো নেটওয়ার্কিং কাজগুলি সম্পন্ন করার জন্য একটি দক্ষ সময়সূচী তৈরি করতে আপনাকে সাহায্য করার জন্য আরও বেশ কয়েকটি সরঞ্জাম সরবরাহ করে। এই সরঞ্জামগুলি ব্যবহার সম্পর্কে আরও জানতে, ব্যাকগ্রাউন্ড প্রক্রিয়াকরণের নির্দেশিকা দেখুন।

সার্ভার-ইনিশিয়েটেড অনুরোধগুলি অপ্টিমাইজ করুন

সার্ভার-ইনিশিয়েটেড অনুরোধগুলি সাধারণত সার্ভার থেকে একটি বিজ্ঞপ্তির প্রতিক্রিয়ায় ঘটে। উদাহরণস্বরূপ, সাম্প্রতিক সংবাদ নিবন্ধগুলি পড়ার জন্য ব্যবহৃত একটি অ্যাপ ব্যবহারকারীর ব্যক্তিগতকরণ পছন্দগুলির সাথে মানানসই নতুন নিবন্ধগুলির একটি ব্যাচ সম্পর্কে একটি বিজ্ঞপ্তি পেতে পারে, যা এটি পরে ডাউনলোড করে।

ফায়ারবেস ক্লাউড মেসেজিং ব্যবহার করে সার্ভার আপডেট পাঠান

ফায়ারবেস ক্লাউড মেসেজিং (FCM) হল একটি হালকা প্রক্রিয়া যা একটি সার্ভার থেকে একটি নির্দিষ্ট অ্যাপ ইনস্ট্যান্সে ডেটা প্রেরণ করতে ব্যবহৃত হয়। FCM ব্যবহার করে, আপনার সার্ভার একটি নির্দিষ্ট ডিভাইসে চলমান আপনার অ্যাপটিকে অবহিত করতে পারে যে এর জন্য নতুন ডেটা উপলব্ধ।

পোলিংয়ের তুলনায়, যেখানে আপনার অ্যাপকে নতুন ডেটার জন্য সার্ভারে নিয়মিত পিং করতে হয়, এই ইভেন্ট-চালিত মডেলটি আপনার অ্যাপকে কেবল তখনই একটি নতুন সংযোগ তৈরি করতে দেয় যখন এটি জানে যে ডাউনলোড করার জন্য ডেটা আছে। এই মডেলটি অপ্রয়োজনীয় সংযোগ কমিয়ে আনে এবং আপনার অ্যাপের মধ্যে তথ্য আপডেট করার সময় লেটেন্সি কমায়।

FCM একটি স্থায়ী TCP/IP সংযোগ ব্যবহার করে বাস্তবায়িত হয়। এটি স্থায়ী সংযোগের সংখ্যা কমিয়ে দেয় এবং প্ল্যাটফর্মটিকে ব্যান্ডউইথ অপ্টিমাইজ করতে এবং ব্যাটারি লাইফের উপর সম্পর্কিত প্রভাব কমাতে সহায়তা করে।