Pertanyaan Praktik terbaik: Memperluas atau mengesampingkan kelas proyek perpustakaan Android


Kami menggunakan sebuah Proyek Perpustakaan Android untuk berbagi kelas inti dan sumber daya di berbagai versi (target) yang berbeda dari aplikasi Android kami. Proyek Android untuk setiap target spesifik referensi proyek pustaka Inti (Di belakang layar, Eclipse membuat dan merujuk sebuah guci dari proyek perpustakaan yang direferensikan).

Mengganti sumber daya seperti gambar dan tata letak XML itu mudah. File sumber daya yang ditempatkan dalam proyek target, seperti ikon aplikasi atau tata letak XML, secara otomatis mengganti sumber daya perpustakaan inti dengan nama yang sama saat aplikasi dibuat. Namun, terkadang suatu kelas perlu ditimpa untuk memungkinkan perilaku spesifik target. Misalnya, layar preferensi target Amazon tidak boleh berisi tautan ke halaman aplikasi Google Play, yang membutuhkan perubahan preferensi proyek Amazon.xml dan preferensi kelas Aktivitas.

Tujuannya adalah untuk mengurangi jumlah kode duplikat di antara proyek target sementara menghapus sebanyak mungkin kode target-spesifik dari pustaka Inti. Kami telah menemukan beberapa pendekatan untuk menerapkan logika spesifik untuk target yang berbeda:

  1. Tulis fungsi spesifik target dalam kelas perpustakaan Inti dan gunakan jika / blok beralih untuk memilih perilaku berdasarkan SKU produk. Pendekatan ini tidak terlalu modular dan membengkak basis kode perpustakaan Inti.
  2. Perluas kelas Core tertentu dalam proyek target dan ganti fungsi kelas dasar (Core) sesuai kebutuhan. Kemudian simpan referensi ke objek kelas dasar di pustaka Core dan instantiate dengan objek kelas diperluas (dari Bagaimana cara mengganti kelas dalam proyek perpustakaan Android?)

Apakah ada strategi lain untuk mengesampingkan atau memperpanjang kelas proyek perpustakaan Android? Apa beberapa praktik terbaik untuk berbagi dan memperluas kelas umum di antara target aplikasi Android?


32
2018-03-30 23:32


asal


Jawaban:


Proyek perpustakaan direferensikan sebagai ketergantungan proyek mentah (mekanisme berbasis sumber), bukan sebagai ketergantungan jar yang dikompilasi (mekanisme pustaka yang dikompilasi berdasarkan kode).

@yorkw ini tidak berlaku untuk versi terbaru Plugin ADT untuk Eclipse http://developer.android.com/sdk/eclipse-adt.html

Dari versi 17, ubah log

Fitur build baru   Menambahkan fitur untuk secara otomatis mengatur dependensi JAR. Setiap file .jar dalam folder / libs ditambahkan ke konfigurasi build (mirip dengan bagaimana sistem build Ant bekerja). Juga, file .jar yang dibutuhkan oleh proyek perpustakaan juga secara otomatis ditambahkan ke proyek yang bergantung pada proyek perpustakaan tersebut. (Info lebih lanjut)

Info lebih lanjut http://tools.android.com/recent/dealingwithdependenciesinandroidprojects

Sebelum itu, memperbarui timpa Kegiatan dari proyek Perpustakaan itu mudah, cukup kecualikan kelas. Sekarang perpustakaan disertakan sebagai file jar, dan tidak ada cara untuk mengecualikan file kelas dari ketergantungan jar.

EDIT:

Solusi saya untuk overwrete / memperpanjang Aktivitas dari jar perpustakaan:

Saya membuat kelas util sederhana:

public class ActivityUtil {

private static Class getActivityClass(Class clazz) {

    // Check for extended activity
    String extClassName = clazz.getName() + "Extended";
    try {
        Class extClass = Class.forName(extClassName);
        return extClass;
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
        // Extended class is not found return base
        return clazz;
    }
}

public static Intent createIntent(Context context, Class clazz) {
    Class activityClass = getActivityClass(clazz);
    return new Intent(context, activityClass);
}
}

Untuk menimpa kelas "SampleActivity" perpustakaan, itu adalah proyek yang bergantung pada pustaka itu, buat kelas baru dengan nama SampleActivityExtended dalam proyek dalam paket yang sama dan tambahkan aktivitas baru ke AndroidManifest.xml Anda.

PENTING: semua maksud referensi kegiatan yang ditimpa harus dibuat melalui kelas util dengan cara berikut:

Intent intent = ActivityUtil.createIntent(MainActivity.this, SampleActivity.class);
...
startActivity(intent);

17
2018-04-12 10:01



di belakang layar, Eclipse membuat dan merujuk sebuah guci dari proyek perpustakaan yang direferensikan.

Ini tidak cukup akurat. Proyek perpustakaan direferensikan sebagai ketergantungan proyek mentah (mekanisme berbasis sumber), bukan sebagai ketergantungan jar yang dikompilasi (mekanisme pustaka yang dikompilasi berdasarkan kode). Saat ini Android SDK tidak mendukung mengekspor proyek pustaka ke file JAR mandiri. Proyek perpustakaan harus selalu dikompilasi / dibangun secara tidak langsung, dengan merujuk perpustakaan dalam aplikasi yang bergantung dan membangun aplikasi itu. Ketika membangun proyek yang bergantung, sumber yang dikompilasi dan sumber daya mentah yang perlu disaring / digabungkan dari proyek Perpustakaan akan disalin dan disertakan dengan benar dalam file apk akhir. Perhatikan bahwa tim Android telah mulai membenahi seluruh desain Proyek Perpustakaan (memindahkannya dari mekanisme berbasis klien ke mekanisme pustaka berbasis kode-dikompilasi) sejak r14, seperti yang disebutkan di posting blog sebelumnya ini.

Apa beberapa praktik terbaik untuk berbagi dan memperluas kelas umum di antara target aplikasi Android?

Solusi yang diberikan oleh Android adalah Proyek Perpustakaan.
Solusi yang diberikan oleh Java adalah Warisan dan Polimorfisme.
Datang bersama, IMO praktik terbaik adalah opsi kedua yang Anda sebutkan dalam pertanyaan:

2.Memperluas kelas Inti tertentu dalam proyek target dan mengganti fungsi kelas dasar (Core) sesuai kebutuhan. Kemudian simpan referensi ke objek kelas dasar di pustaka Core dan instantiate dengan objek kelas diperluas (dari proyek pustaka Android - Bagaimana menimpa kelas?)

Dari pengalaman pribadi saya, saya selalu menggunakan Android Library Project (Terkadang dengan Proyek Java Biasa, untuk mengimplementasikan / membangun common-lib.jar yang hanya berisi POJO) mengelola kode umum, misalnya SuperActivity atau SuperService, dan memperluas / mengimplementasikan kelas / antarmuka yang tepat dalam proyek tergantung untuk Polimorfisme.


4
2018-04-03 04:37



Solusi berdasarkan solusi PoisoneR dan solusi Turbo.

public static Class<?> getExtendedClass(Context context, String clsName) {

    // Check for extended activity
    String pkgName = context.getPackageName();
    Logger.log("pkgName", pkgName);
    String extClassName = pkgName + "." + clsName + "Extended";
    Logger.log("extClassName", extClassName);

    try {
        Class<?> extClass = Class.forName(extClassName);
        return extClass;
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
        // Extended class is not found return base
        return null;
    }
}

Manfaat dari ini adalah itu

  1. Kelas diperpanjang dapat berada di paket proyek, bukan paket perpustakaan. Terima kasih kepada Turbo untuk bagian ini.

  2. Dengan mengambil String sebagai argumen, bukan a Class objek, metode ini dapat digunakan bahkan dengan ProGuard. getName() Di sinilah masalahnya dengan ProGuard, karena itu akan mengembalikan sesuatu seperti "a" alih-alih nama kelas asli. Jadi dalam solusi asli bukannya mencari ClassExtended ia akan mencari aExtended sebaliknya, sesuatu yang tidak ada.


2
2018-01-08 03:38



Bagaimana dengan menggunakan callbackpendekatan di sini? (Oke, callback sedikit menyesatkan tapi saat ini saya tidak memiliki kata lain untuk itu:

Anda dapat mendeklarasikan antarmuka di setiap Aktivitas yang seharusnya / dapat diperluas oleh pengguna. Antarmuka ini akan memiliki metode seperti List<Preference> getPreferences(Activity activity) (Lewati parameter apa pun yang Anda butuhkan di sini, saya akan menggunakan Activity atau setidaknya a Context menjadi futureproof).

Pendekatan ini dapat memberi Anda apa yang Anda inginkan ketika saya memahaminya dengan benar. Meskipun saya belum pernah melakukan ini sebelumnya dan tidak tahu bagaimana orang lain menangani ini, saya akan mencobanya dan melihat apakah itu berhasil.


1
2018-04-02 06:16



Bisakah Anda, tolong, klarifikasi apa yang berbeda di Kindle dan Android biasa? Saya pikir - mereka sama. Yang Anda butuhkan adalah sumber daya yang berbeda untuk Kindle dan perangkat lain. Kemudian gunakan sumber daya yang sesuai. Misalnya saya menggunakan 2 tautan untuk menyimpan:

<string name="appStore">&lt;a href=http://market.android.com/details?id=com.puzzle.jigsaw>Android Market&lt;/a> or &lt;a href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw>Amazon Appstore&lt;/a> &lt;br>http://market.android.com/details?id=com.puzzle.jigsaw &lt;br>href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw</string>
<string name="appStore_amazon">&lt;a href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw>Amazon Appstore&lt;/a> &lt;br>href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw</string>

dan gunakan appStore untuk semua produk Amazone dan appStore_amazon untuk Kindle.

Bagaimana menentukan di mana Anda berada di run time - itu akan menjadi pertanyaan lain yang dijawab di sini berkali-kali.


1
2018-04-08 21:04



Saya terinspirasi oleh jawaban PoinsoneR untuk membuat kelas Utilitas untuk melakukan hal yang sama untuk Fragmen - mengesampingkan fragmen di Perpustakaan android. Langkah-langkahnya mirip dengan jawabannya, jadi saya tidak akan menjelaskannya secara mendetail, tetapi ini kelasnya:

package com.mysweetapp.utilities;

import android.support.v4.app.Fragment;

public class FragmentUtilities 
{
    private static Class getFragmentClass(Class clazz) 
    {
        // Check for extended fragment
        String extClassName = clazz.getName() + "Extended";
        try 
        {
            Class extClass = Class.forName(extClassName);
            return extClass;
        } 
        catch (ClassNotFoundException e) 
        {
            e.printStackTrace();
            // Extended class is not found return base
            return clazz;
        }
    }

    public static Fragment getFragment(Class clazz) 
    {
        Class fragmentClass = getFragmentClass(clazz);

        Fragment toRet = null;

        try 
        {
            toRet = (Fragment)fragmentClass.newInstance();

            return toRet;
        } 
        catch (InstantiationException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        catch (IllegalAccessException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return toRet;
    }
}

Pemakaian:

FragmentUtilities.getFragment(MySpecialFragment.class)

1
2018-03-04 21:48



Anda juga dapat menggunakan pabrik Aktivitas jika Anda perlu menyediakan aktivitas tambahan untuk varian versi differnt dan memiliki perpustakaan Anda berurusan dengan pabrik abstrak saja. Ini dapat diatur dalam file aplikasi varian build Anda.


1
2017-11-12 12:18