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

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

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

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

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

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

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

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

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

ক্যাশে ব্যবহার করুন

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

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

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

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

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

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

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

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

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

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

}

জাভা

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

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)

জাভা

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 Cloud Messaging (FCM) হল একটি লাইটওয়েট মেকানিজম যা সার্ভার থেকে কোনও নির্দিষ্ট অ্যাপ ইনস্ট্যান্সে ডেটা ট্রান্সমিট করতে ব্যবহার করা হয়। FCM ব্যবহার করে, আপনার সার্ভার কোনও নির্দিষ্ট ডিভাইসে চলা আপনার অ্যাপকে জানাতে পারে যে এটির জন্য নতুন ডেটা উপলভ্য আছে।

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

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