3 Ocak 2017 Salı

Android Kütüphanesinde Initialization ve Context'e Ulaşım


Android geliştirme ortamında basit bir işlem(resource'lara ulaşım vs) yapmak için bile context instance'ına ihtiyaç duyarız. Yazdığımız sınıflarda context'e ulaşmak için context'i oradan oraya sürekli parametre olarak aktarmaktan sıkılıyor/üşeniyorsanız aşağıdaki gibi bir Application sınıfı yazarak bu külfetten bir nebze de olsa kurtulabilirsiniz:

public class App extends Application {

    private static App instance;

    public static App getInstance() {
        return instance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
    }

}
Tabii burada static alana context'i atamanın memory leak oluşturma ihtimali aklınıza gelebilir fakat Application instance'ı uygulama boyunca canlı kaldığı için sıkıntı yok. Aynı durum activity için geçerli değil bu yüzden activity instance'ını kullanırken dikkat olmaya bakın. Neyse bu basit sınıf sayesinde o çok ihtiyaç duyduğumuz/o en güzel context'e erişim statik method çağrımına indirgenmiş olur: App.getInstance(). Ve fakat android kütüphanesi geliştirirken context'e bu şekilde ulaşamayız çünkü aynı anda hem uygulama modülü hem de android kütüphanesi Application sınıfını manifestte deklare edemez. Bu durumda mecbur context referansını tutmak için için yardımcı bir bir sınıf oluşturacaksınız ve bu sınıfın init edilmesini kullanıcıya mecbur kılacaksınız. Bu durumda kütüphanenizi kullanan geliştiriciye ek entegrasyon yükü vermiş olursunuz. Dikkat ettiyseniz kullandığınız çoğu kütüphane Application.onCreate() altında sizden context'i ister:
public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        OneLibrary.init(this);
    }

}
Fakat geçenlerde Firebase Crash Reporting'i projeye entegre ederken fark ettim ki entegrasyon için yapılması gereken tek şey gradle'a bağımlılık eklemek. Hal böyle olunca merak edip konuyu irdeledim ve adamların çok farklı bir biçimde context'i aldıklarını gördüm: ContentProvider tanımlayarak. Firabese kütüphanesinde init için boş/işlevsiz bir provider var: FirebaseInitProvider. Merged manifestte ilgili tanımlanın firebase-common.jar'dan geldiğini görebilirsiniz. Bu sınıfın onCreate() metodunda dikkat ederseniz: FirebaseApp.initializeApp(this.getContext()) == null) statment'ı ile bu iş kotarılıyor. Biz de aşağıdaki gibi bir ContentProvider sınıfı yazarak context instance'ına ulaşıp android kütüphanemizde gereken yerlerde kullanabiliriz:
public class InitProvider extends ContentProvider {

    private static Context ctx;

    public static Context getCtx() {
        return ctx;
    }

    @Override
    public boolean onCreate() {
        ctx = getContext();
        return false;
    }

    @Nullable
    public Cursor query(Uri var1, String[] var2, String var3, String[] var4, String var5) {
        return null;
    }

    @Nullable
    public String getType(Uri var1) {
        return null;
    }

    @Nullable
    public Uri insert(Uri var1, ContentValues var2) {
        return null;
    }

    public int delete(Uri var1, String var2, String[] var3) {
        return 0;
    }

    public int update(Uri var1, ContentValues var2, String var3, String[] var4) {
        return 0;
    }

}
Content Provider'ı manifestte deklare ederken dikkat etmemiz gereken authority attribute'ini uygulama bazında benzersiz yapamalıyız bunun için statik string değer geçmek yerine ${applicationId} holder'ını kullanıyoruz:


    
         



ContentProvider.onCreate() metodu uygulama açılışında initialize olurken main thread üzerinde koşturulur. Bu yüzden burada uzun süreli işlem yapacaksanız bunu ayrı bir thread'de yapmalısınız yoksa uygulama açılışnı geciktirerek kullanıcı deneyiminin dibe vurmasına sebep olabilirsiniz.

Konuyla alakalı oluşturduğum örnek projeye buradan ulaşabilirsiniz.

İyi çalışmalar.

2 yorum: