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:
android.permission.INTERNET
— Uygulamalara ağ yuvalarını açma izni verir.android.permission.ACCESS_NETWORK_STATE
— Uygulamalara, ağlarla ilgili bilgilere erişim izni verir.
- 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.
İş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(); } } }