ฉันจะรับเนื้อหาทรัพยากรจากบริบทแบบคงที่ได้อย่างไร


168

ฉันต้องการอ่านสตริงจากxmlไฟล์ก่อนที่ฉันจะทำอะไรอื่น ๆ อีกมากมายเช่นsetTextในวิดเจ็ตดังนั้นฉันจะทำสิ่งนั้นโดยไม่ต้องเรียกใช้ออบเจ็กต์กิจกรรมได้getResources()อย่างไร

คำตอบ:


373
  1. สร้างคลาสย่อยของ Applicationเช่นpublic class App extends Application {
  2. ตั้งค่าandroid:nameแอตทริบิวต์ของ<application>แท็กของคุณในAndroidManifest.xmlเพื่อชี้ไปที่คลาสใหม่ของคุณเช่นandroid:name=".App"
  3. ในonCreate()วิธีการของแอปอินสแตนซ์ของคุณให้บันทึกบริบท (เช่นthis) ไปยังฟิลด์สแตติกที่ตั้งชื่อmContextและสร้างวิธีสแตติกที่ส่งคืนฟิลด์นี้เช่นgetContext():

นี่คือลักษณะที่ควร:

public class App extends Application{

    private static Context mContext;

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

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

ตอนนี้คุณสามารถใช้: App.getContext()เมื่อใดก็ตามที่คุณต้องการรับบริบทแล้วgetResources()(หรือApp.getContext().getResources())


9
อินสแตนซ์ของแอปพลิเคชันไม่ใช่ค่าแบบไดนามิก @Gangnus เป็นอย่างไร ไม่ว่าในกรณีใด - ฉันพบวิธีที่ยากที่ต้องพึ่งพาสถิตยศาสตร์ใน Android เป็นอะไรนอกจากปวดหัว "ตอนนี้คุณเห็นแล้วตอนนี้คุณไม่ได้"
Bostone

18
ฉันไม่สามารถหลีกเลี่ยงการคิดว่านี่เป็น 'แฮ็ค' Altough ฉันใช้มัน (btw ขอบคุณที่ให้ทางออกนี้เนื่องจากฉันกำลังจะส่งออกการแปล) ฉันได้รับความรู้สึกไม่ดีเช่นนี้ไม่ถูกต้องอย่างใด
Illiax

8
ดีกว่าหรือแย่กว่าการส่งผ่านบริบทเป็นพารามิเตอร์แรกในทุกวิธีแบบคงที่เดียวในแอปของคุณหรือไม่ อดีตรู้สึกแฮ็ก แต่สิ่งหลังนั้นซ้ำซากจำเจ
เดฟ

12
เอกสารบอกว่า "โดยปกติไม่จำเป็นต้องใช้ subclass Application ในสถานการณ์ส่วนใหญ่ singletons แบบสแตติกสามารถให้ฟังก์ชันการทำงานที่เหมือนกันในแบบแยกส่วนถ้า singleton ของคุณต้องการบริบทส่วนกลาง (ตัวอย่างเช่นการลงทะเบียนเครื่องรับสัญญาณออกอากาศ) มันสามารถได้รับบริบทซึ่งภายในใช้ Context.getApplicationContext () เมื่อสร้างซิงเกิลครั้งแรก " ~ developer.android.com/reference/android/app/Application.html
David d C e Freitas

25
เพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำมันจะดีกว่าที่จะเก็บบริบทใน WeakReference: ส่วนตัวคง WeakReference <Context> mContext; บริบทคงที่สาธารณะ getContext () {ส่งคืน mContext.get (); } สิ่งนี้จะช่วยได้เมื่อแอปขัดข้องและคุณไม่สามารถตั้งค่าบริบทสแตติกเป็นโมฆะ (WeakReference สามารถเก็บขยะได้)
FrankKrumnow

102

สำหรับทรัพยากรระบบเท่านั้น!

ใช้

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

คุณสามารถใช้งานได้ทุกที่ในแอปพลิเคชันของคุณแม้ในการประกาศค่าคงที่แบบคงที่!


2
มันเท่ห์มาก ฉันมักจะไม่รู้สึกขุ่นเคือง ... เมื่อมีคนใช้ตัวพิมพ์ใหญ่: P แค่ล้อเล่น มาตรฐานของคุณใช้ได้กับทรัพยากรบางอย่างเช่นสตริงและ drawables ... อย่างไรก็ตามตามที่เอกสารระบุว่ามันไม่ทำงานได้ดีสำหรับสิ่งต่าง ๆ เช่นมาตรการการวางแนว ฯลฯ และที่สำคัญที่สุดคือสิ่งนี้จะไม่อนุญาตให้คุณรับ บริบททั่วโลกซึ่งบางครั้งมีประโยชน์สำหรับสิ่งที่อาจต้องการ (ยกToastตัวอย่างเช่นรับSharedPreferenceอินสแตนซ์เปิดฐานข้อมูลตามที่ครูภาษาละตินของฉันพูดว่า: et cetera )
Cristian

1
คุณไม่สามารถแม้แต่จะได้รับความสงบสุขในโลกนี้ด้วย :-) แต่ช่วยในการแก้ปัญหาที่ตั้งไว้ตามคำถามที่นี่ ฉันไม่ได้บอกว่ามันแก้ปัญหาทุกงานเพียง แต่มันแก้งานเกือบทุกที่ในแอปพลิเคชัน ฉันค้นหาวิธีแก้ปัญหาดังกล่าวเป็นเวลา 10 เดือน - ตลอดเวลาที่ฉันใช้ Android และตอนนี้ฉันก็พบมันแล้ว
Gangnus

18
คุณต้องระวังที่นี่ อย่าพยายามค้นหาแหล่งข้อมูลแอปของคุณด้วยวิธีนี้ อ่านการพิมพ์อย่างละเอียด: ส่งคืนออบเจกต์ร่วมทรัพยากรระดับโลกที่ให้การเข้าถึงทรัพยากรระบบเท่านั้น (ไม่มีทรัพยากรแอปพลิเคชัน) และไม่ได้กำหนดค่าสำหรับหน้าจอปัจจุบัน (ไม่สามารถใช้หน่วยมิติไม่เปลี่ยนแปลงตามการวางแนว ฯลฯ )
Bostone

4
@ DroidIn.net การอ้างอิง: "แต่สำหรับทรัพยากรระบบเท่านั้น!" ฉันรู้ / * ถอนหายใจ / *
Gangnus

1
ฉันได้รับข้อยกเว้นในการใช้งาน: android.content.res.Resources $ NotFoundException: ID ทรัพยากรของสตริง
vinidog

6

โซลูชัน Kotlin ของฉันคือการใช้บริบทแอปพลิเคชันแบบคงที่:

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

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

และคลาส Strings ที่ฉันใช้ทุกที่:

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

ดังนั้นคุณสามารถมีวิธีที่สะอาดในการรับสตริงรีซอร์ส

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

โปรดอย่าลบคำตอบนี้ให้ฉันเก็บไว้


วิธีแก้ปัญหาที่ง่ายและสะอาดขอขอบคุณที่แบ่งปันรหัส!
Jeehut

ขอบคุณ! แม้ว่านี่จะเป็นวิธีแก้ปัญหาที่รู้จัก แต่Stringsก็มีประโยชน์
CoolMind

4

นอกจากนี้ยังมีความเป็นไปได้อีกอย่าง ฉันโหลด OpenGl shaders จากแหล่งข้อมูลเช่นนี้:

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);
}

อย่างที่คุณเห็นคุณสามารถเข้าถึงทรัพยากรใด ๆ ในเส้นทาง/res/... เปลี่ยนaClassเป็นชั้นเรียนของคุณ นี่คือวิธีที่ฉันโหลดทรัพยากรในการทดสอบ (androidTests)


1
ทางออกเดียวที่ทำงานสำหรับฉันเมื่อไม่มีกิจกรรม (การพัฒนาปลั๊กอินที่ไม่มีคลาสที่สามารถขยายแอปพลิเคชัน) ขอบคุณ +1
itaton

3

The 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;
    }

}

เริ่มต้น Singleton ในApplicationคลาสย่อยของคุณ:

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);
    }
}

หากฉันไม่ผิดมันทำให้คุณมีแอพพลิเคชั่นคอนเทนท์ได้ทุกที่เรียกมันด้วย ApplicationContextSingleton.getInstance.getApplicationContext(); คุณไม่จำเป็นต้องล้างข้อมูล ณ จุดใด ๆ เช่นเมื่อปิดแอปพลิเคชัน

อย่าลืมอัปเดตAndroidManifest.xmlเพื่อใช้Applicationคลาสย่อยนี้:

<?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"
    >

ตอนนี้คุณควรจะสามารถใช้ ApplicationContextSingleton.getInstance (). getApplicationContext (). getResources () จากที่ใดก็ได้นอกจากนี้ยังมีสถานที่น้อยมากที่ซับคลาสของแอปพลิเคชันไม่สามารถทำได้

โปรดแจ้งให้เราทราบหากคุณเห็นสิ่งผิดปกติที่นี่ขอบคุณ :)


2

ทางออกอื่น:

หากคุณมีคลาสย่อยแบบสแตติกในคลาสภายนอกแบบไม่คงที่คุณสามารถเข้าถึงทรัพยากรจากภายในคลาสย่อยผ่านตัวแปรสแตติกในคลาสภายนอกซึ่งคุณเริ่มต้นในการสร้างคลาสภายนอก ชอบ

public class Outerclass {

    static String resource1

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

    public static class Innerclass {

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

ฉันใช้มันสำหรับฟังก์ชั่น getPageTitle (ตำแหน่ง int) ของ Static FragmentPagerAdapter ภายใน FragmentActivity ของฉันซึ่งมีประโยชน์เพราะ I8N


2

ทางลัด

ฉันใช้App.getRes()แทนApp.getContext().getResources()(ตามที่ @Cristian ตอบ)

มันง่ายมากที่จะใช้ทุกที่ในรหัสของคุณ!

ดังนั้นนี่คือทางออกที่ไม่เหมือนใครซึ่งคุณสามารถเข้าถึงทรัพยากรได้จากทุกที่Util classโดยที่คุณสามารถเข้าถึงแหล่งข้อมูลได้จากทุกที่เช่น

(1) สร้างหรือแก้ไขApplicationชั้นเรียนของคุณ

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) เพิ่มฟิลด์ชื่อในmanifest.xml <applicationแท็กของคุณ (หรือข้ามสิ่งนี้หากมีอยู่แล้ว)

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

ตอนนี้คุณก็พร้อมแล้ว

ใช้App.getRes().getString(R.string.some_id)ที่ใดก็ได้ในรหัส


0

ฉันคิดว่าเป็นไปได้มากขึ้น แต่บางครั้งฉันใช้วิธีนี้ (เต็มโลก):

    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();

0

ฉันโหลด shader สำหรับ openGL ES จากฟังก์ชั่นคงที่

จำไว้ว่าคุณต้องใช้ตัวพิมพ์เล็กสำหรับชื่อไฟล์และไดเรกทอรีมิฉะนั้นการดำเนินการจะล้มเหลว

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() : "";
    }

    ...

}

0
public Static Resources mResources;

 @Override
     public void onCreate()
     {
           mResources = getResources();
     }

ปัญหาคือ getResources () ต้องการบริบท ดังนั้นนี้อาจจะไม่ได้จริงๆ Soltution สำหรับ "โดยไม่ต้องมีวัตถุกิจกรรม" (ในที่ที่คุณโพสต์เมธอด onCreate ())
โทเบียสรีค

0

ฉันใช้ API ระดับ 27 และพบทางออกที่ดีที่สุดหลังจากดิ้นรนเป็นเวลาประมาณสองวัน หากคุณต้องการอ่านไฟล์ xml จากคลาสที่ไม่ได้มาจากกิจกรรมหรือแอปพลิเคชันให้ทำดังต่อไปนี้

  1. วางไฟล์ testdata.xml ไว้ในไดเรกทอรีสินทรัพย์

  2. เขียนรหัสต่อไปนี้เพื่อรับเอกสาร testdata แยกวิเคราะห์

        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);

-1

ในชั้นเรียนของคุณที่คุณใช้ฟังก์ชั่นแบบคงที่คุณสามารถเรียกวิธีการส่วนตัว \ สาธารณะจากชั้นนี้ ส่วนตัว \ วิธีประชาชนสามารถเข้าถึงgetResources

ตัวอย่างเช่น:

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));
   }
}

และจาก class \ activity อื่น ๆ คุณสามารถโทร:

Text.setColor('some EditText you initialized');

-1

ถ้าคุณมีบริบทผมหมายถึงข้างใน

public void onReceive(Context context, Intent intent){

}

คุณสามารถใช้รหัสนี้เพื่อรับทรัพยากร:

context.getResources().getString(R.string.app_name);

2
ชื่อของคำถามที่พูดในบริบทคงที่ คำตอบของคุณไม่ครอบคลุม
Rune Schjellerup Philosof
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.