Ağ kullanımını yönetme

Bu derste, ağ kaynaklarının kullanımı üzerinde ayrıntılı kontrolü olan uygulamaların nasıl yazılacağı açıklanmaktadır. Uygulamanız çok sayıda ağ işlemi gerçekleştiriyorsa kullanıcıların, uygulamanızın veri alışkanlıklarını kontrol etmesine olanak tanıyan kullanıcı ayarları sağlamanız gerekir. Bu ayarlar, uygulamanızın veri senkronizasyonu yapma sıklığı, yükleme/indirme işlemlerinin yalnızca kablosuz ağa bağlıyken gerçekleştirilip gerçekleştirilmeyeceği ve dolaşım sırasında veri kullanılıp kullanılmayacağı gibi konular olabilir. Bu kontroller sayesinde kullanıcıların, sınırlarına yaklaştıklarında uygulamanızın arka plan verilerine erişimini devre dışı bırakma olasılıkları çok daha düşüktür. Çünkü kullanıcılar, uygulamanızın ne kadar veri kullandığını hassas bir şekilde kontrol edebilirler.

Belirli bir süre içindeki ağ bağlantılarının sayısı ve türleri de dahil olmak üzere uygulamanızın ağ kullanımı hakkında daha fazla bilgi edinmek için Web uygulamaları ve Ağ profil aracı ile ağ trafiğini inceleme başlıklı makaleleri okuyun. İndirme işlemlerinin ve ağ bağlantılarının pil ömrü üzerindeki etkisini en aza indiren uygulamaların nasıl yazılacağıyla ilgili genel yönergeler için Pil ömrünü optimize etme ve Pili bitirmeden veri aktarma bölümlerine göz atın.

NetworkConnect örneğine de göz atabilirsiniz.

Cihazın ağ bağlantısını kontrol etme

Bir cihazın farklı türde ağ bağlantıları olabilir. Bu derste bir kablosuz ağ ya da mobil ağ bağlantısı kullanmaya odaklanacağız. Olası ağ türlerinin tam listesi için ConnectivityManager bölümüne bakın.

Kablosuz bağlantı özelliği genellikle daha hızlıdır. Ayrıca, mobil veriler genellikle sayaçlı olduğundan pahalı olabilir. Uygulamalar için yaygın olarak kullanılan bir strateji, yalnızca kablosuz ağ varsa büyük verileri getirmektir.

Ağ işlemlerini gerçekleştirmeden önce ağ bağlantısının durumunu kontrol etmek iyi bir uygulamadır. Diğer unsurların yanı sıra, bu durum uygulamanızın istemeden yanlış radyoyu kullanmasını engelleyebilir. Ağ bağlantısı yoksa uygulamanız sorunsuz şekilde yanıt vermelidir. Ağ bağlantısını kontrol etmek için genellikle aşağıdaki sınıfları kullanırsınız:

  • ConnectivityManager: Ağ bağlantısının durumuyla ilgili sorguları yanıtlar. Ayrıca, ağ bağlantısı değiştiğinde uygulamaları bilgilendirir.
  • NetworkInfo: Belirli bir türdeki (şu anda mobil veya kablosuz) ağ arayüzünün durumunu açıklar.

Bu kod snippet'i, kablosuz ve mobil için ağ bağlantısını test eder. Bu ağ arayüzlerinin kullanılabilir olup olmadığını (yani ağ bağlantısının mümkün olup olmadığını) ve/veya bağlı olup olmadığını (yani ağ bağlantısının var olup olmadığını ve yuva kurup veri geçirmenin mümkün olup olmadığını) belirler:

Kotlin

private const val DEBUG_TAG = "NetworkStatusExample"
...
val connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
var isWifiConn: Boolean = false
var isMobileConn: Boolean = false
connMgr.allNetworks.forEach { network ->
    connMgr.getNetworkInfo(network).apply {
        if (type == ConnectivityManager.TYPE_WIFI) {
            isWifiConn = isWifiConn or isConnected
        }
        if (type == ConnectivityManager.TYPE_MOBILE) {
            isMobileConn = isMobileConn or isConnected
        }
    }
}
Log.d(DEBUG_TAG, "Wifi connected: $isWifiConn")
Log.d(DEBUG_TAG, "Mobile connected: $isMobileConn")

Java

private static final String DEBUG_TAG = "NetworkStatusExample";
...
ConnectivityManager connMgr =
        (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
boolean isWifiConn = false;
boolean isMobileConn = false;
for (Network network : connMgr.getAllNetworks()) {
    NetworkInfo networkInfo = connMgr.getNetworkInfo(network);
    if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
        isWifiConn |= networkInfo.isConnected();
    }
    if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
        isMobileConn |= networkInfo.isConnected();
    }
}
Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);

Kararlarınızı ağın "kullanılabilir" olup olmadığına göre belirlememeniz gerektiğini unutmayın. isConnected() kesintili mobil ağlar, uçak modu ve kısıtlanmış arka plan verileri gibi durumları ele aldığından, ağ işlemleri gerçekleştirmeden önce her zaman isConnected() kutusunu kontrol etmeniz gerekir.

Bir ağ arayüzünün olup olmadığını kontrol etmenin daha kısa bir yolu aşağıdaki gibidir. getActiveNetworkInfo() yöntemi, bulabildiği ilk bağlı ağ arayüzünü temsil eden bir NetworkInfo örneği veya hiçbir arayüz bağlı değilse (yani internet bağlantısı yoksa) null döndürür:

Kotlin

fun isOnline(): Boolean {
    val connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val networkInfo: NetworkInfo? = connMgr.activeNetworkInfo
    return networkInfo?.isConnected == true
}

Java

public boolean isOnline() {
    ConnectivityManager connMgr = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    return (networkInfo != null && networkInfo.isConnected());
}

Daha ayrıntılı bir durumu sorgulamak için NetworkInfo.DetailedState kullanabilirsiniz, ancak bu nadiren gereklidir.

Ağ kullanımını yönetme

Kullanıcılara, uygulamanızın ağ kaynaklarını kullanımı üzerinde açık kontrol sağlayan bir tercihler etkinliği uygulayabilirsiniz. Örneğin:

  • Kullanıcıların yalnızca cihaz bir kablosuz ağa bağlı olduğunda video yüklemesine izin verebilirsiniz.
  • Ağ kullanılabilirliği ve zaman aralığı gibi belirli ölçütlere bağlı olarak senkronizasyon gerçekleştirebilir (veya yapmamayı tercih edebilirsiniz).

Ağ erişimini destekleyen ve ağ kullanımını yöneten bir uygulama yazmak için manifest dosyanız doğru izinlere ve amaç filtrelerine sahip olmalıdır.

  • Bu bölümün ilerleyen kısımlarında alıntılanan manifest aşağıdaki izinleri içerir:
  • Uygulamanızın, veri kullanımını kontrol etme seçenekleri sunan bir etkinlik tanımladığını belirtmek için ACTION_MANAGE_NETWORK_USAGE işlemi için intent filtresi beyan edebilirsiniz. ACTION_MANAGE_NETWORK_USAGE, belirli bir uygulamanın ağ veri kullanımını yönetmeyle ilgili ayarları gösterir. Uygulamanızda, kullanıcıların ağ kullanımını kontrol etmesine olanak tanıyan bir ayar etkinliği varsa bu etkinlik için bu intent filtresini beyan etmeniz gerekir.

Örnek uygulamada, bu işlem SettingsActivity sınıfı tarafından gerçekleştirilir. Burada, kullanıcıların feed'i ne zaman indireceklerine karar vermelerine olanak tanıyan bir tercihler kullanıcı arayüzü görüntülenir.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.networkusage"
    ...>

    <uses-sdk android:minSdkVersion="4"
           android:targetSdkVersion="14" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        ...>
        ...
        <activity android:label="SettingsActivity" android:name=".SettingsActivity">
             <intent-filter>
                <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
                <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
        </activity>
    </application>
</manifest>

Hassas kullanıcı verileriyle ilgili çalışan ve Android 11 ve sonraki sürümleri hedefleyen uygulamalar, işlem başına ağ erişimi verebilir. Hangi işlemlere ağ erişimine izin verildiğini açıkça belirterek veri yüklemesi gerekmeyen tüm kodları ayırabilirsiniz.

Uygulamanızın yanlışlıkla veri yüklemesini önleyeceği garanti edilmese de, uygulamanızda verilerin sızıntısına neden olma ihtimalini azaltabileceğiniz bir yol sağlar.

Aşağıda, işlem başına işlevi kullanan bir manifest dosyası örneği gösterilmektedir:

<processes>
    <process />
    <deny-permission android:name="android.permission.INTERNET" />
    <process android:process=":withoutnet1" />
    <process android:process="com.android.cts.useprocess.withnet1">
        <allow-permission android:name="android.permission.INTERNET" />
    </process>
    <allow-permission android:name="android.permission.INTERNET" />
    <process android:process=":withoutnet2">
        <deny-permission android:name="android.permission.INTERNET" />
    </process>
    <process android:process="com.android.cts.useprocess.withnet2" />
</processes>

Bir tercih etkinliğini uygulama

Bu konunun önceki manifest alıntısında da görebileceğiniz gibi örnek uygulamanın SettingsActivity etkinliğinde ACTION_MANAGE_NETWORK_USAGE işlemi için bir intent filtresi vardır. SettingsActivity, PreferenceActivity sınıfının bir alt sınıfıdır. Kullanıcıların aşağıdakileri belirtmelerini sağlayan bir tercihler ekranı (şekil 1'de gösterilmiştir) görüntülenir:

  • Her XML feed girişi için özetlerin mi yoksa her giriş için sadece bir bağlantının mı gösterileceğini belirtir.
  • XML feed'inin herhangi bir ağ bağlantısı veya yalnızca kablosuz ağ olduğunda indirilip indirilmeyeceği.

Tercihler paneli Ağ tercihini ayarlama

Şekil 1. Tercihler etkinliği'ne dokunun.

İşte SettingsActivity. OnSharedPreferenceChangeListener kodunu uyguladığını unutmayın. Kullanıcı bir tercihi değiştirdiğinde onSharedPreferenceChanged() etkinleşir ve refreshDisplay doğru değerine ayarlanır. Bu, kullanıcı ana etkinliğe döndüğünde ekranın yenilenmesine neden olur:

Kotlin

class SettingsActivity : PreferenceActivity(), OnSharedPreferenceChangeListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Loads the XML preferences file
        addPreferencesFromResource(R.xml.preferences)
    }

    override fun onResume() {
        super.onResume()

        // Registers a listener whenever a key changes
        preferenceScreen?.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
    }

    override fun onPause() {
        super.onPause()

        // Unregisters the listener set in onResume().
        // It's best practice to unregister listeners when your app isn't using them to cut down on
        // unnecessary system overhead. You do this in onPause().
        preferenceScreen?.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
    }

    // When the user changes the preferences selection,
    // onSharedPreferenceChanged() restarts the main activity as a new
    // task. Sets the refreshDisplay flag to "true" to indicate that
    // the main activity should update its display.
    // The main activity queries the PreferenceManager to get the latest settings.

    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
        // Sets refreshDisplay to true so that when the user returns to the main
        // activity, the display refreshes to reflect the new settings.
        NetworkActivity.refreshDisplay = true
    }
}

Java

public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Loads the XML preferences file
        addPreferencesFromResource(R.xml.preferences);
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Registers a listener whenever a key changes
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    protected void onPause() {
        super.onPause();

       // Unregisters the listener set in onResume().
       // It's best practice to unregister listeners when your app isn't using them to cut down on
       // unnecessary system overhead. You do this in onPause().
       getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    }

    // When the user changes the preferences selection,
    // onSharedPreferenceChanged() restarts the main activity as a new
    // task. Sets the refreshDisplay flag to "true" to indicate that
    // the main activity should update its display.
    // The main activity queries the PreferenceManager to get the latest settings.

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        // Sets refreshDisplay to true so that when the user returns to the main
        // activity, the display refreshes to reflect the new settings.
        NetworkActivity.refreshDisplay = true;
    }
}

Tercih değişikliklerine yanıt verme

Kullanıcı, ayarlar ekranında tercihlerini değiştirdiğinde genellikle uygulamanın davranışıyla ilgili sonuçlar doğurur. Uygulama, bu snippet'te onStart() tercih ayarlarını kontrol eder. Ayar ile cihazın ağ bağlantısı eşleşme varsa (örneğin, ayar "Wi-Fi" ise ve cihazın kablosuz bağlantısı varsa) uygulama, feed'i indirir ve ekranı yeniler.

Kotlin

class NetworkActivity : Activity() {

    // The BroadcastReceiver that tracks network connectivity changes.
    private lateinit var receiver: NetworkReceiver

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Registers BroadcastReceiver to track network connection changes.
        val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        receiver = NetworkReceiver()
        this.registerReceiver(receiver, filter)
    }

    public override fun onDestroy() {
        super.onDestroy()
        // Unregisters BroadcastReceiver when app is destroyed.
        this.unregisterReceiver(receiver)
    }

    // Refreshes the display if the network connection and the
    // pref settings allow it.

    public override fun onStart() {
        super.onStart()

        // Gets the user's network preference settings
        val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)

        // Retrieves a string value for the preferences. The second parameter
        // is the default value to use if a preference value is not found.
        sPref = sharedPrefs.getString("listPref", "Wi-Fi")

        updateConnectedFlags()

        if (refreshDisplay) {
            loadPage()
        }
    }

    // Checks the network connection and sets the wifiConnected and mobileConnected
    // variables accordingly.
    fun updateConnectedFlags() {
        val connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

        val activeInfo: NetworkInfo? = connMgr.activeNetworkInfo
        if (activeInfo?.isConnected == true) {
            wifiConnected = activeInfo.type == ConnectivityManager.TYPE_WIFI
            mobileConnected = activeInfo.type == ConnectivityManager.TYPE_MOBILE
        } else {
            wifiConnected = false
            mobileConnected = false
        }
    }

    // Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
    fun loadPage() {
        if (sPref == ANY && (wifiConnected || mobileConnected) || sPref == WIFI && wifiConnected) {
            // AsyncTask subclass
            DownloadXmlTask().execute(URL)
        } else {
            showErrorPage()
        }
    }

    companion object {

        const val WIFI = "Wi-Fi"
        const val ANY = "Any"
        const val SO_URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort;=newest"

        // Whether there is a Wi-Fi connection.
        private var wifiConnected = false
        // Whether there is a mobile connection.
        private var mobileConnected = false
        // Whether the display should be refreshed.
        var refreshDisplay = true

        // The user's current network preference setting.
        var sPref: String? = null
    }
...

}

Java

public class NetworkActivity extends Activity {
    public static final String WIFI = "Wi-Fi";
    public static final String ANY = "Any";
    private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort;=newest";

    // Whether there is a Wi-Fi connection.
    private static boolean wifiConnected = false;
    // Whether there is a mobile connection.
    private static boolean mobileConnected = false;
    // Whether the display should be refreshed.
    public static boolean refreshDisplay = true;

    // The user's current network preference setting.
    public static String sPref = null;

    // The BroadcastReceiver that tracks network connectivity changes.
    private NetworkReceiver receiver = new NetworkReceiver();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Registers BroadcastReceiver to track network connection changes.
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        receiver = new NetworkReceiver();
        this.registerReceiver(receiver, filter);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // Unregisters BroadcastReceiver when app is destroyed.
        if (receiver != null) {
            this.unregisterReceiver(receiver);
        }
    }

    // Refreshes the display if the network connection and the
    // pref settings allow it.

    @Override
    public void onStart () {
        super.onStart();

        // Gets the user's network preference settings
        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);

        // Retrieves a string value for the preferences. The second parameter
        // is the default value to use if a preference value is not found.
        sPref = sharedPrefs.getString("listPref", "Wi-Fi");

        updateConnectedFlags();

        if(refreshDisplay){
            loadPage();
        }
    }

    // Checks the network connection and sets the wifiConnected and mobileConnected
    // variables accordingly.
    public void updateConnectedFlags() {
        ConnectivityManager connMgr = (ConnectivityManager)
                getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
        if (activeInfo != null && activeInfo.isConnected()) {
            wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
            mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE;
        } else {
            wifiConnected = false;
            mobileConnected = false;
        }
    }

    // Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
    public void loadPage() {
        if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected))
                || ((sPref.equals(WIFI)) && (wifiConnected))) {
            // AsyncTask subclass
            new DownloadXmlTask().execute(URL);
        } else {
            showErrorPage();
        }
    }
...

}

Bağlantı değişikliklerini algılama

Bulmacanın son parçası olan BroadcastReceiver alt sınıfı NetworkReceiver. Cihazın ağ bağlantısı değiştiğinde NetworkReceiver, CONNECTIVITY_ACTION işlemine müdahale eder, ağ bağlantısı durumunu belirler ve wifiConnected ile mobileConnected işaretlerini uygun şekilde "true/false" (doğru/yanlış) değerine ayarlar. Sonuç olarak, kullanıcı uygulamaya bir daha döndüğünde uygulama yalnızca en son feed'i indirir ve NetworkActivity.refreshDisplay, true değerine ayarlanmışsa ekranı günceller.

Gereksiz çağrılan bir BroadcastReceiver kurulumu, sistem kaynaklarını boşa harcayabilir. Örnek uygulama, BroadcastReceiver NetworkReceiver öğesini onCreate() içinde kaydeder ve onDestroy() içindeki kaydını iptal eder. Bu, manifest dosyasında <receiver> belirtmekten daha hafiftir. Manifest'te bir <receiver> belirttiğinizde, haftalar boyunca çalıştırmasanız bile uygulamanızı istediği zaman uyandırabilir. NetworkReceiver adlı uygulamaya ana etkinlik içinden kaydolup kaydını iptal ederek, kullanıcının uygulamadan ayrıldıktan sonra uygulamayı uyandırmamasını sağlarsınız. Manifest'te bir <receiver> belirtirseniz ve tam olarak nerede ihtiyacınız olduğunu biliyorsanız uygun şekilde etkinleştirmek ve devre dışı bırakmak için setComponentEnabledSetting() kullanabilirsiniz.

İşte NetworkReceiver:

Kotlin

class NetworkReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        val conn = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val networkInfo: NetworkInfo? = conn.activeNetworkInfo

        // Checks the user prefs and the network connection. Based on the result, decides whether
        // to refresh the display or keep the current display.
        // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
        if (WIFI == sPref && networkInfo?.type == ConnectivityManager.TYPE_WIFI) {
            // If device has its Wi-Fi connection, sets refreshDisplay
            // to true. This causes the display to be refreshed when the user
            // returns to the app.
            refreshDisplay = true
            Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show()

            // If the setting is ANY network and there is a network connection
            // (which by process of elimination would be mobile), sets refreshDisplay to true.
        } else if (ANY == sPref && networkInfo != null) {
            refreshDisplay = true

            // Otherwise, the app can't download content--either because there is no network
            // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there
            // is no Wi-Fi connection.
            // Sets refreshDisplay to false.
        } else {
            refreshDisplay = false
            Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show()
        }
    }
}

Java

public class NetworkReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager conn =  (ConnectivityManager)
            context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = conn.getActiveNetworkInfo();

        // Checks the user prefs and the network connection. Based on the result, decides whether
        // to refresh the display or keep the current display.
        // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
        if (WIFI.equals(sPref) && networkInfo != null
            && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
            // If device has its Wi-Fi connection, sets refreshDisplay
            // to true. This causes the display to be refreshed when the user
            // returns to the app.
            refreshDisplay = true;
            Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();

        // If the setting is ANY network and there is a network connection
        // (which by process of elimination would be mobile), sets refreshDisplay to true.
        } else if (ANY.equals(sPref) && networkInfo != null) {
            refreshDisplay = true;

        // Otherwise, the app can't download content--either because there is no network
        // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there
        // is no Wi-Fi connection.
        // Sets refreshDisplay to false.
        } else {
            refreshDisplay = false;
            Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();
        }
    }
}