如何从静态上下文获取资源内容?

我希望在小部件上执行大量类似于 setText的操作之前从 xml文件中读取字符串,所以如果没有活动对象来调用 getResources(),我怎么能做到这一点呢?

148870 次浏览
  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())。

仅用于系统资源!

使用

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

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

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

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

在实现 静电干扰函数的类中,可以从该类调用 私人公共场所方法。私有公共方法可以访问 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');

如果你有一个背景,我的意思是内部;

public void onReceive(Context context, Intent intent){


}

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

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

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


}

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 () ,也是应用程序子类不能使用的极少数地方。

如果你发现这里有什么问题,请告诉我,谢谢。 :)

另一种解决办法:

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

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。

还有另一种可能性,我从下面的资源中加载 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更改为您的类。这也是我如何在测试(androidTest)中加载资源的方式

抄近路

我使用 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)

从静态函数加载 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() : "";
}


...


}
public Static Resources mResources;


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

我使用 API 级别27,并找到了一个最好的解决方案后,挣扎了大约两天左右。如果您想从一个不从 Activity 或 Application 派生的类中读取 xml 文件,那么执行以下操作。

  1. 将 testdata.xml 文件放在 asset 目录中。

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

我的 Kotlin 解决方案是使用静态应用程序上下文:

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


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

还有 String 类,我在任何地方都会用到:

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

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

在没有上下文的情况下获取 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");

你为什么不试试

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

这里有一个可供选择的、略有不同的方法,您可以尝试一下。

您可以像其他解决方案提到的那样子类化 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());
}

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

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

检索字符串资源的另一种方法不是使用其他类(或 Context实例)中的 Resources实例,而是使用 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
}
}
}