I am trying to use a singleton class to create this database because I need it to be open for each of the activities in the application. I keep getting a null pointer error from either the "getFilesDir" or "if(instance == null) calls, and since I am not that familiar with android programming I am not sure what is going on.
package com.database;
import java.io.File;
import android.app.Application;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
import com.businessclasses.Field;
import com.db4o.Db4oEmbedded;
import com.db4o.ObjectContainer;
import com.db4o.config.AndroidSupport;
import com.db4o.config.EmbeddedConfiguration;
public final class anotherTest extends Application {
private static ObjectContainer playsDB;
private static ObjectContainer gamePlanDB;
private anotherTest instance;
public anotherTest(){
final EmbeddedConfiguration config = Db4oEmbedded.newConfiguration();
config.common().add(new AndroidSupport());
//instance.getFilesDir().getPath()
new File(getFilesDir().getPath(), "/PlaysDB.db4o").delete();
Log.d("db", "" + instance.getFilesDir().getPath());
//getFilesDir().getPath();
//String appPath = "/data/data/PlaysDB/database";
playsDB = Db4oEmbedded.openFile(config, getFilesDir().getPath() + "/PlaysDB.db4o");
//File test = new File(appPath + "/PlaysDB.db4o");
//if(test != null){
// Log.d("db", "file was created");
// Log.d("db", "path >> " + test.getAbsolutePath());
//test.delete();
//}
//playsDB = Db4oEmbedded.openFile(config, appPath + "/PlaysDB.db4o");
}
public synchronized anotherTest getInstance(){
if(instance == null){
instance = new anotherTest();
}
return instance;
}
public void storePlay(Field field){
if(field != null){
if(playsDB == null){
Log.d("db", "database is null");
}
playsDB.store(field);
playsDB.commit();
Log.d("added play", "play added to db");
}
else{
Log.e("field input null", "play not added to db");
}
}
}
And here is my call in one of the activities.
public void onClick(View v) {
String formation = playFormation.getText().toString();
String name = playName.getText().toString();
String type = playType.getSelectedItem().toString();
//TODO:send to database
Field newField = new Field();
newField.setPlayName(name);
newField.setPlayType(type);
anotherTest temp = new anotherTest();
temp.getInstance().storePlay(newField);
}
Have been trying to make this database work for the past few weeks to no avail. Any help or guidance would be greatly appreciated.
The way this is structured doesn't make sense.
Java classes are always named with proper case - so it should be "AnotherTest".
You're not actually creating a singleton, because neither your instance, nor your getInstance() are static. Also, you never access your instance via "getInstance()".
You have a class AnotherTest, which has as a field an instance of AnotherTest, which is never, ever initialised, and which is accessed in the constructor of AnotherTest. This is just guaranteed to be null.
You are relying on implicit state of the Application superclass, which is never, ever provided because Application is supposed to be declared in the androidmanifest.xml, where it will be constructed for you. Meanwhile, you are constructing an instance manually in your event handler, so it has none of the state you expect - but anyway, it will fall over in its own constructor because it tries to reference a null pointer to itself, as mentioned above.
Your problems are nothing to do with DB40, and everything to do with not understanding what you're doing.
It needs to be way more like:
public final class AnotherTest {
private static AnotherTest instance;
private final Context context;
private AnotherTest(Context context){
this.context = context;
// Do whatever you need to do with DB4O using *only* the supplied context here
}
public static synchronized AnotherTest getInstance(Context context){
if(instance == null){
instance = new AnotherTest(context);
}
return instance;
}
}
And in the event handler:
AnotherTest.getInstance(Context.getApplicationContext()).storePlay(newField);
Related
I've installed Android Support Library but in the developer.android site says that for implementing it on my project I need to edit my build.gradle file that I don't have because it's an Unity project.
I've created a build.gradle file copying the content of this website: http://gradleplease.appspot.com/ and I put that file on the root of my Unity project but when I try to use the library it does not work
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
You need Java code to ask for permission, and you need an interface into said Java code from Unity's C# runtime. You need to create a Unity Plugin to do this.
Below is the plugin that I've created to grant the WRITE_EXTERNAL_STORAGE permission at runtime.
You need a project structure like this:
Plugins/
Android/
NoodlePermissionGranter/
project.properties
AndroidManifest.xml
NoodlePermissionGranter.cs
libs/
NoodlePermissionGranter.jar
NoodlePermissionGranter.cs:
///////////////////////////////////////////////////////////
///////////////// NoodlePermissionGranter /////////////////
/// Implements runtime granting of Android permissions. ///
/// This is necessary for Android M (6.0) and above. //////
///////////////////////////////////////////////////////////
//////////////////// Noodlecake Studios ///////////////////
///////////////////////////////////////////////////////////
using UnityEngine;
using System.Collections;
using System;
public class NoodlePermissionGranter : MonoBehaviour {
// subscribe to this callback to see if your permission was granted.
public static Action<bool> PermissionRequestCallback;
// for now, it only implements the external storage permission
public enum NoodleAndroidPermission
{
WRITE_EXTERNAL_STORAGE
}
public static void GrantPermission(NoodleAndroidPermission permission)
{
if (!initialized)
initialize ();
noodlePermissionGranterClass.CallStatic ("grantPermission", activity, (int)permission);
}
//////////////////////////////
/// Initialization Stuff /////
//////////////////////////////
// it's a singleton, but no one needs to know about it. hush hush. dont touch me.
private static NoodlePermissionGranter instance;
private static bool initialized = false;
public void Awake()
{
// instance is also set in initialize.
// having it here ensures this thing doesnt break
// if you added this component to the scene manually
instance = this;
DontDestroyOnLoad (this.gameObject);
// object name must match UnitySendMessage call in NoodlePermissionGranter.java
if (name != NOODLE_PERMISSION_GRANTER)
name = NOODLE_PERMISSION_GRANTER;
}
private static void initialize()
{
// runs once when you call GrantPermission
// add object to scene
if (instance == null) {
GameObject go = new GameObject();
// instance will also be set in awake, but having it here as well seems extra safe
instance = go.AddComponent<NoodlePermissionGranter>();
// object name must match UnitySendMessage call in NoodlePermissionGranter.java
go.name = NOODLE_PERMISSION_GRANTER;
}
// get the jni stuff. we need the activty class and the NoodlePermissionGranter class.
noodlePermissionGranterClass = new AndroidJavaClass("com.noodlecake.unityplugins.NoodlePermissionGranter");
AndroidJavaClass u3d = new AndroidJavaClass ("com.unity3d.player.UnityPlayer");
activity = u3d.GetStatic<AndroidJavaObject> ("currentActivity");
initialized = true;
}
///////////////////
//// JNI Stuff ////
///////////////////
static AndroidJavaClass noodlePermissionGranterClass;
static AndroidJavaObject activity;
private const string WRITE_EXTERNAL_STORAGE="WRITE_EXTERNAL_STORAGE";
private const string PERMISSION_GRANTED = "PERMISSION_GRANTED"; // must match NoodlePermissionGranter.java
private const string PERMISSION_DENIED = "PERMISSION_DENIED"; // must match NoodlePermissionGranter.java
private const string NOODLE_PERMISSION_GRANTER = "NoodlePermissionGranter"; // must match UnitySendMessage call in NoodlePermissionGranter.java
private void permissionRequestCallbackInternal(string message)
{
// were calling this method from the java side.
// the method name and gameobject must match NoodlePermissionGranter.java's UnitySendMessage
bool permissionGranted = (message == PERMISSION_GRANTED);
if (PermissionRequestCallback != null)
PermissionRequestCallback (permissionGranted);
}
}
NoodlePermissionGranter.java:
package com.noodlecake.unityplugins;
///////////////////////////////////////////////////////////
///////////////// NoodlePermissionGranter /////////////////
/// Implements runtime granting of Android permissions. ///
/// This is necessary for Android M (6.0) and above. //////
///////////////////////////////////////////////////////////
//////////////////// Noodlecake Studios ///////////////////
///////////////////////////////////////////////////////////
import android.Manifest;
import android.os.Build;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.util.Log;
import android.content.pm.PackageManager;
import java.io.File;
import com.unity3d.player.UnityPlayerActivity;
import com.unity3d.player.UnityPlayer;
public class NoodlePermissionGranter
{
// Only implements WRITE_EXTERNAL_STORAGE so far.
// Implement the rest by matching the enum in NoodlePermissionGranter.cs
// to the getPermissionStringFromEnumInt below.
private final static String UNITY_CALLBACK_GAMEOBJECT_NAME = "NoodlePermissionGranter";
private final static String UNITY_CALLBACK_METHOD_NAME = "permissionRequestCallbackInternal";
private final static String PERMISSION_GRANTED = "PERMISSION_GRANTED"; // this will be an arg to the above method
private final static String PERMISSION_DENIED = "PERMISSION_DENIED";
public static String getPermissionStringFromEnumInt(int permissionEnum) throws Exception
{
switch (permissionEnum)
{
case 0:
return Manifest.permission.WRITE_EXTERNAL_STORAGE;
// "and the rest is still unwritten" - Natasha Bedingfield
}
Log.e("NoodlePermissionGranter", "Error. Unknown permissionEnum " + permissionEnum);
throw new Exception(String.format("Error. Unknown permissionEnum %d",permissionEnum));
}
public static void grantPermission(Activity currentActivity, int permissionEnum)
{
// permission enum must match ordering in NoodlePermissionGranter.cs
final Activity act = currentActivity;
Log.i("NoodlePermissionGranter","grantPermission " + permissionEnum) ;
if (Build.VERSION.SDK_INT < 23) {
Log.i("NoodlePermissionGranter","Build.VERSION.SDK_INT < 23 (" + Build.VERSION.SDK_INT+")");
UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_GRANTED);
return;
}
try
{
final int PERMISSIONS_REQUEST_CODE = permissionEnum;
final String permissionFromEnumInt = getPermissionStringFromEnumInt(permissionEnum);
if (currentActivity.checkCallingOrSelfPermission(permissionFromEnumInt) == PackageManager.PERMISSION_GRANTED) {
Log.i("NoodlePermissionGranter", "already granted");
UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_GRANTED);
return;
}
final FragmentManager fragmentManager = currentActivity.getFragmentManager();
final Fragment request = new Fragment() {
#Override public void onStart()
{
super.onStart();
Log.i("NoodlePermissionGranter","fragment start");
String[] permissionsToRequest = new String [] {permissionFromEnumInt};
Log.i("NoodlePermissionGranter","fragment start " + permissionsToRequest[0]);
requestPermissions(permissionsToRequest, PERMISSIONS_REQUEST_CODE);
}
#Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{
Log.i("NoodlePermissionGranter", "onRequestPermissionsResult");
if (requestCode != PERMISSIONS_REQUEST_CODE)
return;
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
Log.i("NoodlePermissionGranter", PERMISSION_GRANTED);
UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_GRANTED);
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
Log.i("NoodlePermissionGranter",PERMISSION_DENIED);
UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_DENIED);
}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(this);
fragmentTransaction.commit();
// shouldBeOkayToStartTheApplicationNow();
}
};
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(0, request);
fragmentTransaction.commit();
}
catch(Exception error)
{
Log.w("[NoodlePermissionGranter]", String.format("Unable to request permission: %s", error.getMessage()));
UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_DENIED);
}
}
}
BuildNoodlePermissionGranter.sh
export JAVA_HOME=/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
ClASSPATH=$UNITY_ROOT"/Unity.app/Contents/PlaybackEngines/AndroidPlayer/Variations/mono/Release/Classes/classes.jar"
javac NoodlePermissionGranter.java -bootclasspath $ANDROID_SDK_ROOT/platforms/android-23/android.jar -classpath $ClASSPATH -d .
javap -s com.noodlecake.unityplugins.NoodlePermissionGranter
jar cvfM NoodlePermissionGranter.jar com/
rm -rf com
You need project.properties and a dummy AndroidManifest.xml in order to have Unity package a jar outside of Plugins/Android/libs
project.properties
target=android-9
android.library=true
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.noodlecake.unityplugins.noodlepermissiongranter"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:targetSdkVersion="23" />
</manifest>
It'd be nice if the PermissionRequestCallback supplied the requested permission enum as a parameter, but UnityPlayer.UnitySendMessage only supports a single string argument and I decided not to implemented the string serialization (using JSON to do this would be a good choice).
Another addition to Jason's excellent code for Unity 5.3.3 and up (I'm using 5.4), I added this to the manifest to block Unity from automatically asking at launch:
<application>
<meta-data android:name="unityplayer.SkipPermissionsDialog" android:value="true" />
</application>
In addition to Jason Knight's post (which I used for my own Unity plugin for handling runtime permissions):
I used Android studio for creating a plugin. I followed the directions on the following site and worked perfectly: http://www.thegamecontriver.com/2015/04/android-plugin-unity-android-studio.html
I also added another method using the shouldShowRequestPermissionRationale() function so I'm able to hide certain UI elements if the user denied the permission and checked the "Don't ask again" checkbox.
The other answers (especially Jason Knight's) have been super helpful to me, but I had to adjust the code to get it to work, so I'm sharing those changes here.
As noted in the comments, that code has this error in Android Studio: final Fragment request = new Fragment(); part saying "Fragments should be static such that they can be re-instantiated by the system, and anonymous classes are not static"
Now I'm not a Java expert so maybe I did things wrong, but I tried to adjust things as explained here: Fragments should be static such that they can be re-instantiated by the system, and anonymous classes are not static
Mostly, I split the Fragment into a new class, so that it's not an anonymous class. So now there are two Java files:
package com.synapse.unityplugins;
import android.Manifest;
import android.os.Build;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.util.Log;
import android.content.pm.PackageManager;
import com.unity3d.player.UnityPlayer;
public class PermissionGranter {
public final static String UNITY_CALLBACK_GAMEOBJECT_NAME = "SynapsePlugin_listener";
public final static String UNITY_CALLBACK_METHOD_NAME = "permissionRequestCallbackInternal";
public final static String PERMISSION_GRANTED = "PERMISSION_GRANTED";
public final static String PERMISSION_DENIED = "PERMISSION_DENIED";
// only implemented WRITE_EXTERNAL_STORAGE so far
public static String getPermissionStringFromEnumInt(int permissionEnum) throws Exception
{
switch (permissionEnum) {
case 0:
return Manifest.permission.WRITE_EXTERNAL_STORAGE;
// "and the rest is still unwritten" - Natasha Bedingfield
}
Log.e("PermissionGranter", "Error. Unknown permissionEnum " + permissionEnum);
throw new Exception(String.format("Error. Unknown permissionEnum %d",permissionEnum));
}
public static void grantPermission(int permissionEnum)
{
final Activity act = UnityPlayer.currentActivity;
Log.i("PermissionGranter","grantPermission " + permissionEnum) ;
if (Build.VERSION.SDK_INT < 23) {
Log.i("PermissionGranter","Build.VERSION.SDK_INT < 23 (" + Build.VERSION.SDK_INT+")");
UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_GRANTED);
return;
}
try {
final String permissionFromEnumInt = getPermissionStringFromEnumInt(permissionEnum);
if (act.checkCallingOrSelfPermission(permissionFromEnumInt) == PackageManager.PERMISSION_GRANTED) {
Log.i("PermissionGranter", "already granted");
UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_GRANTED);
return;
}
final Fragment request = PermissionRequestFragment.newInstance(permissionEnum);
final FragmentManager fragmentManager = act.getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(0, request);
fragmentTransaction.commit();
}
catch(Exception error)
{
Log.w("PermissionGranter", String.format("Unable to request permission: %s", error.getMessage()));
UnityPlayer.UnitySendMessage(UNITY_CALLBACK_GAMEOBJECT_NAME, UNITY_CALLBACK_METHOD_NAME, PERMISSION_DENIED);
}
}
}
That's the main plugin class, and here's the fragment:
package com.synapse.unityplugins;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import com.unity3d.player.UnityPlayer;
public class PermissionRequestFragment extends Fragment {
public static PermissionRequestFragment newInstance(int permissionEnum) {
PermissionRequestFragment frag = new PermissionRequestFragment();
Bundle args = new Bundle();
args.putInt("requested", permissionEnum);
frag.setArguments(args);
return frag;
}
#Override
public void onStart() {
super.onStart();
int permissionEnum = getArguments().getInt("requested");
final int PERMISSIONS_REQUEST_CODE = permissionEnum;
try {
final String permissionFromEnumInt = PermissionGranter.getPermissionStringFromEnumInt(permissionEnum);
String[] permissionsToRequest = new String[]{permissionFromEnumInt};
Log.i("PermissionGranter", "fragment start " + permissionsToRequest[0]);
requestPermissions(permissionsToRequest, PERMISSIONS_REQUEST_CODE);
} catch (Exception error) {
Log.w("PermissionGranter", String.format("Unable to request permission: %s", error.getMessage()));
UnityPlayer.UnitySendMessage(PermissionGranter.UNITY_CALLBACK_GAMEOBJECT_NAME,
PermissionGranter.UNITY_CALLBACK_METHOD_NAME, PermissionGranter.PERMISSION_DENIED);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
Log.i("PermissionGranter", "onRequestPermissionsResult");
int permissionEnum = getArguments().getInt("requested");
final int PERMISSIONS_REQUEST_CODE = permissionEnum;
if (requestCode != PERMISSIONS_REQUEST_CODE)
return;
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the task now
Log.i("PermissionGranter", PermissionGranter.PERMISSION_GRANTED);
UnityPlayer.UnitySendMessage(PermissionGranter.UNITY_CALLBACK_GAMEOBJECT_NAME,
PermissionGranter.UNITY_CALLBACK_METHOD_NAME, PermissionGranter.PERMISSION_GRANTED);
} else {
// permission denied, boo! Disable the functionality that needed it
Log.i("PermissionGranter", PermissionGranter.PERMISSION_DENIED);
UnityPlayer.UnitySendMessage(PermissionGranter.UNITY_CALLBACK_GAMEOBJECT_NAME,
PermissionGranter.UNITY_CALLBACK_METHOD_NAME, PermissionGranter.PERMISSION_DENIED);
}
final Activity act = UnityPlayer.currentActivity;
final FragmentManager fragmentManager = act.getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(this);
fragmentTransaction.commit();
}
}
And for completeness here's the C# within Unity:
using UnityEngine;
using System.Collections;
using System;
public class SynapsePlugin : MonoBehaviour {
// subscribe to this callback to see if your permission was granted.
public static Action<bool> PermissionRequestCallback;
// for now, it only implements the external storage permission
public enum AndroidPermission {
WRITE_EXTERNAL_STORAGE
}
public static void GrantPermission(AndroidPermission permission) {
if (!initialized)
initialize ();
PermissionGranterClass.CallStatic ("grantPermission", (int)permission);
}
//////////////////////////////
/// Initialization Stuff /////
//////////////////////////////
private const string PLUGIN_LISTENER_NAME = "SynapsePlugin_listener"; // must match UnitySendMessage call in Java
// it's a singleton, but no one needs to know about it. hush hush. dont touch me.
private static SynapsePlugin instance;
private static bool initialized = false;
static AndroidJavaClass PermissionGranterClass;
private const string PERMISSION_GRANTED = "PERMISSION_GRANTED"; // must match Java
private const string PERMISSION_DENIED = "PERMISSION_DENIED"; // must match Java
// runs automatically when making calls, or can pre-init manually
public static void initialize() {
// add object to scene
if (instance == null) {
GameObject go = new GameObject();
go.name = PLUGIN_LISTENER_NAME;
// instance will also be set in awake, but having it here as well seems extra safe
instance = go.AddComponent<SynapsePlugin>();
}
// get the jni stuff
new AndroidJavaClass("com.synapse.unityplugins.PermissionGranter");
initialized = true;
}
public void Awake() {
DontDestroyOnLoad (this.gameObject);
// instance is also set in initialize.
// having it here ensures this thing doesnt break
// if you added this component to the scene manually
instance = this;
if (name != PLUGIN_LISTENER_NAME)
name = PLUGIN_LISTENER_NAME;
}
// we're calling this method from the java side.
// the method name and gameobject must match Java's UnitySendMessage
private void permissionRequestCallbackInternal(string message) {
bool permissionGranted = (message == PERMISSION_GRANTED);
if (PermissionRequestCallback != null)
PermissionRequestCallback(permissionGranted);
}
}
I've used Jason knight's answer to create this plugin that does the job, the whole code is available on the github repo.
There is also unity package file for easy integration.
Following class I use to create a MediaPlayer object and. This works fine
import android.content.Context;
import android.media.MediaPlayer;
import android.view.View.OnClickListener;
public class PatientAlarm {
private static MediaPlayer md ;
private PatientAlarm() {
}
private PatientAlarm(Context context) {
md = MediaPlayer.create(context, R.raw.patient_alarm);
md.setLooping(true);
}
public static MediaPlayer getMediaPlayer(Context context) {
if (md == null) {
new PatientAlarm(context);
}
return md;
}
public static void startMediaPlayer(Context context) {
if (md == null) {
new PatientAlarm(context);
md.start();
} else {
if (!md.isPlaying()) {
new PatientAlarm(context);
md.start();
}
}
}
public static void stopMediaPlayer(OnClickListener onClickListener) {
if (md == null) {
} else {
if (md.isPlaying()) {
md.stop();
}
}
}
}
But the problem is Klocwork analyzer says
NPE.COND: Null pointer dereference of 'md' where null comes from condition : Java : PatientAlarm.java : Critical (1) : Analyze : Local
That occurs in below line
md.start();
I have no idea what it does mean. before start md, I check whether it is null.
Please guide me to make my code more reliable and how to solve this issue,
You totally messed up the concept of Object Oriented Programming.
You defined PatientAlarm class to incapsulate all logic of working with MediaPlayer. It's OK.
But why did you make all methods static? Why new PatientAlarm() called inside these methods and created instance even not assigned to some variable or field that makes it target for garbage collection?
This is example of how you should do it:
public class PatientAlarm {
private MediaPlayer md;
public PatientAlarm(Context context) {
md = MediaPlayer.create(context, R.raw.patient_alarm);
md.setLooping(true);
md.prepare();
}
public void start() {
if (!md.isPlaying()) {
md.start();
}
}
public void stop() {
if (md.isPlaying()) {
md.stop();
}
}
}
Adapt it to your case and read more about using MediaPlayer class.
which md.start(); line? there are 2... anyway your code is totally vulnerable for concurrency issues, you may read about locks to prevent it, but I would start form refactoring, before you get into advanced stuff. Creating a new object just to initialize a static member of the class is probably the most ugly approach for singleton I have ever seen.. If you really need to, use static method to do it for you, at least there wont be a new object for GC to collect. But in fact the use of static object in your example seems to be a miss first of all. Try rewriting it without static fields or methods and most likely your code will be simpler and more reliable.
In my application i have to share various java-beans class among the activities.
In order to do that, i extended the Application class, in which i create an HashMap filled with all the java-beans. Each java-beans has its own Key.
Example code:
public class MyApplication extends Application {
public static final String CLASSROOM_KEY = "Classroom";
private HashMap<String, Object> myObjects;
#Override
public void onCreate() {
myObjects = new HashMap<String, Object>();
myObjects.put(CLASSROOM_KEY, new Classroom());
}
public HashMap<String, Object> getMyObjects() {
return myObjects;
}
}
This is usable in all the activities, and this is ok. BUT, i have two problems:
1) I need to get myObjets also in non-activity classes, like utils classes, but in these classes i can't do "getApplicationContext()" because they don't extend Activity.
For example, from my main activity i start a service (but it is in a normal class), and the service calls a query that in turn is in another normal class.
The query needs an object that is in myObjects!
I can't make myObjects public static i think.
2) In MyApplication i have to create all my java-beans in advance.
What if in the future i wanted to create a new classroom object in addition to the already present one?
I should create a new key for it, but it is impossible!
Thanks for your help.
UDPATE
I change the question:
In this class:
public class ClassroomUtils {
private static String result = null;
private static String studentObjectID = null;
public static void queryClassroom(String UUID, final Classroom classroom) {
ParseQuery<ParseObject> query = ParseQuery.getQuery("Classroom");
query.whereEqualTo("BeaconUUID", UUID);
query.getFirstInBackground(new GetCallback<ParseObject>() {
public void done(ParseObject object, ParseException e) {
if (e == null) {
try {
result = object.getString("Label");
} catch(Exception e1){
System.out.println("Vuota");
}
if(result != null) {
Log.i("Classroom", "Retrieved " + result );
classroom.setClassroom(result);
sendNotification(result);
addStudentClassroomRelation(object);
}
} else {
Log.e("Classroom", "Error: " + e.getMessage());
}
}
});
}
i want to avoid to pass the classroom to this method (called from another normal class). How can i access to global objects from this class?
I can't make myObjects public static i think.
Why not? myObjects is effectively global in scope already. There is nothing to be gained, from a memory management standpoint, by having myObjects be a private data member of Application. If you want Classroom to be a Java singleton, do so. You just have to watch your memory management, as you do with your current implementation.
In MyApplication i have to create all my java-beans in advance
No, you do not.
What if in the future i wanted to create a new classroom object in addition to the already present one?
Then create another one. Perhaps the right singleton is a School, which holds onto a collection of Classroom objects. Again, your primary near-term issue is one of memory management, so you do not run out of memory because you are trying to keep these objects around all of the time.
1) I need to get myObjets also in non-activity classes, like utils classes, but in these classes i can't do "getApplicationContext()" because they don't extend Activity.
The best way, I think, is to create the MyApplication class as a singleton. There you can retrieve the data from anywhere by calling getInstance and the corresponding getter/setter for your attributes.
Short example:
public class MyApplication extends Application {
private static MyApplication mInstance;
public MyApplication getInstance(){
// this means you have only one existing instance of this class.
if(mInstance == null){
// set the context to this MyApplication instance
mInstance = this;
}
// return the instance of this class
return mInstance;
}
// here your stuff for MyApplication
public HashMap<String, Object> getMyObjects() {
return myObjects;
}
}
Then you can call it from another class like this:
public class CFoo{
public CFoo(){
//retrieve myObjects from MyApplication
MyApplication.getInstance().getMyObjects();
}
}
I'm working on an Android app and we're investigating memory use.
Looking at a heap dump from hprof, we're seeing nearly 2M (22% of our heap) being used in a static cache in JarURLConnectionImpl:
Looking at the source code for JarURLConnectionImpl, it appears that entries are added to the static jarCache variable, but never removed.
If it's true that they're never removed, that strikes me as a potential memory leak.
Is this a leak? Is there a fix or workaround?
Here's an ugly workaround:
private static HashMap<URL,JarFile> jarCache;
static {
try {
Class<?> jarURLConnectionImplClass = Class.forName("org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnectionImpl");
final Field jarCacheField = jarURLConnectionImplClass.getDeclaredField("jarCache");
jarCacheField.setAccessible(true);
//noinspection unchecked
jarCache = (HashMap<URL, JarFile>) jarCacheField.get(null);
} catch(Exception e) {
// ignored
}
}
Then, periodically run the following:
// HACK http://stackoverflow.com/questions/14610350/android-memory-leak-in-apache-harmonys-jarurlconnectionimpl
if( jarCache!=null ) {
try {
for (
final Iterator<Map.Entry<URL, JarFile>> iterator = jarCache.entrySet().iterator(); iterator.hasNext(); ) {
final Map.Entry<URL, JarFile> e = iterator.next();
final URL url = e.getKey();
if (Strings.toString(url).endsWith(".apk")) {
Log.i(TAG,"Removing static hashmap entry for " + url);
try {
final JarFile jarFile = e.getValue();
jarFile.close();
iterator.remove();
} catch( Exception f ) {
Log.e(TAG,"Error removing hashmap entry for "+ url,f);
}
}
}
} catch( Exception e ) {
// ignored
}
}
I run it on activity creation so it gets executed every time one of my activities is created. The ugly hashmap entry doesn't seem to get recreated all that often, but it DOES seem to reappear occasionally, so it's not sufficient to just run this code once.
This is definitely a nasty memory leak. I've opened an issue for it since no one else seems to have reported it.
Thanks for the "ugly workaround" emmby, that was helpful. A safer approach, although potentially with a performance impact, is to disable URLConnection caching altogether. Since the URLConnection.defaultUseCaches flag is static and, as you might guess, is the default for each instance's useCaches flag, you can just set this to false and no more instances will cache their connections. This will affect all implementations of URLConnection, so it may have farther-ranging effects than desired, but I think it's a reasonable trade-off.
You can just create a simple class like this and instantiate it very early in your app's onCreate():
public class URLConnectionNoCache extends URLConnection {
protected URLConnectionNoCache(URL url) {
super(url);
setDefaultUseCaches(false);
}
public void connect() throws IOException {
}
}
The interesting thing is that since this occurs after your app is loaded and run, the system libs should already be cached, and this will only prevent further caching, so this probably gives the best possible trade-off: not caching your apk while allowing the performance benefits of caching the system jars.
Before doing this I did modify emmby's solution a bit to make it a standalone class that creates a background thread to periodically clear the cache. And I restricted it to just clear the app's apk, though that can be relaxed if desired. The thing to worry about here is that you're modifying the objects while they may be in use, which is generally not a good thing. If you do want to go this route you just need to call the start() method with a context, e.g. in your app's onCreate().
package com.example;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import android.content.Context;
// hack to remove memory leak in JarURLConnectionImpl
// from http://stackoverflow.com/questions/14610350/android-memory-leak-in-apache-harmonys-jarurlconnectionimpl
public class JarURLMonitor {
private static JarURLMonitor instance;
private Pattern pat;
private Field jarCacheField;
public volatile boolean stop;
private static final long CHECK_INTERVAL = 60 * 1000;
public static synchronized void start(Context context) {
if (instance == null) {
instance = new JarURLMonitor(context);
}
}
public static synchronized void stop() {
if (instance != null) {
instance.stop = true;
}
}
private JarURLMonitor(Context context) {
// get jar cache field
try {
final Class<?> cls = Class.forName("libcore.net.url.JarURLConnectionImpl");
jarCacheField = cls.getDeclaredField("jarCache");
jarCacheField.setAccessible(true);
}
catch (Exception e) {
// log
}
if (jarCacheField != null) {
// create pattern that matches our package: e.g. /data/app/<pkgname>-1.apk
pat = Pattern.compile("^.*/" + context.getPackageName() + "-.*\\.apk$");
// start background thread to check it
new Thread("JarURLMonitor") {
#Override
public void run() {
try {
while (!stop) {
checkJarCache();
Thread.sleep(CHECK_INTERVAL);
}
}
catch (Exception e) {
// log
}
}
}.start();
}
}
private void checkJarCache() throws Exception {
#SuppressWarnings("unchecked")
final HashMap<URL, JarFile> jarCache = (HashMap<URL, JarFile>)jarCacheField.get(null);
final Iterator<Map.Entry<URL, JarFile>> iterator = jarCache.entrySet().iterator();
while (iterator.hasNext()) {
final Map.Entry<URL, JarFile> entry = iterator.next();
final JarFile jarFile = entry.getValue();
final String file = jarFile.getName();
if (pat.matcher(file).matches()) {
try {
jarFile.close();
iterator.remove();
}
catch (Exception e) {
// log
}
}
}
}
}
In an Android app, is there anything wrong with the following approach:
public class MyApp extends android.app.Application {
private static MyApp instance;
public MyApp() {
instance = this;
}
public static Context getContext() {
return instance;
}
}
and pass it everywhere (e.g. SQLiteOpenHelper) where context is required (and not leaking of course)?
There are a couple of potential problems with this approach, though in a lot of circumstances (such as your example) it will work well.
In particular you should be careful when dealing with anything that deals with the GUI that requires a Context. For example, if you pass the application Context into the LayoutInflater you will get an Exception. Generally speaking, your approach is excellent: it's good practice to use an Activity's Context within that Activity, and the Application Context when passing a context beyond the scope of an Activity to avoid memory leaks.
Also, as an alternative to your pattern you can use the shortcut of calling getApplicationContext() on a Context object (such as an Activity) to get the Application Context.
In my experience this approach shouldn't be necessary. If you need the context for anything you can usually get it via a call to View.getContext() and using the Context obtained there you can call Context.getApplicationContext() to get the Application context. If you are trying to get the Application context this from an Activity you can always call Activity.getApplication() which should be able to be passed as the Context needed for a call to SQLiteOpenHelper().
Overall there doesn't seem to be a problem with your approach for this situation, but when dealing with Context just make sure you are not leaking memory anywhere as described on the official Google Android Developers blog.
Some people have asked: how can the singleton return a null pointer?
I'm answering that question. (I cannot answer in a comment because I need to post code.)
It may return null in between two events: (1) the class is loaded, and (2) the object of this class is created. Here's an example:
class X {
static X xinstance;
static Y yinstance = Y.yinstance;
X() {xinstance=this;}
}
class Y {
static X xinstance = X.xinstance;
static Y yinstance;
Y() {yinstance=this;}
}
public class A {
public static void main(String[] p) {
X x = new X();
Y y = new Y();
System.out.println("x:"+X.xinstance+" y:"+Y.yinstance);
System.out.println("x:"+Y.xinstance+" y:"+X.yinstance);
}
}
Let's run the code:
$ javac A.java
$ java A
x:X#a63599 y:Y#9036e
x:null y:null
The second line shows that Y.xinstance and X.yinstance are null; they are null because the variables X.xinstance ans Y.yinstance were read when they were null.
Can this be fixed? Yes,
class X {
static Y y = Y.getInstance();
static X theinstance;
static X getInstance() {if(theinstance==null) {theinstance = new X();} return theinstance;}
}
class Y {
static X x = X.getInstance();
static Y theinstance;
static Y getInstance() {if(theinstance==null) {theinstance = new Y();} return theinstance;}
}
public class A {
public static void main(String[] p) {
System.out.println("x:"+X.getInstance()+" y:"+Y.getInstance());
System.out.println("x:"+Y.x+" y:"+X.y);
}
}
and this code shows no anomaly:
$ javac A.java
$ java A
x:X#1c059f6 y:Y#152506e
x:X#1c059f6 y:Y#152506e
BUT this is not an option for the Android Application object: the programmer does not control the time when it is created.
Once again: the difference between the first example and the second one is that the second example creates an instance if the static pointer is null. But a programmer cannot create the Android application object before the system decides to do it.
UPDATE
One more puzzling example where initialized static fields happen to be null.
Main.java:
enum MyEnum {
FIRST,SECOND;
private static String prefix="<", suffix=">";
String myName;
MyEnum() {
myName = makeMyName();
}
String makeMyName() {
return prefix + name() + suffix;
}
String getMyName() {
return myName;
}
}
public class Main {
public static void main(String args[]) {
System.out.println("first: "+MyEnum.FIRST+" second: "+MyEnum.SECOND);
System.out.println("first: "+MyEnum.FIRST.makeMyName()+" second: "+MyEnum.SECOND.makeMyName());
System.out.println("first: "+MyEnum.FIRST.getMyName()+" second: "+MyEnum.SECOND.getMyName());
}
}
And you get:
$ javac Main.java
$ java Main
first: FIRST second: SECOND
first: <FIRST> second: <SECOND>
first: nullFIRSTnull second: nullSECONDnull
Note that you cannot move the static variable declaration one line upper, the code will not compile.
Application Class:
import android.app.Application;
import android.content.Context;
public class MyApplication extends Application {
private static Context mContext;
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
public static Context getAppContext() {
return mContext;
}
}
Declare the Application in the AndroidManifest:
<application android:name=".MyApplication"
...
/>
Usage:
MyApplication.getAppContext()
You are trying to create a wrapper to get Application Context and there is a possibility that it might return "null" pointer.
As per my understanding, I guess its better approach to call- any of the 2
Context.getApplicationContext() or Activity.getApplication().
It is a good approach. I use it myself as well. I would only suggest to override onCreate to set the singleton instead of using a constructor.
And since you mentioned SQLiteOpenHelper: In onCreate () you can open the database as well.
Personally I think the documentation got it wrong in saying that There is normally no need to subclass Application. I think the opposite is true: You should always subclass Application.
I would use Application Context to get a System Service in the constructor. This eases testing & benefits from composition
public class MyActivity extends Activity {
private final NotificationManager notificationManager;
public MyActivity() {
this(MyApp.getContext().getSystemService(NOTIFICATION_SERVICE));
}
public MyActivity(NotificationManager notificationManager) {
this.notificationManager = notificationManager;
}
// onCreate etc
}
Test class would then use the overloaded constructor.
Android would use the default constructor.
I like it, but I would suggest a singleton instead:
package com.mobidrone;
import android.app.Application;
import android.content.Context;
public class ApplicationContext extends Application
{
private static ApplicationContext instance = null;
private ApplicationContext()
{
instance = this;
}
public static Context getInstance()
{
if (null == instance)
{
instance = new ApplicationContext();
}
return instance;
}
}
I'm using the same approach, I suggest to write the singleton a little better:
public static MyApp getInstance() {
if (instance == null) {
synchronized (MyApp.class) {
if (instance == null) {
instance = new MyApp ();
}
}
}
return instance;
}
but I'm not using everywhere, I use getContext() and getApplicationContext() where I can do it!
I know the original question was posted 13 years ago, and this is the Kotlin version of getting context everywhere.
class MyApplication : Application() {
companion object {
#JvmStatic
private var instance: MyApplication? = null
#JvmStatic
public final fun getContext(): Context? {
return instance
}
}
override fun onCreate() {
instance = this
super.onCreate()
}
}