【问题标题】:How can I get a resource content from a static context?如何从静态上下文中获取资源内容?
【发布时间】:2011-05-22 11:03:00
【问题描述】:

我想先从xml 文件中读取字符串,然后再对小部件执行setText 之类的其他操作,那么如果没有活动对象来调用getResources(),我该怎么做呢?

【问题讨论】:

    标签: java android static constants android-resources


    【解决方案1】:
    1. 创建Application 的子类,例如public class App extends Application {
    2. AndroidManifest.xml 中设置<application> 标记的android:name 属性以指向您的新类,例如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())。

    【讨论】:

    • 应用程序的实例不是动态值,@Gangnus 怎么会这样?无论如何 - 我发现在 Android 中依赖静态的困难方式只不过是令人头疼。 “现在你看到了,现在你没有”
    • 我无法避免认为这是一个“黑客”。尽管我正在使用它(顺便说一句,感谢您提供此解决方案,因为我正要外部化本地化)我有一种不好的感觉,好像这在某种程度上是错误的。
    • 比将 Context 作为应用程序中每个静态方法的第一个参数传入更好还是更差?前者感觉很老套,但后者是不必要的重复。
    • 文档说“通常不需要继承 Application。在大多数情况下,静态单例可以以更模块化的方式提供相同的功能。如果您的单例需要全局上下文(例如注册广播接收器),可以为检索它的函数提供一个上下文,该上下文在第一次构造单例时在内部使用 Context.getApplicationContext()。” ~developer.android.com/reference/android/app/Application.html
    • 为了避免内存泄漏,最好将上下文存储在 WeakReference 中: private static WeakReference mContext; public static Context getContext(){ return mContext.get();当应用程序崩溃并且您无法将静态上下文设置为 null 时,这应该会有所帮助(WeakReference 可以被垃圾收集)。
    【解决方案2】:

    仅适用于系统资源!

    使用

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

    您可以在应用程序的任何地方使用它们,甚至在静态常量声明中!

    【讨论】:

    • 这很酷。我通常不会被冒犯......只是当有人使用大写时 :P 开个玩笑。好吧,您的标准适用于字符串和可绘制对象等某些资源......但是,正如文档所述,它不适用于方向测量等。此外,最重要的是,这不会让您获得全局上下文,有时对可能需要它的事情很有用(例如,提出一个Toast,获得一个SharedPreference 实例,打开一个数据库,正如我的拉丁语老师所说:等等)。
    • 你甚至无法通过它赢得全世界的和平:-)。但这有助于解决此处问题所提出的问题。我并不是说它解决了所有任务,只是说它几乎在应用程序的每个地方都解决了它的任务。我用了 10 个月的时间寻找这样的解决方案——我一直在使用 Android。现在我找到了。
    • 这里你要小心。不要尝试使用此方法查找您的应用资源。阅读细则:返回一个全局共享资源对象,该对象仅提供对系统资源(无应用程序资源)的访问,并且未针对当前屏幕进行配置(不能使用维度单位,不根据方向更改等)。
    • @DroidIn.net 引用:“但仅适用于系统资源!”。我知道/*叹气/*
    • 我遇到了一个异常:android.content.res.Resources$NotFoundException: String resource ID
    【解决方案3】:

    我的 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")
    

    请不要删除这个答案,让我保留一个。

    【讨论】:

    • 简单干净的解决方案,谢谢分享代码!
    • 谢谢!虽然这是一个已知的解决方案,但Strings 很有帮助。
    • 非常感谢。你救了我
    • 非常感谢。优秀的解决方案
    • 老实说,迄今为止我发现的最干净、最简单的,简单实用!!
    【解决方案4】:

    快捷方式

    我使用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 getRes() {
            return res;
        }
    
    }
    

    (2) 将名称字段添加到您的 manifest.xml <application 标签。 (或者如果已经存在则跳过此)

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

    现在你可以走了。

    在代码中的任何位置使用App.getRes().getString(R.string.some_id)

    【讨论】:

    • 这个解决方案对我不起作用,给出 'java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.res.Resources.getString(int)' on a android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3047)'处的空对象引用
    • 我编辑了答案,App 类中的方法是getRes() 而不是getResources()
    • 即使我改变方法,它也行不通。仍然给出空指针异常。请注意,我是从另一个类调用它。
    • 是的,我做到了。您可能想在这里查看我的问题stackoverflow.com/q/63245020/13572191。我也尝试了其他解决方案,虽然它们适用于默认语言,但在更改语言时会失败。感谢回复
    • 您的字符串也应该在其他语言字符串文件中被覆盖。
    【解决方案5】:

    还有另一种可能性。我从以下资源加载 OpenGL 着色器:

    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)

    【讨论】:

    • 在没有 Activity 时唯一对我有用的解决方案(开发没有可以扩展应用程序的类的插件)。谢谢+1
    【解决方案6】:

    单身人士:

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

    Application 子类中初始化 Singleton:

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

    如果我没记错的话,这会给你一个到处都是 applicationContext 的钩子,用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() 了,还有应用程序子类不能使用的极少数地方。

    如果您在这里发现任何问题,请告诉我,谢谢。 :)

    【讨论】:

      【解决方案7】:

      另一种解决方案:

      如果您在非静态外部类中有静态子类,则可以通过外部类中的静态变量从子类中访问资源,您在创建外部类时对其进行初始化。喜欢

      public class Outerclass {
      
          static String resource1
      
          public onCreate() {
              resource1 = getString(R.string.text);
          }
      
          public static class Innerclass {
      
              public StringGetter (int num) {
                  return resource1; 
              }
          }
      }
      

      我将它用于我的 FragmentActivity 中的静态 FragmentPagerAdapter 的 getPageTitle(int position) 函数,由于 I8N,它很有用。

      【讨论】:

        【解决方案8】:

        我认为,更多的方法是可能的。 但有时,我使用这个解决方案。 (完全全球):

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

        【讨论】:

          【解决方案9】:

          我从静态函数为 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() : "";
              }
          
              ...
          
          }
          

          【讨论】:

            【解决方案10】:

            我正在使用 API 级别 27,并在挣扎了大约两天后找到了最佳解决方案。如果要从不是从 Activity 或 Application 派生的类中读取 xml 文件,请执行以下操作。

            1. 将 testdata.xml 文件放入 assets 目录中。

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

            【讨论】:

              【解决方案11】:

              在没有上下文的情况下以 InputStream 的形式重新获取图像:

              Class<? extends MyClass> aClass = MyClass.class;
              URL r = aClass.getResource("/res/raw/test.png");
              URLConnection urlConnection = r.openConnection();
              return new BufferedInputStream(urlConnection.getInputStream());
              

              如果您的文件需要目录树,它也可以工作(资产支持子目录):

              URL r = aClass.getResource("/assets/images/base/2.png");
              

              【讨论】:

                【解决方案12】:

                你为什么不尝试

                Resources.getSystem().getString(R.string.foo);
                

                【讨论】:

                  【解决方案13】:

                  您可以尝试另一种略有不同的方法。

                  您可以像提到的其他解决方案一样子类化Application 类,并存储对Resources 实例的静态引用。

                  创建一个应用程序类并在onCreate方法中初始化Resources变量。这将在您的应用启动时调用。我们可以在这里使用WeakReference 来防止由于将此实例存储为静态变量而可能发生的内存泄漏(尽管这种情况不太可能发生)

                  public class App extends Application {
                      private static WeakReference<Resources> res;
                  

                  既然您提到您只想从 xml 资源声明中检索字符串,则无需将此资源变量公开给其他类,以封装资源实例并防止其泄漏。因此,您可以将引用存储为私有变量。

                  记得在onCreate中初始化这个变量:

                  @Override 
                  public void onCreate() { 
                      super.onCreate(); 
                      res = new WeakReference<>(getResources());
                  }
                  

                  我们还需要在AndroidManifest.xmlapplication 标记下将应用程序的android:name 声明为.App(或您设置的任何其他名称)。

                  <application android:name=".App"
                  ........... other attributes here ...........
                  

                  另一种检索字符串资源的方法不是在其他类中使用Resources 实例(或Context 实例),而是让App 类在静态方法中为您获取此资源。这使实例保持封装/私有。

                  您可以在App 类中使用静态方法来检索这些值(例如getStringGlobal,只是不要将其称为getString,因为它会与默认方法冲突)

                  public static String getStringGlobal(@StringRes int resId) { 
                     if (res != null && res.get() != null) { 
                          return res.get().getString(resId); 
                     } else {
                          // This should not happen, you should throw an exception here, or you can return a fallback string to ensure the app still runs
                      }
                  }
                  

                  正如所见,您还可以添加错误处理以防Resources 的实例不可用(这不应该发生,但以防万一)。

                  然后您可以通过调用检索字符串资源

                  App.getStringGlobal(R.string./*your string resource name*/)
                  

                  所以你的App.java

                  public class App extends Application { 
                      private static WeakReference<Resources> res;
                  
                      @Override 
                      public void onCreate() { 
                          super.onCreate(); 
                          res = new WeakReference<>(getResources());    
                      }
                  
                      public static String getStringGlobal(@StringRes int resId) { 
                         if (res != null && res.get() != null) { 
                              return res.get().getString(resId); 
                         } else {
                          // This should not happen(reference to Resources invalid), you should throw an exception here, or you can return a fallback string to ensure the app still runs
                         }
                      }
                  }
                  

                  【讨论】:

                    【解决方案14】:

                    在您实现static 函数的类中,您可以从该类调用private\public 方法。 private\public 方法可以访问 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));
                       }
                    }
                    

                    从其他类\活动中,您可以调用:

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

                    【讨论】:

                      【解决方案15】:

                      如果你有上下文,我的意思是在里面;

                      public void onReceive(Context context, Intent intent){
                      
                      }
                      

                      您可以使用此代码获取资源:

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

                      【讨论】:

                      • 问题的标题在静态上下文中说明。您的答案未涵盖的内容。
                      【解决方案16】:
                      public Static Resources mResources;
                      
                       @Override
                           public void onCreate()
                           {
                                 mResources = getResources();
                           }
                      

                      【讨论】:

                      • 嗯,问题是,getResources() 需要一个上下文。所以这可能不是“没有活动对象”的真正解决方案(您在其中发布了 onCreate() 方法)
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-04-29
                      • 2012-12-01
                      • 1970-01-01
                      • 2012-08-02
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-10-01
                      相关资源
                      最近更新 更多