Acra: install, extend Application - Activity? - android

I am trying to install the ACRA crash report system to my android project. Now, my project is already extending a class, the Activity class. How can I implement the Acra project then?
As they state in normal way, you have to make a class f.e. MyApplication and extend it with Application. Since I am already extending the Activity class I am not sure what to do... They say: If your app already contains an Application subclass, add ACRA to this class; however, I don't know how I should do this..
Thanks!
http://code.google.com/p/acra/wiki/BasicSetup

Just create a MyApplication class that extends from Application, do what they say about overriding onCreate() and then go to your AndroidManifest.
You should have an <application> with values such as android:label or android:theme. Just add android:name=".MyApplication" there and you're ready to go.
Have in mind that if your package is com.example.test, MyApplication has to be there. If you want to put MyApplication wherever else, you must point to where it is.
For example, if your package is com.example.test and MyApplication is in com.example.test.application, you must add android:name=".application.MyApplication to your manifest. I strongly reccomend you to use a package just for your Application, as it atomizes your project and makes it far more manageable and mantainable.

Application is used because of the manifest. In the manifest, it is just to add this to the application tag(with all activities inside):
android:name=".MyApplication"
Ex:
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:name=".MyApplication"
android:theme="#style/AppTheme" >
Because of the easy initialization(as it is automatically initialized by the Android System on launch) it will never not report. It can crash instantly on startup and still report. So it is a really smart setup.
My application class looks like this:
#ReportsCrashes(
formUri = "https://backend.com",
customReportContent = { /* */ReportField.APP_VERSION_NAME, ReportField.PACKAGE_NAME,ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL,ReportField.LOGCAT },
mode = ReportingInteractionMode.TOAST,
resToastText = R.string.crash_toast_text
)
public class ACRAHandler extends Application {
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
final ACRAConfiguration config = new ConfigurationBuilder(this)
.build();
// Initialise ACRA
ACRA.init(this, config);
}
}
If you for an instance are using Firebase, you can use both together in the same application-extending class without any issues. I tried it myself and it worked, no problems with error reporting or Firebase.
Additionally, the new links for ACRA is now on Github: https://github.com/ACRA/acra/wiki/BasicSetup
I answered this because it was so long ago the answers came and it needs an update

An application sub class is required to maintain a global application state, it is not necessary for every app to sub class it. If you app does not have one yet, you can create it.
Example:
/* do ACRA imports */
#ReportsCrashes(formKey = "x-x-x-x-x-x")
public class YourApplication extends Application{
public void onCreate(){
ACRA.init(this);
super.onCreate();
}
}
you should also declare in the manifest file as stated in the tutorial.

Related

WorkManager - Should we remove the default initializer, when we use both Default initialization and Custom initialization?

I'm getting the following new error, when I upgrade WorkManager from "2.2.0" to "2.3.0-rc01"
The error occurs when I'm exporting APK.
C:\app: Error: Remove androidx.work.impl.WorkManagerInitializer from your AndroidManifest.xml when using on-demand initialization. [RemoveWorkManagerInitializer]
Explanation for issues of type "RemoveWorkManagerInitializer":
If an android.app.Application implements
androidx.work.Configuration.Provider,
the default androidx.work.impl.WorkManagerInitializer needs to be removed
from the
AndroidManifest.xml file.
I'm not sure why I didn't get such error in 2.2.0, as "On-Demand Initialization" is introduced since 2.1.0.
According to https://developer.android.com/topic/libraries/architecture/workmanager/advanced/custom-configuration#remove-default
I'm not kinna sure, whether it is a right thing to include the following in my AndroidManifest.xml.
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
Currently, the following is my Application class.
MyApplication class
public class MyApplication extends MultiDexApplication implements Configuration.Provider {
private static MyApplication me;
#Override
public void onCreate() {
super.onCreate();
me = this;
}
public static MyApplication instance() {
return me;
}
#NonNull
#Override
public Configuration getWorkManagerConfiguration() {
return new Configuration.Builder()
.build();
}
}
How I construct WorkManager
public static WorkManager getWorkManager() {
MyApplication myApplication = MyApplication.instance();
if (myApplication == null) {
// Very rare edge case. Not sure how it happens. But, it happens :)
return WorkManager.getInstance();
} else {
return WorkManager.getInstance(myApplication);
}
}
It seems that there is rare chance that "Default initialization" (WorkManager.getInstance()) is being executed too, when the Application class is null.
I can easily eliminate error during APK exporting, by including the following provider. But, is that a right thing to to so?
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
If you are getting this error after updating to WorkManager 2.6.0 or higher, you have to use this snippet in your manifest:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
We introduced this lint rule in WorkManager 2.3.0-*. The problem we are trying to address with this Lint rule is that if you have both the WorkManagerInitializer ContentProvider and your Application subtype implements Configuration.Provider (for on-demand initialization) - the ContentProvider will always take precedence.
This might be unexpected, especially when you have additional Configuration which will not take effect because the ContentProvider always uses the default configuration.
All you really need to do is to remove the default provider. That way initialization will no longer be eager, but be on-demand.
Yes you need to remove the default work manager initializer as you did if you want to use on-demand initialization, so keep the following piece of code in your manifest:
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
Also the above documentation clearly state that you should not be calling WorkManager.getInstance() (without the Context argument):
Note: If you call the deprecated no-parameter WorkManager.getInstance() method before WorkManager has been initialized, the method throws an exception. You should always use the WorkManager.getInstance(Context) method, even if you're not customizing WorkManager.
After looking at the androix/work changelog you will see that a new feature was added in version 2.3.0-beta02:
Added a lint rule which ensures that the content provider androidx.work.impl.WorkManagerInitializer is removed from the AndroidManifest.xml when using on demand initialization. (aosp/1167007)
The reason why you have this error after upgrading from version 2.2.0 to 2.3.0.rc1 is because, the android team added a RemoveWorkManagerInitializerDetector that would throw the exception you got in the following pull request at build time.
Now about the source code, I suggest you tight the getWorkManager method to the application directly like below:
import androidx.annotation.NonNull;
import androidx.work.Configuration;
import androidx.work.WorkManager;
public class App extends MultiDexApplication implements Configuration.Provider {
private static App APP_INSTANCE;
#Override
public void onCreate() {
super.onCreate();
APP_INSTANCE = this;
}
public static App getInstance() {
return APP_INSTANCE;
}
#NonNull
#Override
public Configuration getWorkManagerConfiguration() {
return new Configuration.Builder()
.build();
}
public static WorkManager getWorkManager() {
return WorkManager.getInstance(APP_INSTANCE);
}
}
And just call App.getWorkManager() whenever you need to in the app source code
You could do something equivalent for your ContentProvider if there is any.
PS: Interesting codelabs tutorials exits for java or kotlin
WorkManagerInitializer is used to provide a context to WorkManager. This happens because content providers are initialized before the Application (See this question). This is the reason you need to provide the context yourself if you do custom initialization. So if you use the custom initialization, you shouldn't need it.
If you're calling the getWorkManager method from a content provider then your application instance will be null. To resolve this issue just pass the context as a parameter to the method by calling getContext inside the content provider:
public static WorkManager getWorkManager(Context context) {
return WorkManager.getInstance(context);
}
public class MyContentProvider extends ContentProvider {
#Override
public boolean onCreate() {
WorkManager workManager = getWorkManager(getContext());
...
}
...
}

Why doesn't my SplashActivity's initialization code get executed?

My app has some global/static data structures that need to be initialized before showing the main Activity, so I put the work into onCreate method of my SplashActivity, which just shows a splash image for 2 seconds, starts another activity, and finishes itself:
initializeGlobalData();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent i = new Intent(SplashActivity.this, MainActivity.class);
startActivity(i);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
finish();
}
}, 2000);
Now, my app sometimes mysteriously crashes because of null pointer reference - some global data structures are not initialized. It could only mean that the onCreate method of SplashActivity is not called (right?).
I have no idea how to reproduce this, but it happens quite often. It's possible I left the app in the background, and re-enter. But application level data should not be released, right?
It's possible I left the app in the background, and re-enter. But application level data should not be released, right?
It depends on what you mean when you say "global/static data structures that need to be initialized".
If the user leaves your app, it is expected that the Android OS might terminate your app's process. When this happens, anything that is stored only in memory will be lost.
A common example is e.g. some public static value that you load once and then refer to throughout your application. When the OS terminates your app's process, and then the user returns to your app, that public static value will need to be re-initialized.
Splash Activities are, by nature, short lived and shouldn't be relied upon for any global data structures. If you need such, you need to create an Application class and do all of your global data structure initialization there. Those will not go out of scope for the lifetime of the app.
Why not just initialize them in Application class
public class MyApplication extends Application {
private int globalData = 0;
public int getGlobalData() {
return globalData;
}
public void setGlobalData(int globalData) {
this.globalData = globalData ;
}
#Override
public void onCreate() {
super.onCreate();
setGlobalData(100)
}
}
Change the application tag in manifest file-
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:name=".MyApplication" . // declare the application class
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
Now you can access this anywhere in the app like
((MyApplication) getApplicationContext()).getGlobalData()
The behavior you are having is because if the app is in the background even if it has not been closed, Android OS can clear the initialized variable if not being used.

How to add analytics to an Android application with a library in its own process

I have an Android application which uses Google Analytics. As recommended by Google, it overrides android.app.Application and supplies the class name in its AndroidManifest.xml:
<application
android:name=".MyApplication">
My app relies on a library which contains a service which must run in its own process. Therefore, its AndroidManifest.xml has:
<application>
<service
android:name="org.myservice"
android:enabled="true"
android:exported="false"
android:process=":myservice">
When running, I found that a second, independent instance of MyApplication is launched in the library's process. This tries to set up analytics, etc. But the library never asked for this class and doesn't want it.
I tried to declare a different application class in the library's AndroidManifest.xml but this causes "Manifest merger failed" build errors.
How can I keep the library process from depending on MyApplication?
Because manifests are merged together, it is only possible to have a single class designated by android:name (in either one library or in the main app). One of these objects will be instantiated per process.
My solution is for the Main application class to detect whether the process name matches the APPLICATION_ID. This will only be true for the main process and not for any other.
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
// The print library has its own process but will use this class.
// Only set up analytics if we are in the main process.
if (isMainProcess()) {
// Once-per-app code here
}
}
public boolean isMainProcess() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
if (processInfo.pid == android.os.Process.myPid()) {
return TextUtils.equals(BuildConfig.APPLICATION_ID,
processInfo.processName);
}
}
return false;
}
}

Multiple application android:name tags in manifest

I am using two libraries(SUGAR ORM and Instabug). They both require me to use the android:name tag of the application in my manifest. However, it seems you cannot have duplicates.Is there a way around this?
My manifest:
<application
android:allowBackup="true"
android:label="#string/app_name"
android:icon="#drawable/runrouter_biggest"
android:theme="#style/MyTheme"
tools:replace="android:icon"
android:name="com.orm.SugarApp"
//I need to declare a second android:name here>
Thanks,
Matt
You seem to be out of luck. You can have only one instance of application class so you can specify only one android:name there.
However, you are actually not out of luck. Instabug does not require you to use their application class, all they need is to call Instanbug.initialize(...)... which you can easily do in your application class derived from SugarApp:
class MyApplication extends SugarApp
{
#Override
void onCreate()
{
super.onCreate();
Instabug.initialize(this)
.setAnnotationActivityClass(InstabugAnnotationActivity.class)
.setShowIntroDialog(true)
.setEnableOverflowMenuItem(true);
}
}
And define this application class in android:name.

Instrument Android App with startInstrumentation

I want to instrument my App with the Instrumentation Interface from inside the tested app.
I have a custom activity that every of my activities extend. In that I want to start the instrumentation to get information about the app:
public class BxActivity extends Activity {
#Override
protected void onResume() {
super.onResume();
...
boolean instru = startInstrumentation(new ComponentName(BxActivity.this, InstrumentationRobot.class), null, null);
Imho that should restart the app with the instrumentation code in the InstrumentationRobot.class. It is in the same Android project and package.
public class InstrumentationRobot extends Instrumentation {
#Override
public void onCreate(Bundle arguments) {
super.onCreate(arguments);
Log.v("InstrRobot", "Hello from Robot");
start();
}
I've added the instrumentation to the manifest like this:
<instrumentation
android:name="InstrumentationRobot" (class in the same pkg)
android:targetPackage="my.package.name" />
</application>
This is the correct manifest code for my instrumentation, so my little robot prints out "hello" to me.
Thanks, soeren
I've found the solution and updated my question with the correct code.
The mistake was in the manifest file.
I deleted the
<uses-library android:name="android.test.runner" />
tag and the instrumentation class name must not start with a dot, if in the instrumentation class is in the same package and .apk file as the app itself.
The instrumentation tag must also be a direct child of the manifest.

Categories

Resources