How can I get a resource content from a static context? - java

I want to read strings from an xml file before I do much of anything else like setText on widgets, so how can I do that without an activity object to call getResources() on?

Create a subclass of Application, for instance public class App extends Application {
Set the android:name attribute of your <application> tag in the AndroidManifest.xml to point to your new class, e.g. android:name=".App"
In the onCreate() method of your app instance, save your context (e.g. this) to a static field named mContext and create a static method that returns this field, e.g. getContext():
This is how it should look:
public class App extends Application{
private static Context mContext;
#Override
public void onCreate() {
super.onCreate();
mContext = this;
}
public static Context getContext(){
return mContext;
}
}
Now you can use: App.getContext() whenever you want to get a context, and then getResources() (or App.getContext().getResources()).

For system resources only!
Use
Resources.getSystem().getString(android.R.string.cancel)
You can use them everywhere in your application, even in static constants declarations!

My Kotlin solution is to use a static Application context:
class App : Application() {
companion object {
lateinit var instance: App private set
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
And the Strings class, that I use everywhere:
object Strings {
fun get(#StringRes stringRes: Int, vararg formatArgs: Any = emptyArray()): String {
return App.instance.getString(stringRes, *formatArgs)
}
}
So you can have a clean way of getting resource strings
Strings.get(R.string.some_string)
Strings.get(R.string.some_string_with_arguments, "Some argument")
Please don't delete this answer, let me keep one.

Shortcut
I use App.getRes() instead of App.getContext().getResources() (as #Cristian answered)
It is very simple to use anywhere in your code!
So here is a unique solution by which you can access resources from anywhere like Util class .
(1) Create or Edit your Application class.
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) Add name field to your manifest.xml <application tag. (or Skip this if already there)
<application
android:name=".App"
...
>
...
</application>
Now you are good to go.
Use App.getRes().getString(R.string.some_id) anywhere in code.

There is also another possibilty. I load OpenGl shaders from resources like this:
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);
}
As you can see, you can access any resource in path /res/...
Change aClass to your class. This also how I load resources in tests (androidTests)

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;
}
}
Initialize the Singleton in your Application subclass:
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);
}
}
If I´m not wrong, this gives you a hook to applicationContext everywhere, call it with ApplicationContextSingleton.getInstance.getApplicationContext();
You shouldn´t need to clear this at any point, as when application closes, this goes with it anyway.
Remember to update AndroidManifest.xml to use this Application subclass:
<?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"
>
Now you should be able to use ApplicationContextSingleton.getInstance().getApplicationContext().getResources() from anywhere, also the very few places where application subclasses can´t.
Please let me know if you see anything wrong here, thank you. :)

Another solution:
If you have a static subclass in a non-static outer class, you can access the resources from within the subclass via static variables in the outer class, which you initialise on creation of the outer class. Like
public class Outerclass {
static String resource1
public onCreate() {
resource1 = getString(R.string.text);
}
public static class Innerclass {
public StringGetter (int num) {
return resource1;
}
}
}
I used it for the getPageTitle(int position) Function of the static FragmentPagerAdapter within my FragmentActivity which is useful because of I8N.

I think, more way is possible.
But sometimes, I using this solution. (full global):
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();

I load shader for openGL ES from static function.
Remember you must use lower case for your file and directory name, or else the operation will be failed
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() : "";
}
...
}

I am using API level 27 and found a best solution after struggling for around two days. If you want to read a xml file from a class which doesn't derive from Activity or Application then do the following.
Put the testdata.xml file inside the assets directory.
Write the following code to get the testdata document parsed.
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);

Getting image resouse as InputStream without context:
Class<? extends MyClass> aClass = MyClass.class;
URL r = aClass.getResource("/res/raw/test.png");
URLConnection urlConnection = r.openConnection();
return new BufferedInputStream(urlConnection.getInputStream());
If you need derectory tree for your files, it will also works (assets supports sub-dirs):
URL r = aClass.getResource("/assets/images/base/2.png");

why you dont try
Resources.getSystem().getString(R.string.foo);

Here is an alternative, slightly different, approach that you may try.
You could subclass the Application class like what other solutions mentioned, and store a static reference to an instance of Resources.
Create an application class and initialise the Resources variable in the onCreate method. This will be called when your app starts. We can use WeakReference here to prevent memory leaks that might happen as a result of storing this instance as a static variable(although it is very unlikely to happen)
public class App extends Application {
private static WeakReference<Resources> res;
Since you mentioned that you only want to retrieve strings from the xml resource declaration, there is no need to expose this resource variable to other classes, for encapsulation of the resources instance and to prevent it from leaking out. Hence, you may store the reference as a private variable.
Remember to initialise this variable in onCreate:
#Override
public void onCreate() {
super.onCreate();
res = new WeakReference<>(getResources());
}
We also need to declare the application's android:name as .App(or any other name you set it to) in AndroidManifest.xml under the application tag.
<application android:name=".App"
........... other attributes here ...........
Another way of retrieving the string resource is not by using the Resources instance in other classes(or the Context instance), but to get the App class to get this for you in a static method. This keeps the instance encapsulated/private.
You can use a static method in your App class to retrieve these values(e.g. getStringGlobal, just do not call it getString as it will conflict with the default method)
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
}
}
As seen, you can also add error handling in case the instance of Resources is not available(this should not happen, but just in case).
You can then retrieve the string resource by calling
App.getStringGlobal(R.string./*your string resource name*/)
So your 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
}
}
}

In your class, where you implement the static function, you can call a private\public method from this class. The private\public method can access the getResources.
for example:
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));
}
}
and from other class\activity, you can call:
Text.setColor('some EditText you initialized');

if you have a context, i mean inside;
public void onReceive(Context context, Intent intent){
}
you can use this code to get resources:
context.getResources().getString(R.string.app_name);

public Static Resources mResources;
#Override
public void onCreate()
{
mResources = getResources();
}

Related

Volley static callback listeners context GCed

After using LeakCanary I found that there were many leaks in my app, most of them due to Volley's anonymous callback listeners. So I wrote a Util (below) class which uses static callbacks and WeakReference to keep reference to Context and an anonymous callback. But when I open the app for the first time, i.e. a cold start, the context is GCed soon after the request is made but during a warm start all works fine. Also this happens only for the first activity in the app.
Any alternative way of handling memory leaks with volley which works properly are also welcome.
public abstract class VUtil {
public static final String TAG = VUtil.class.getSimpleName();
public interface JsonCallback {
void onSuccess(JSONObject response);
}
public interface StringCallback {
void onSuccess(String response);
}
public interface ErrorCallback {
void onError(VolleyError error);
}
public static class JsonResponseListener implements Response.Listener<JSONObject> {
private final WeakReference<Context> mContextWeakReference;
private final WeakReference<JsonCallback> mCallbackWeakReference;
public JsonResponseListener(Context context, JsonCallback callback) {
mContextWeakReference = new WeakReference<>(context);
mCallbackWeakReference = new WeakReference<>(callback);
}
#Override
public void onResponse(JSONObject jsonObject) {
Context context = mContextWeakReference.get();
JsonCallback callback = mCallbackWeakReference.get();
if (context != null && callback != null) {
callback.onSuccess(jsonObject);
} else {
Log.d(TAG, "Context was GCed");
}
}
}
public static class StringResponseListener implements Response.Listener<String> {
private final WeakReference<Context> mContextWeakReference;
private final WeakReference<StringCallback> mCallbackWeakReference;
public StringResponseListener(Context context, StringCallback callback) {
mContextWeakReference = new WeakReference<>(context);
mCallbackWeakReference = new WeakReference<>(callback);
}
#Override
public void onResponse(String response) {
Context context = mContextWeakReference.get();
StringCallback callback = mCallbackWeakReference.get();
if (context != null && callback != null) {
callback.onSuccess(response);
} else {
Log.d(TAG, "Context was GCed");
}
}
}
public static class ErrorListener implements Response.ErrorListener {
private final WeakReference<Context> mContextWeakReference;
private final WeakReference<ErrorCallback> mCallbackWeakReference;
public ErrorListener(Context context, ErrorCallback callback) {
mContextWeakReference = new WeakReference<>(context);
mCallbackWeakReference = new WeakReference<>(callback);
}
#Override
public void onErrorResponse(VolleyError error) {
Context context = mContextWeakReference.get();
ErrorCallback callback = mCallbackWeakReference.get();
if (context != null && callback != null) {
callback.onError(error);
} else {
Log.d(TAG, "Context was GCed");
}
}
}
}
GC depends on many many things that are happening. One possible cause for your case is that when you do your first request after a 'cold boot' you app must init various custom objects, fragments, activities, views caches etc. and thus needs memory before increases the heap and thus do a GC.
The solution I propose however is to change your architecture.
1) it seems that u keep ref to context but it is never used. just drop it
2) you have Volley callbacks which delegates to your custom callbacks which you need to pass anyway, why don't you simply use 1 set of callbacks which you pass to the respective requests.
3) you WeekRef your custom callbacks but u cannot do without them. Week Referencing is not the ultimate solution to memory leaks. you have to find out why the ref is still there when you don't need it.
so if you leak issue is in JsonCallback, StringCallback and ErrorCallback implementations just try to figure this out instead of making the chain longer and cutting it at the end.
Thanks to djodjo's answer which helped me to reach a solution
Always use addToRequestQueue(request, TAG). Here TAG bit is what we'll use to cancel requests when their Activity/Fragment/View or anything is GCed
What i did is create a base activity and add all this request cancellation code in that activity. Here's what it looks like
public abstract class BaseActivity extends AppCompatActivity {
public final String tag;
public BaseActivity() {
super();
tag = getClass().getSimpleName();
}
#Override
protected void onDestroy() {
App.getInstance().cancelRequests(tag);
super.onDestroy();
}
protected <T> void addToRequestQueue(Request<T> request) {
App.getInstance().addToRequestQueue(request, tag);
}
}
cancelRequests is just simple code
getRequestQueue().cancelAll(tag);
Extend your activities from this BaseActivity and use addToRequestQueue to make requests, which will get cancelled automatically when your activity is destroyed. Do similar thing for fragment / dialog / whatever else.
If you make requests from anywhere else which doesn't follow a life-cycle, make sure that it's not binding to any Context and you'll be fine.

Application Context in Non Activity Class?

There is alot of info on this topic but none of it assist me.
I have several global variables/fields inside the app's application class:
public class App extends Application {
private static App instance
public string PUBNUB_SUB = BuildConfig.PUBNUB_SUB_KEY;
public string PUBNUB_PUB = BuildConfig.PUBNUB_PUB_KEY;
public void onCreate() {
instance = this;
}
public static App getInstance(){
return instance;
}
}
In Activities/Fragments I'm successfully accessing those variables like so:
class ActivityA extend Activity {
App baseApp;
Pubnub pubnub;
public void onCreate(Bundle savedInstanceState){
baseApp = new(App)getApplicationContext();
Pubnub(baseApp.PUBNUB_SUB, baseApp.PUBNUB_PUB)
}
Now How do I access getApplicationContext() in a non-Activity/non-Fragment Class?
public class Events {
App baseApp;
Context mContext;
app = new(LoQooApp)mContext.getApplication(); ???
public Events(Context context) {
app = new(LoQooApp)mContext.getApplication(); ???
}
The above doesnt work, where should
app = new(LoQooApp) getApplication(); go?
Looks like PUBNUB_SUB and PUBNUB_PUB are constants, so you could declare them as public static final and then access using a static reference: App.PUBNUB_SUB and App.PUBNUB_PUB

Android - get manifest meta-data out of activity

I want to do something like this
private static final String url;
private static final String pass;
private static final String user;
static {
Bundle metadata = ctx.getPackageManager().getApplicationInfo(ctx.getPackageName(), PackageManager.GET_META_DATA).metaData;
url = (String) metadata.get("JMSQueueURL");
user = (String) metadata.get("JMSQueueUsername");
pass = (String) metadata.get("JMSQueuePassword");
}
So far it was in activity (but not as static), so that I was possible to get package manager, but now i want to move this piece of code to another class which doesn't inherits ContextWrapper (from where we can get package manager). Is it possible somehow? This class is something like util class.
You can pass the Context from the calling method to the method in the Util class and use the context there to get the details you want. This way you can call the method in the Util class from different modules in your application, with different contexts.
// Calling the Util method
Bundle metadata = Util.getMetaData(context);
...
// Inside the Util class
public static Bundle getMetaData(Context context) {
return context.getPackageManager().getApplicationInfo(ctx.getPackageName(), PackageManager.GET_META_DATA).metaData;
}
This allows me to access the metadata from anywhere in my application, without a context:
public class MyAndroidApp extends Application {
private static MyAndroidApp instance;
#Override
public void onCreate() {
super.onCreate();
instance = this;
}
public static MyAndroidApp getInstance() {
return instance;
}
public static Bundle getMetadata() {
try {
return getInstance()
.getPackageManager()
.getApplicationInfo(getInstance().getPackageName(), PackageManager.GET_META_DATA)
.metaData;
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
}

Android - Random NullPointerException crash reports

Sometimes I'm receiving crash reports from google caused by random NullPointerExceptions (see above). I tried to reproduce those errors but I'm not able to catch them.
Examples of NPE I get :
Caused by: java.lang.NullPointerException
at com.gamequiz.databasemanager.CategoryManager.getAllCategories(CategoryManager.java:28)
Caused by: java.lang.NullPointerException
at com.gamequiz.databasemanager.QuestionManager.getQuestionsFromLevel(QuestionManager.java:30)
at com.gamequiz.databasemanager.QuestionManager.getNumberOfQuestionAnsweredFromLevel(QuestionManager.java:148)
I though that my dbHelper variable is null sometimes but I can't figure out why.
Since I don't know how to resolve that, I post the all steps of my code :
First of all I initialize all the managers in the LaunchActivity :
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_launch);
initializeAllManagers();
//some stuff
}
public void initializeAllManagers(){
InitializeAllManagers.init(getApplicationContext());
}
In my InitializeAllManagers class, I set all the managers I need for the lifecycle of the app :
public class InitializeAllManagers {
public static void init(Context context){
DatabaseManager.init(context);
CategoryManager.init(DatabaseManager.getInstance().getHelper());
//and others initializations
}
}
DatabaseManager class (initialization of the dbManager and dbHelper) :
public class DatabaseManager {
private static DatabaseManager instance;
private DatabaseHelper helper;
public static void init(Context ctx) {
if (instance==null) {
instance = new DatabaseManager(ctx);
}
}
public static DatabaseManager getInstance() {
return instance;
}
private DatabaseManager(Context ctx) {
helper = new DatabaseHelper(ctx);
}
public DatabaseHelper getHelper() {
return helper;
}
}
Finally there is an example of one manager :
public class CategoryManager {
private static DatabaseHelper dbHelper;
public static void init(DatabaseHelper dbHelperInstance) {
dbHelper = dbHelperInstance;
}
public static ArrayList <Category> getAllCategories(){
ArrayList <Category> cList = null;
try {
cList = (ArrayList<Category>) dbHelper.getCategoryDao().queryForAll();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return cList;
}
}
So I suspect that my dbHelper variable is null sometimes. Does anyone have an idea about how I can solve this issue ?
EDIT :
NPE mainly refers to this lines :
cList = (ArrayList<Category>) dbHelper.getCategoryDao().queryForAll();
Dao <Question, Long> questionDao = dbHelper.getQuestionDao();
That's why I suspect that dbHelper is null sometimes, and apparently crashes occurs when the app is sleeping for a moment (see feedback above).
Feedback of one user :
So, mainly if I leave the app without exiting it, the app will often
crash when I try to go back to it. Sometimes I just get sent back to
the menu, but mostly just all the way out of the app and I have to
restart it to continue.
I suspect that this happens when the app is minimized for a while and then get re-opened. Android can and will remove Objects from the heap if it feels like it needs the memory. Especially static variables can be removed. Check static variables for null before you access them.
For example in your DatabaseManager class, create the instance in the getInstance() method instead of in init().
public static DatabaseManager getInstance() {
if(instance == null) instance = new DatabaseManager();
return instance;
}
Edit:
Please note that my reasoning in this answer is wrong. Please read all the comments for clarification.

Detecting if you're in the main process or the remote service process in Application

I have an application which has a remote service running in a separate process:
<service android:name=".MyService" android:process=":remote"/>
I'm also using an Application class:
<application android:label="#string/app_name" android:name=".MyApplication" ...
Can I do something like this?
public class MyApplication extends Application {
public MyApplication() {
if (isRemoteService()) {
setupLog("remoteservice.log");
} else {
setupLog("application.log");
}
}
I'm thinking I could get the process name and use that to detect if I'm in the remote service or the main app, but I haven't found out how to get the process name. I can get the PID from android.os.Process.myPID(), but that doesn't help me much.
For example, if you want to check whether you are in main process, you can write code in your application like this:
public class MyApplication extends Application {
#Override
public void onCreate() {
//...code here will be execute in every process...
if (isMainProcess()) {
//...code here will be execute only in main process
}
super.onCreate();
}
// your package name is the same with your main process name
private boolean isMainProcess() {
return getPackageName().equals(getProcessName());
}
// you can use this method to get current process name, you will get
// name like "com.package.name"(main process name) or "com.package.name:remote"
private String getProcessName() {
int mypid = android.os.Process.myPid();
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> infos = manager.getRunningAppProcesses();
for(RunningAppProcessInfo info : infos) {
if (info.pid == mypid) {
return info.processName;
}
}
// may never return null
return null;
}
}
I can offer an indirect solution:
Within each of the respective StartUp methods, set a System Property:
System.setProperty("PROCESS_TYPE","SERVICE");
System.setProperty("PROCESS_TYPE","RECEIVER");
System.setProperty("PROCESS_TYPE","ACTIVITY");
The Properties are static, isolated to the process, and can be reached from everywhere. Added benefit is they can be directly used by logging frameworks like Logback.
I had a similar issue and this is what I did.
MyService and MyActivity have a common part, MyEngine, but the behaviour must be a bit different in these two cases.
One thing that is different is setup, but this setup is done in the classes MyService and MyActivity.
Another thing that is different for the activity and the service is done via a listener: MyEngine defines the interface MyEngine.Listener while MyService and MyActivity provide the engine with different implementations of that interface.
So, if you want to pass a boolean value, two methods are possible:
// Method 1: different initialization
class MyEngine {
MyEngine(boolean isService) { ... }
}
class MyActivity extends Activity {
private MyEngine = new MyEngine(false);
...
}
class MyService extends Service {
private MyEngine = new MyEngine(true);
...
}
// Method 2: callbacks
class MyEngine {
interface Listener {
boolean isService();
}
private Listener mListener;
MyEngine(Listener listener) { mListener = listener; }
}
class MyActivity extends Activity {
private mListener = new MyEngine.Listener() {
boolean isService() { return false; }
}
private MyEngine = new MyEngine(mListener);
...
}
class MyService extends Service {
private mListener = new MyEngine.Listener() {
boolean isService() { return true; }
}
private MyEngine = new MyEngine(mListener);
...
}
Notes.
The boolean value used in the above example is useless in the real world: if you want to use, say, different log file names, it is better to pass the file name rather than the boolean. If you want to perform two different actions, it's better to have one listener function with two implementations.
Of course, one could pass a Context and check if it is a child of Activity or a Service, or get the name of the current process, but these things are Android-specific implementation details, and it's better not to depend on them unless absolutely necessary.
I'm using a piece of code, which is taken from Google's WorkManager lib. As of writing this it's located in GreedyScheduler.java. Having defined method getProcessName() as such:
#Nullable
private String getProcessName() {
if (SDK_INT >= 28) {
return Application.getProcessName();
}
// Try using ActivityThread to determine the current process name.
try {
Class<?> activityThread = Class.forName(
"android.app.ActivityThread",
false,
GreedyScheduler.class.getClassLoader());
final Object packageName;
if (SDK_INT >= 18) {
Method currentProcessName = activityThread.getDeclaredMethod("currentProcessName");
currentProcessName.setAccessible(true);
packageName = currentProcessName.invoke(null);
} else {
Method getActivityThread = activityThread.getDeclaredMethod(
"currentActivityThread");
getActivityThread.setAccessible(true);
Method getProcessName = activityThread.getDeclaredMethod("getProcessName");
getProcessName.setAccessible(true);
packageName = getProcessName.invoke(getActivityThread.invoke(null));
}
if (packageName instanceof String) {
return (String) packageName;
}
} catch (Throwable exception) {
Log.d("TAG", "Unable to check ActivityThread for processName", exception);
}
// Fallback to the most expensive way
int pid = Process.myPid();
ActivityManager am =
(ActivityManager) mContext.getSystemService(ACTIVITY_SERVICE);
if (am != null) {
List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
if (processes != null && !processes.isEmpty()) {
for (ActivityManager.RunningAppProcessInfo process : processes) {
if (process.pid == pid) {
return process.processName;
}
}
}
}
return null;
}
Then on the calling side:
boolean isMainProcess() {
return TextUtils.equals(mContext.getPackageName(), getProcessName());
}

Categories