Uygulama kaynaklarının değerini çalışma zamanında değiştirme

Çalışma zamanında kaynak eşlemesi (RRO), çalışma zamanında hedef paketin kaynak değerlerini değiştiren bir pakettir. Örneğin, sistem görüntüsüne yüklenen bir uygulama, bir kaynağın değerine bağlı olarak davranışını değiştirebilir. Kaynak değerini derleme zamanında sabit kodlamak yerine, farklı bir bölüme yüklenen bir RRO, uygulamanın kaynaklarının değerlerini çalışma zamanında değiştirebilir.

RRO'lar etkinleştirilebilir veya devre dışı bırakılabilir. Bir RRO'nun kaynak değerlerini değiştirme özelliğini etkinleştirmek veya devre dışı bırakmak için etkinleştirme/devre dışı bırakma durumunu programatik olarak ayarlayabilirsiniz. RRO'lar varsayılan olarak devre dışıdır (ancak statik RRO'lar varsayılan olarak etkindir).

Yer paylaşımı kaynakları

Yer paylaşımları, yer paylaşımı paketinde tanımlanan kaynakları hedef pakette tanımlanan kaynaklarla eşleyerek çalışır. Bir uygulama, hedef paketteki bir kaynağın değerini çözmeye çalıştığında bunun yerine hedef kaynağın eşlendiği yer paylaşımı kaynağının değeri döndürülür.

Manifest dosyasını ayarlama

Bir paket, <overlay> etiketi <manifest> etiketinin alt öğesi olarak içeriyorsa RRO paketi olarak kabul edilir.

  • Gerekli android:targetPackage özelliğinin değeri, RRO'nun yerleştirmeyi amaçladığı paketin adını belirtir.

  • İsteğe bağlı android:targetName özelliğinin değeri, RRO'nun hedef paketin kaynaklarının yer paylaşımı yapmayı amaçladığı, yer paylaşımı yapılabilir alt kümesinin adını belirtir. Hedef, üstüne yazılabilir bir kaynak grubu tanımlamıyorsa bu özellik bulunmamalıdır.

Aşağıdaki kodda örnek bir yer paylaşımı AndroidManifest.xml gösterilmektedir.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.overlay">
    <application android:hasCode="false" />
    <overlay android:targetPackage="com.example.target"
                   android:targetName="OverlayableResources"/>
</manifest>

Yer paylaşımları kodun üzerine yerleştirilemez. Bu nedenle DEX dosyaları içermezler. Ayrıca, manifest dosyasındaki <application> etiketinin android:hasCode özelliği false olarak ayarlanmalıdır.

Kaynak haritasını tanımlama

Android 11 veya sonraki sürümlerde, yer paylaşımı kaynakları haritasını tanımlamak için önerilen mekanizma, yer paylaşımı paketinin res/xml dizininde bir dosya oluşturmak, yer paylaşımı uygulanması gereken hedef kaynakları ve bunların yerine kullanılacak değerleri listelemek, ardından <overlay> manifest etiketinin android:resourcesMap özelliğinin değerini kaynak eşleme dosyasına bir referans olarak ayarlamaktır.

Aşağıdaki kodda bir örnek res/xml/overlays.xml dosyası gösterilmektedir.

<?xml version="1.0" encoding="utf-8"?>
<overlay xmlns:android="http://schemas.android.com/apk/res/android" >
    <!-- Overlays string/config1 and string/config2 with the same resource. -->
    <item target="string/config1" value="@string/overlay1" />
    <item target="string/config2" value="@string/overlay1" />

    <!-- Overlays string/config3 with the string "yes". -->
    <item target="string/config3" value="@android:string/yes" />

    <!-- Overlays string/config4 with the string "Hardcoded string". -->
    <item target="string/config4" value="Hardcoded string" />

    <!-- Overlays integer/config5 with the integer "42". -->
    <item target="integer/config5" value="42" />
</overlay>

Aşağıdaki kodda örnek bir yer paylaşımı manifesti gösterilmektedir.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.overlay">
    <application android:hasCode="false" />
    <overlay android:targetPackage="com.example.target"
                   android:targetName="OverlayableResources"
                   android:resourcesMap="@xml/overlays"/>
</manifest>

Paketi oluşturma

Android 11 veya sonraki sürümlerde, Android Asset Packaging Tool 2'nin (AAPT2) aynı değere sahip kaynakların yapılandırmalarını tekilleştirmeyi (--no-resource-deduping) ve varsayılan yapılandırmaları olmayan kaynakları kaldırmayı (--no-resource-removal) denemesini engelleyen bir Soong derleme kuralı desteklenir. Aşağıdaki kodda bir örnek Android.bp dosyası gösterilmektedir.

runtime_resource_overlay {
    name: "ExampleOverlay",
    sdk_version: "current",
}

Kaynakları çözme

Bir hedef kaynak veya yer paylaşımı kaynağı için sorgulanan kaynakla ilgili birden fazla yapılandırma tanımlanmışsa kaynak çalışma zamanı, cihaz yapılandırmasının yapılandırmasıyla en iyi eşleşen yapılandırmanın değerini döndürür. En iyi eşleşen yapılandırmanın hangisi olduğunu belirlemek için yer paylaşımı kaynak yapılandırmaları kümesini hedef kaynak yapılandırmaları kümesiyle birleştirin ve ardından normal kaynak çözünürlüğü akışını izleyin (ayrıntılar için Android en iyi eşleşen kaynağı nasıl bulur? başlıklı makaleye bakın).

Örneğin, bir yer paylaşımı drawable-en yapılandırması için bir değer tanımlıyorsa ve hedef drawable-en-port için bir değer tanımlıyorsa drawable-en-port daha iyi bir eşleşmeye sahiptir. Bu nedenle, çalışma zamanında hedef yapılandırmanın drawable-en-port değeri seçilir. Tüm drawable-en yapılandırmalarını yerleştirmek için yerleştirme, hedef tarafından tanımlanan her drawable-en yapılandırması için bir değer tanımlamalıdır.

Yer paylaşımları kendi kaynaklarına referans verebilir ve Android sürümleri arasında farklı davranışlar sergileyebilir.

  • Android 11 veya sonraki sürümlerde her yer paylaşımının, hedef kaynak kimliği alanı ya da diğer yer paylaşımı kaynak kimliği alanlarıyla çakışmayan kendi ayrılmış kaynak kimliği alanı vardır. Bu nedenle, kendi kaynaklarına referans veren yer paylaşımları beklendiği gibi çalışır.

  • Android 10 veya önceki sürümlerde, yer paylaşımları ve hedef paketler aynı kaynak kimliği alanını paylaşır. Bu durum, @type/name söz dizimini kullanarak kendi kaynaklarına referans vermeye çalıştıklarında çakışmalara ve beklenmedik davranışlara neden olabilir.

Yer paylaşımlarını etkinleştirme/devre dışı bırakma

Yer paylaşımları manuel ve programatik olarak etkinleştirilebilir/devre dışı bırakılabilir.

Yer paylaşımlarını manuel olarak devre dışı bırakma veya etkinleştirme

Bir RRO'yu manuel olarak etkinleştirmek ve doğrulamak için şu komutu çalıştırın:

adb shell cmd overlay enable --user current com.example.carrro
adb shell cmd overlay list --user current | grep -i com.example com.example.carrro

Bu, SystemUI'nin sahibi olan sistem kullanıcısı (userId = 0) için RRO'yu etkinleştirir. Bu talimat, ön plandaki kullanıcı (userId = 10) tarafından başlatılan uygulamaları etkilemez. Ön plandaki kullanıcı için RRO'yu etkinleştirmek üzere -–user 10 parametresini kullanın:

adb shell cmd overlay enable --user 10 com.example.carrro

Yer paylaşımlarını programatik olarak etkinleştirme veya devre dışı bırakma

Değiştirilebilir yer paylaşımlarını etkinleştirmek ve devre dışı bırakmak için OverlayManager API'sini kullanın (Context#getSystemService(Context.OVERLAY_SERVICE) kullanarak API arayüzünü alın). Bir yer paylaşımı yalnızca hedeflediği paket veya android.permission.CHANGE_OVERLAY_PACKAGES iznine sahip bir paket tarafından etkinleştirilebilir. Bir yer paylaşımı etkinleştirildiğinde veya devre dışı bırakıldığında yapılandırma değişikliği etkinlikleri hedef pakete yayılır ve hedef etkinlikler yeniden başlatılır.

Yerleştirilebilir kaynakları kısıtlama

Android 10 veya sonraki sürümlerde <overlayable> XML etiketi, RRO'ların yerleştirmesine izin verilen bir dizi kaynağı kullanıma sunar. Aşağıdaki örnek res/values/overlayable.xml dosyasında, string/foo ve integer/bar, cihazın görünümünü temalandırmak için kullanılan kaynaklardır. Bu kaynakları yerleştirmek için yerleştirme, yerleştirilebilir kaynak koleksiyonunu ada göre açıkça hedeflemelidir.

<!-- The collection of resources for theming the appearance of the device -->
<overlayable name="ThemeResources">
       <policy type="public">
               <item type="string" name="foo/" />
               <item type="integer" name="bar/" />
       </policy>
       ...
</overlayable>

Bir APK, birden fazla <overlayable> etiketi tanımlayabilir ancak her etiketin paket içinde benzersiz bir adı olmalıdır. Örneğin:

  • İki farklı paketin <overlayable name="foo"> değerini tanımlaması sorun olmaz.

  • Tek bir APK'da iki <overlayable name="foo"> bloğu olması uygun değildir.

Aşağıdaki kodda, AndroidManifest.xml dosyasındaki bir yer paylaşımı örneği gösterilmektedir.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.my.theme.overlay">
       <application android:hasCode="false" />
       <!-- This overlay will override the ThemeResources resources -->
       <overlay android:targetPackage="android" android:targetName="ThemeResources">
</manifest>

Bir uygulama <overlayable> etiketi tanımladığında, bu uygulamayı hedefleyen yer paylaşımları:

  • targetName belirtilmelidir.

  • Yalnızca <overlayable> etiketi içinde listelenen kaynakları yerleştirebilir.

  • Yalnızca bir <overlayable> adı hedeflenebilir.

Yerleştirilebilir kaynakları kullanıma sunan ancak belirli bir <overlayable> etiketini hedeflemek için android:targetName kullanmayan bir paketi hedefleyen yer paylaşımını etkinleştiremezsiniz.

Politikaları kısıtlama

Yerleştirilebilir kaynaklara kısıtlamalar uygulamak için <policy> etiketini kullanın. type özelliği, bir yer paylaşımının dahil edilen kaynakları geçersiz kılmak için hangi politikaları karşılaması gerektiğini belirtir. Desteklenen türler şunlardır:

  • public. Herhangi bir yer paylaşımı kaynağı geçersiz kılabilir.
  • system. Sistem bölümündeki herhangi bir yer paylaşımı, kaynakları geçersiz kılabilir.
  • vendor. Tedarikçi bölümündeki herhangi bir yer paylaşımı, kaynakları geçersiz kılabilir.
  • product. Ürün bölümündeki herhangi bir yer paylaşımı, kaynakları geçersiz kılabilir.
  • oem. OEM bölümündeki herhangi bir yer paylaşımı, kaynakları geçersiz kılabilir.
  • odm. Odm bölümündeki herhangi bir yer paylaşımı, kaynakları geçersiz kılabilir.
  • signature. Hedef APK ile aynı imzaya sahip tüm yer paylaşımları kaynakları geçersiz kılabilir.
  • actor. Actor APK'sı ile aynı imzaya sahip tüm yer paylaşımları kaynakları geçersiz kılabilir. Oyuncu, system/config dosyasındaki named-actor etiketinde belirtilir.
  • config_signature. overlay-config APK'sı ile aynı imzayla imzalanan tüm yer paylaşımları kaynakları geçersiz kılabilir. Yer paylaşımı yapılandırması, sistem yapılandırmasındaki overlay-config-signature etiketinde tanımlanır.

Aşağıdaki kodda, <policy> dosyasındaki bir res/values/overlayable.xml etiketi örneği gösterilmektedir.

<overlayable name="ThemeResources">
   <policy type="vendor" >
       <item type="string" name="foo" />
   </policy>
   <policy type="product|signature"  >
       <item type="string" name="bar" />
       <item type="string" name="baz" />
   </policy>
</overlayable>

Birden çok politika belirtmek için ayırıcı karakter olarak dikey çubuk (|) kullanın. Birden fazla politika belirtildiğinde, bir yer paylaşımının <policy> etiketinde listelenen kaynakları geçersiz kılmak için yalnızca bir politikayı karşılaması gerekir.

Yer paylaşımlarını yapılandırma

Android, Android sürümüne bağlı olarak katmanların değiştirilebilirliğini, varsayılan durumunu ve önceliğini yapılandırmak için farklı mekanizmaları destekler.

  • Android 11 veya sonraki sürümlerin yüklü olduğu cihazlarda manifest özellikleri yerine bir OverlayConfig dosyası (config.xml) kullanılabilir. Yer paylaşımları için önerilen yöntem, yer paylaşımlı dosya kullanmaktır.

  • Tüm cihazlar, statik RRO'ları yapılandırmak için manifest özelliklerini (android:isStatic ve android:priority) kullanabilir.

OverlayConfig Kullanma

Android 11 veya sonraki sürümlerde, OverlayConfig kullanarak yer paylaşımlarının değiştirilebilirliğini, varsayılan durumunu ve önceliğini yapılandırabilirsiniz. Bir yerleşimi yapılandırmak için partition/overlay/config/config.xml konumundaki dosyayı oluşturun veya değiştirin. Burada partition/overlay/config/config.xml, yapılandırılacak yerleşimin bölümüdür.partition Yapılandırılacak bir yer paylaşımı, yapılandırıldığı bölümün overlay/ dizininde bulunmalıdır. Aşağıdaki kodda bir örnek product/overlay/config/config.xml gösterilmektedir.

<config>
    <merge path="OEM-common-rros-config.xml" />
    <overlay package="com.oem.overlay.device" mutable="false" enabled="true" />
    <overlay package="com.oem.green.theme" enabled="true" />
</config>"

<overlay> etiketi, hangi yer paylaşımı paketinin yapılandırıldığını belirten bir package özelliği gerektirir. İsteğe bağlı enabled özelliği, yer paylaşımının varsayılan olarak etkinleştirilip etkinleştirilmeyeceğini kontrol eder (varsayılan değer false'dir). İsteğe bağlı mutable özelliği, yer paylaşımının değiştirilebilir olup olmadığını ve etkinleştirilmiş durumunun çalışma zamanında programatik olarak değiştirilip değiştirilemeyeceğini kontrol eder (varsayılan değer true'dir). Bir yapılandırma dosyasında listelenmeyen yer paylaşımları değiştirilebilir ve varsayılan olarak devre dışıdır.

Yer paylaşımı önceliği

Birden fazla yer paylaşımı aynı kaynakları geçersiz kıldığında yer paylaşımlarının sırası önemlidir. Bir yer paylaşımı, kendi yapılandırmasından önceki yapılandırmalara sahip yer paylaşımlarından daha yüksek önceliğe sahiptir. Farklı bölümlerdeki yer paylaşımlarının öncelik sırası (en az öncelikten en çok önceliğe doğru) aşağıdaki gibidir.

  • system
  • vendor
  • odm
  • oem
  • product
  • system_ext

Dosyaları birleştirme

<merge> etiketlerinin kullanılması, diğer yapılandırma dosyalarının belirtilen konumda yapılandırma dosyasıyla birleştirilmesine olanak tanır. Etiketin path özelliği, birleştirilecek dosyanın, yer paylaşımı yapılandırma dosyalarını içeren dizine göre yolunu gösterir.

Manifest özelliklerini/statik RRO'ları kullanma

Android 10 veya önceki sürümlerde, yer paylaşımı değişmezliği ve önceliği aşağıdaki manifest özellikleri kullanılarak yapılandırılır.

  • android:isStatic. Bu boole özelliğinin değeri true olarak ayarlandığında, yer paylaşımı varsayılan olarak etkinleştirilir ve değiştirilemez. Bu da yer paylaşımının devre dışı bırakılmasını engeller.

  • android:priority. Bu sayısal özelliğin değeri (yalnızca statik yer paylaşımlarını etkiler), birden fazla statik yer paylaşımı aynı kaynak değerini hedeflediğinde yer paylaşımının önceliğini yapılandırır. Daha yüksek bir sayı, daha yüksek bir önceliği gösterir.

Aşağıdaki kodda bir örnek gösterilmektedir AndroidManifest.xml.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.overlay">
    <application android:hasCode="false" />
    <overlay android:targetPackage="com.example.target"
                   android:isStatic="true"
                   android:priority="5"/>
</manifest>

Android 11'deki değişiklikler

Android 11 veya sonraki sürümlerde, bir yapılandırma dosyası partition/overlay/config/config.xml konumundaysa yer paylaşımları bu dosya kullanılarak yapılandırılır ve android:isStatic ile android:priority, bölümdeki yer paylaşımlarını etkilemez. Herhangi bir bölümde yer paylaşımı yapılandırma dosyası tanımlamak, yer paylaşımı bölümü önceliğini zorunlu kılar.

Ayrıca, Android 11 veya sonraki sürümlerde, paket yükleme sırasında okunan kaynakların değerlerini etkilemek için statik yer paylaşımları kullanma özelliği kaldırılmıştır. Bileşen etkin durumunu yapılandıran boole değerlerini değiştirmek için statik yer paylaşımlarını kullanma gibi yaygın bir kullanım alanında <component-override> SystemConfig etiketini (Android 11'de yeni) kullanın.

Hata ayıklama yer paylaşımları

Yer paylaşımlarını manuel olarak etkinleştirmek, devre dışı bırakmak ve boşaltmak için aşağıdaki yer paylaşımı yöneticisi kabuk komutunu kullanın.

adb shell cmd overlay

Kullanıcı belirtmeden enable kullanmak, geçerli kullanıcıyı (yani Sistem kullanıcı arayüzünün sahibi olan sistem kullanıcısını (userId = 0)) etkiler. Bu durum, uygulamaların sahibi olan ön plan kullanıcısını (userId = 10) etkilemez. Ön plandaki kullanıcı için RRO'yu etkinleştirmek üzere –-user 10 parametresini kullanın:

adb shell cmd overlay enable --user 10 com.example.carrro

OverlayManagerService, hedef paketteki kaynak kimliklerini yer paylaşımı paketindeki kaynak kimlikleriyle eşlemek için idmap2 kullanır. Oluşturulan kimlik eşlemeleri /data/resource-cache/ içinde saklanır. Yer paylaşımınız doğru çalışmıyorsa /data/resource-cache/ içinde yer paylaşımınıza karşılık gelen idmap dosyasını bulun ve aşağıdaki komutu çalıştırın.

adb shell idmap2 dump --idmap-path [file]

Bu komut, kaynakların eşlemesini aşağıda gösterildiği gibi yazdırır.

[target res id] - > [overlay res id] [resource name]
0x01040151 -> 0x01050001 string/config_dozeComponent
0x01040152 -> 0x01050002 string/config_dozeDoubleTapSensorType
0x01040153 -> 0x01050003 string/config_dozeLongPressSensorType