Bagaimana saya bisa mendapatkan konten sumber daya dari konteks statis?

168

Saya ingin membaca string dari xmlfile sebelum saya melakukan banyak hal lain seperti setTextpada widget, jadi bagaimana saya bisa melakukannya tanpa objek aktivitas untuk dipanggil getResources()?

bayi yang hilang
sumber

Jawaban:

373
  1. Buat subkelas Application, misalnyapublic class App extends Application {
  2. Tetapkan android:nameatribut <application>tag Anda di AndroidManifest.xmluntuk menunjuk ke kelas baru Anda, misalnyaandroid:name=".App"
  3. Dalam onCreate()metode instance aplikasi Anda, simpan konteks Anda (misalnya this) ke bidang statis bernama mContextdan buat metode statis yang mengembalikan bidang ini, misalnya getContext():

Begini tampilannya:

public class App extends Application{

    private static Context mContext;

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

    public static Context getContext(){
        return mContext;
    }
}

Sekarang Anda dapat menggunakan: App.getContext()kapan pun Anda ingin mendapatkan konteks, dan kemudian getResources()(atau App.getContext().getResources()).

Cristian
sumber
9
Instance dari aplikasi ini bukan nilai dinamis, bagaimana bisa begitu @Gangnus? Dalam hal apapun - saya menemukan cara sulit yang mengandalkan statika di Android tidak lain adalah sakit kepala. "Sekarang kamu melihatnya, sekarang kamu tidak"
Bostone
18
Saya tidak bisa menghindari berpikir bahwa ini adalah 'hack'. Meskipun saya menggunakannya (btw terima kasih telah memberikan solusi ini, karena saya akan mengeksternalisasi lokalisasi) saya mendapatkan perasaan buruk ini, seperti ini entah bagaimana salah.
Illiax
8
Lebih baik atau lebih buruk daripada hanya meneruskan dalam Konteks sebagai parameter pertama dalam setiap metode statis di aplikasi Anda? Yang pertama terasa berantakan, tetapi yang terakhir berulang-ulang sia-sia.
Dave
12
Dokumen mengatakan "Biasanya tidak perlu untuk subkelas Aplikasi. Dalam kebanyakan situasi, lajang statis dapat menyediakan fungsionalitas yang sama dengan cara yang lebih modular. Jika lajang Anda membutuhkan konteks global (misalnya untuk mendaftarkan penerima siaran), fungsi untuk mengambil dapat diberikan Konteks yang secara internal menggunakan Context.getApplicationContext () saat pertama kali membangun singleton. " ~ developer.android.com/reference/android/app/Application.html
David d C e Freitas
25
Untuk menghindari kebocoran memori, sebaiknya simpan Konteks dalam WeakReference: private static WeakReference <Context> mContext; public static Context getContext () {return mContext.get (); } Ini akan membantu ketika aplikasi mogok dan Anda tidak dapat mengatur konteks statis ke nol (WeakReference dapat dikumpulkan dari sampah).
FrankKrumnow
102

Hanya untuk sumber daya sistem!

Menggunakan

Resources.getSystem().getString(android.R.string.cancel)

Anda dapat menggunakannya di mana-mana dalam aplikasi Anda, bahkan dalam deklarasi konstanta statis!

Gangnus
sumber
2
Itu keren. Saya biasanya tidak tersinggung ... hanya ketika seseorang menggunakan huruf besar: P Hanya bercanda. Nah, standar Anda berfungsi untuk beberapa sumber daya seperti string dan drawable ... namun, seperti yang dikatakan dalam dokumentasi, itu tidak berfungsi baik untuk hal-hal seperti ukuran orientasi, dll. Juga, dan yang paling penting, ini tidak akan memungkinkan Anda untuk mendapatkan konteks global yang kadang-kadang berguna untuk hal-hal yang mungkin membutuhkannya (meningkatkan Toastmisalnya, mendapatkan SharedPreferencecontoh, membuka database, seperti kata guru bahasa Latin saya: et cetera ).
Cristian
1
Anda bahkan tidak bisa memenangkan kedamaian di seluruh dunia dengan itu :-). Tetapi membantu memecahkan masalah yang ditetapkan oleh pertanyaan di sini. Saya tidak mengatakan itu menyelesaikan setiap tugas, hanya itu menyelesaikan tugasnya hampir di setiap tempat dalam aplikasi. Saya mencari solusi seperti itu selama 10 bulan - sepanjang waktu saya menggunakan Android. Dan sekarang saya menemukannya.
Gangnus
18
Anda harus berhati-hati di sini. Jangan mencoba menemukan sumber daya aplikasi Anda menggunakan metode ini. Baca cetakan halus: Kembalikan objek Sumber Daya global bersama yang menyediakan akses ke hanya sumber daya sistem (tanpa sumber daya aplikasi), dan tidak dikonfigurasikan untuk layar saat ini (tidak dapat menggunakan unit dimensi, tidak berubah berdasarkan orientasi, dll.).
Bostone
4
@ Kutipan DroidIn.net: "Tetapi hanya untuk sumber daya sistem!". Saya tahu / * sigh / *
Gangnus
1
Saya mendapat pengecualian menggunakan itu: android.content.res.Resources $ NotFoundException: String resource ID
vinidog
6

Solusi Kotlin saya adalah menggunakan konteks Aplikasi statis:

class App : Application() {
    companion object {
        lateinit var instance: App private set
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
    }
}

Dan kelas Strings, yang saya gunakan di mana-mana:

object Strings {
    fun get(@StringRes stringRes: Int, vararg formatArgs: Any = emptyArray()): String {
        return App.instance.getString(stringRes, *formatArgs)
    }
}

Jadi Anda bisa memiliki cara yang bersih untuk mendapatkan string sumber daya

Strings.get(R.string.some_string)
Strings.get(R.string.some_string_with_arguments, "Some argument")

Tolong jangan hapus jawaban ini, biarkan saya simpan satu.

Vitalii Malyi
sumber
Solusi sederhana dan bersih, terima kasih telah berbagi kode!
Jeehut
Terima kasih! Meskipun ini adalah solusi yang dikenal, Stringssangat membantu.
CoolMind
4

Ada juga kemungkinan lain. Saya memuat shader OpenGl dari sumber daya seperti ini:

static private String vertexShaderCode;
static private String fragmentShaderCode;

static {
    vertexShaderCode = readResourceAsString("/res/raw/vertex_shader.glsl");
    fragmentShaderCode = readResourceAsString("/res/raw/fragment_shader.glsl");
}

private static String readResourceAsString(String path) {
    Exception innerException;
    Class<? extends FloorPlanRenderer> aClass = FloorPlanRenderer.class;
    InputStream inputStream = aClass.getResourceAsStream(path);

    byte[] bytes;
    try {
        bytes = new byte[inputStream.available()];
        inputStream.read(bytes);
        return new String(bytes);
    } catch (IOException e) {
        e.printStackTrace();
        innerException = e;
    }
    throw new RuntimeException("Cannot load shader code from resources.", innerException);
}

Seperti yang Anda lihat, Anda dapat mengakses sumber daya apa pun di jalur /res/... Ubah aClasske kelas Anda. Ini juga cara saya memuat sumber daya dalam pengujian (androidTests)

Gregory Stein
sumber
1
Satu-satunya solusi yang bekerja untuk saya ketika tidak memiliki Kegiatan (mengembangkan plugin tanpa kelas yang dapat memperpanjang Aplikasi). Terima kasih +1
itaton
3

Singleton:

package com.domain.packagename;

import android.content.Context;

/**
 * Created by Versa on 10.09.15.
 */
public class ApplicationContextSingleton {
    private static PrefsContextSingleton mInstance;
    private Context context;

    public static ApplicationContextSingleton getInstance() {
        if (mInstance == null) mInstance = getSync();
        return mInstance;
    }

    private static synchronized ApplicationContextSingleton getSync() {
        if (mInstance == null) mInstance = new PrefsContextSingleton();
        return mInstance;
    }

    public void initialize(Context context) {
        this.context = context;
    }

    public Context getApplicationContext() {
        return context;
    }

}

Inisialisasi Singleton di Applicationsubkelas Anda :

package com.domain.packagename;

import android.app.Application;

/**
 * Created by Versa on 25.08.15.
 */
public class mApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        ApplicationContextSingleton.getInstance().initialize(this);
    }
}

Jika saya tidak salah, ini memberi Anda pengait ke applicationContext di mana saja, sebut saja dengan itu. ApplicationContextSingleton.getInstance.getApplicationContext(); Anda tidak perlu menghapus ini di titik mana pun, seperti saat aplikasi ditutup, ini tetap berlaku.

Ingatlah untuk memperbarui AndroidManifest.xmluntuk menggunakan Applicationsubkelas ini :

<?xml version="1.0" encoding="utf-8"?>

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.domain.packagename"
    >

<application
    android:allowBackup="true"
    android:name=".mApplication" <!-- This is the important line -->
    android:label="@string/app_name"
    android:theme="@style/AppTheme"
    android:icon="@drawable/app_icon"
    >

Sekarang Anda harus dapat menggunakan ApplicationContextSingleton.getInstance (). GetApplicationContext (). GetResources () dari mana saja, juga sangat sedikit tempat di mana subkelas aplikasi tidak bisa.

Tolong beri tahu saya jika Anda melihat sesuatu yang salah di sini, terima kasih. :)

Versa
sumber
2

Solusi lain:

Jika Anda memiliki subkelas statis di kelas luar non-statis, Anda dapat mengakses sumber daya dari dalam subkelas melalui variabel statis di kelas luar, yang Anda inisialisasi saat membuat kelas luar. Suka

public class Outerclass {

    static String resource1

    public onCreate() {
        resource1 = getString(R.string.text);
    }

    public static class Innerclass {

        public StringGetter (int num) {
            return resource1; 
        }
    }
}

Saya menggunakannya untuk Fungsi getPageTitle (posisi int) dari FragmentPagerAdapter statis dalam FragmentActivity saya yang berguna karena I8N.

Stephan Brunker
sumber
2

Jalan pintas

Saya menggunakan App.getRes()sebagai gantinya App.getContext().getResources()(seperti yang dijawab @Cristian)

Sangat mudah digunakan di mana pun dalam kode Anda!

Jadi di sini adalah solusi unik dimana Anda dapat mengakses sumber daya dari mana saja seperti Util class.

(1) Buat atau Edit Applicationkelas Anda .

import android.app.Application;
import android.content.res.Resources;

public class App extends Application {
    private static App mInstance;
    private static Resources res;


    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
        res = getResources();
    }

    public static App getInstance() {
        return mInstance;
    }

    public static Resources getResourses() {
        return res;
    }

}

(2) Tambahkan bidang nama ke manifest.xml <applicationtag Anda . (atau Lewati ini jika sudah ada)

<application
        android:name=".App"
        ...
        >
        ...
    </application>

Sekarang kamu baik untuk pergi.

Gunakan App.getRes().getString(R.string.some_id)di mana saja dalam kode.

Khemraj
sumber
0

Saya pikir, lebih banyak cara adalah mungkin. Namun terkadang, saya menggunakan solusi ini. (global lengkap):

    import android.content.Context;

    import <your package>.R;

    public class XmlVar {

        private XmlVar() {
        }

        private static String _write_success;

        public static String write_success() {
            return _write_success;
        }


        public static void Init(Context c) {
            _write_success = c.getResources().getString(R.string.write_success);
        }
    }
//After activity created:
cont = this.getApplicationContext();
XmlVar.Init(cont);
//And use everywhere
XmlVar.write_success();
pengguna2684935
sumber
0

Saya memuat shader untuk openGL ES dari fungsi statis.

Ingat Anda harus menggunakan huruf kecil untuk nama file dan direktori Anda, jika tidak operasi akan gagal

public class MyGLRenderer implements GLSurfaceView.Renderer {

    ...

    public static int loadShader() {
        //    Read file as input stream
        InputStream inputStream = MyGLRenderer.class.getResourceAsStream("/res/raw/vertex_shader.txt");

        //    Convert input stream to string
        Scanner s = new Scanner(inputStream).useDelimiter("\\A");
        String shaderCode = s.hasNext() ? s.next() : "";
    }

    ...

}
pengguna2174870
sumber
0
public Static Resources mResources;

 @Override
     public void onCreate()
     {
           mResources = getResources();
     }
Makvin
sumber
Nah, masalahnya adalah, getResources () membutuhkan konteks. Jadi ini mungkin bukan solusi untuk "tanpa objek aktivitas" (di mana Anda memposting metode onCreate ())
Tobias Reich
0

Saya menggunakan API level 27 dan menemukan solusi terbaik setelah berjuang selama sekitar dua hari. Jika Anda ingin membaca file xml dari kelas yang tidak berasal dari Aktivitas atau Aplikasi maka lakukan hal berikut.

  1. Letakkan file testdata.xml di dalam direktori assets.

  2. Tulis kode berikut untuk mendapatkan dokumen testdata diuraikan.

        InputStream inputStream = this.getClass().getResourceAsStream("/assets/testdata.xml");
    
        // create a new DocumentBuilderFactory
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // use the factory to create a documentbuilder
        DocumentBuilder builder = factory.newDocumentBuilder();
        // create a new document from input stream
        Document doc = builder.parse(inputStream);
Jnana
sumber
-1

Di kelas Anda, tempat Anda menerapkan fungsi statis , Anda bisa memanggil metode privat \ publik dari kelas ini. Metode privat \ publik dapat mengakses getResources .

sebagai contoh:

public class Text {

   public static void setColor(EditText et) {
      et.resetColor(); // it works

      // ERROR
      et.setTextColor(getResources().getColor(R.color.Black)); // ERROR
   }

   // set the color to be black when reset
   private void resetColor() {
       setTextColor(getResources().getColor(R.color.Black));
   }
}

dan dari aktivitas \ kelas lain, Anda dapat memanggil:

Text.setColor('some EditText you initialized');
Maor Cohen
sumber
-1

jika Anda memiliki konteks, maksud saya di dalam;

public void onReceive(Context context, Intent intent){

}

Anda dapat menggunakan kode ini untuk mendapatkan sumber daya:

context.getResources().getString(R.string.app_name);
eren130
sumber
2
Judul pertanyaan mengatakan dalam konteks statis. Yang mana jawaban Anda tidak mencakup.
Rune Schjellerup Philosof