Como posso obter um conteúdo de recurso de um contexto estático?

168

Quero ler seqüências de caracteres de um xmlarquivo antes de fazer qualquer outra coisa como setTextnos widgets, então como posso fazer isso sem um objeto de atividade para chamar getResources()?

bebê perdido
fonte

Respostas:

373
  1. Crie uma subclasse de Application, por exemplopublic class App extends Application {
  2. Defina o android:nameatributo da sua <application>tag no AndroidManifest.xmlpara apontar para sua nova classe, por exemploandroid:name=".App"
  3. No onCreate()método da instância do aplicativo, salve o contexto (por exemplo this) em um campo estático chamado mContexte crie um método estático que retorne esse campo, por exemplo getContext():

É assim que deve parecer:

public class App extends Application{

    private static Context mContext;

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

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

Agora você pode usar: App.getContext()sempre que quiser obter um contexto e, em seguida getResources()(ou App.getContext().getResources()).

Cristian
fonte
9
A instância do aplicativo não é um valor dinâmico, como assim @Gangnus? De qualquer forma, eu achei da maneira mais difícil que confiar na estática no Android não passa de dor de cabeça. "Agora você vê, agora você não
18
Não posso deixar de pensar que isso é um 'hack'. Embora eu o esteja usando (btw, obrigado por fornecer esta solução, pois estava prestes a externalizar a localização), sinto esse mau pressentimento, como se isso estivesse errado de alguma forma.
Illiax
8
Melhor ou pior do que apenas passar o Contexto como o primeiro parâmetro em cada método estático no seu aplicativo? O primeiro parece hacky, mas o último é desnecessariamente repetitivo.
Dave
12
Os documentos dizem "Normalmente não há necessidade de subclassificar Aplicativo. Na maioria das situações, singletons estáticos podem fornecer a mesma funcionalidade de uma maneira mais modular. Se o seu singleton precisar de um contexto global (por exemplo, para registrar receptores de transmissão), a função a ser recuperada pode receber um Context que usa internamente Context.getApplicationContext () ao construir o singleton pela primeira vez. " ~ developer.android.com/reference/android/app/Application.html
David d C e Freitas
25
Para evitar vazamento de memória, seria melhor armazenar o Contexto em um WeakReference: private static WeakReference <Context> mContext; Contexto estático público getContext () {return mContext.get (); } Isso deve ajudar quando o aplicativo falha e você não pode definir o contexto estático como nulo (o WeakReference pode ser coletado no lixo).
FrankKrumnow
102

Apenas para recursos do sistema!

Usar

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

Você pode usá-los em qualquer lugar do seu aplicativo, mesmo em declarações de constantes estáticas!

Gangnus
fonte
2
Isso é legal. Normalmente não me ofendo ... justamente quando alguém usa maiúsculas: P Brincadeirinha. Bem, seu padrão funciona para alguns recursos, como strings e drawables ... no entanto, como a documentação diz, ele não funciona bem para coisas como medidas de orientação, etc. Além disso, e mais importante, isso não permitirá que você obtenha um contexto global que às vezes é útil para coisas que podem precisar (criar uma Toastinstância, obter uma SharedPreferenceinstância, abrir um banco de dados, como meu professor de língua latina diz: et cetera ).
Cristian
1
Você não pode nem ganhar a paz em todo o mundo com isso :-). Mas ajuda a resolver o problema definido pela pergunta aqui. Não estou dizendo que resolve todas as tarefas, apenas que resolve quase todas as tarefas do aplicativo. Eu procurei por essa solução por 10 meses - o tempo todo em que uso o Android. E agora eu encontrei.
Gangnus
18
Você tem que ter cuidado aqui. Não tente encontrar os recursos do seu aplicativo usando esse método. Leia as letras miúdas: Retorne um objeto Global compartilhado de Recursos que fornece acesso apenas aos recursos do sistema (sem recursos do aplicativo) e não está configurado para a tela atual (não pode usar unidades de dimensão, não muda com base na orientação, etc.).
Bostone
4
@ Citação DroidIn.net: "Mas apenas para recursos do sistema!". Eu sei / * suspiro / *
Gangnus
1
Eu tenho uma exceção usando que: android.content.res.Resources $ NotFoundException: String recurso ID
vinidog
6

Minha solução Kotlin é usar um contexto de aplicativo estático:

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

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

E a classe Strings, que eu uso em qualquer lugar:

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

Assim, você pode ter uma maneira limpa de obter cadeias de recursos

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

Por favor, não apague esta resposta, deixe-me manter uma.

Vitalii Malyi
fonte
Solução simples e limpa, obrigado por compartilhar o código!
Jeehut
Obrigado! Embora essa seja uma solução conhecida, Stringsfoi útil.
CoolMind
4

Há também outra possibilidade. Eu carrego os shaders OpenGl a partir de recursos como este:

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

Como você pode ver, você pode acessar qualquer recurso no caminho /res/... Change aClasspara sua classe. Também é assim que eu carrego recursos em testes (androidTests)

Gregory Stein
fonte
1
A única solução que funcionou para mim quando não havia uma atividade (desenvolver um plug-in sem uma classe que pudesse estender o aplicativo). Obrigado +1
itaton
3

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

}

Inicialize o Singleton na sua Applicationsubclasse:

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

Se não estou errado, isso dá a você um gancho para o applicationContext em todos os lugares, chame-o com ApplicationContextSingleton.getInstance.getApplicationContext(); Você não precisa limpar isso a qualquer momento, pois quando o aplicativo é fechado, isso também acontece com ele.

Lembre-se de atualizar AndroidManifest.xmlpara usar esta Applicationsubclasse:

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

Agora você deve poder usar ApplicationContextSingleton.getInstance (). GetApplicationContext (). GetResources () de qualquer lugar, também os poucos lugares em que as subclasses de aplicativos não podem.

Avise-me se vir algo errado aqui, obrigado. :)

Versa
fonte
2

Outra solução:

Se você tiver uma subclasse estática em uma classe externa não estática, poderá acessar os recursos de dentro da subclasse por meio de variáveis ​​estáticas na classe externa, que você inicializa na criação da classe externa. Gostar

public class Outerclass {

    static String resource1

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

    public static class Innerclass {

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

Usei-o para a função getPageTitle (int position) do FragmentPagerAdapter estático em meu FragmentActivity, útil por causa do I8N.

Stephan Brunker
fonte
2

Atalho

Eu uso em App.getRes()vez de App.getContext().getResources()(como @ Cristian respondeu)

É muito simples de usar em qualquer lugar do seu código!

Então, aqui está uma solução exclusiva pela qual você pode acessar recursos de qualquer lugar Util class.

(1) Crie ou edite sua Applicationturma.

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) Adicione um campo de nome à sua manifest.xml <applicationtag. (ou Ignore se já estiver lá)

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

Agora você está pronto para ir.

Use App.getRes().getString(R.string.some_id)em qualquer lugar no código.

Khemraj
fonte
0

Eu acho, de mais maneira é possível. Mas, às vezes, estou usando esta solução. (global completo):

    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();
user2684935
fonte
0

Eu carrego o shader para o openGL ES a partir da função estática.

Lembre-se de que você deve usar letras minúsculas para o nome do arquivo e diretório, caso contrário a operação falhará

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

    ...

}
user2174870
fonte
0
public Static Resources mResources;

 @Override
     public void onCreate()
     {
           mResources = getResources();
     }
Makvin
fonte
Bem, o problema é que getResources () precisa de um contexto. Portanto, esta provavelmente não é realmente um soltution pois "sem um objeto atividade" (em que você postou o método onCreate ())
Tobias Reich
0

Estou usando o nível 27 da API e encontrei a melhor solução depois de lutar por cerca de dois dias. Se você deseja ler um arquivo xml de uma classe que não deriva de Activity ou Application, faça o seguinte.

  1. Coloque o arquivo testdata.xml no diretório de ativos.

  2. Escreva o código a seguir para obter o documento testdata analisado.

        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
fonte
-1

Na sua classe, onde você implementa a função estática , você pode chamar um método private \ public dessa classe. O método private \ public pode acessar os getResources .

por exemplo:

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

e de outra classe \ atividade, você pode chamar:

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

se você tem um contexto, quero dizer por dentro;

public void onReceive(Context context, Intent intent){

}

você pode usar este código para obter recursos:

context.getResources().getString(R.string.app_name);
eren130
fonte
2
O título da pergunta diz em um contexto estático. Qual sua resposta não cobre.
Rune Schjellerup Philosof