i am creating a plugin with the onAttachedToEngine function that i use to assign context and mediaPlayer session to variables, exactly like the docs say so nothing special is being done. The problem is that when my plugin throws an error and it's time for the onDestroy function to be called, the onDestroy function doesn't seem to see the variables i set during the usage of the current object.
so if i set a variable X to "hello" in the onAttachedToEngine, onDestroy will keep seeing it as null and creates nullPointer exceptions which crashes my plugin and app.
here is a sample code i am using :
public class flutterPlugin extends MediaBrowserService implements FlutterPlugin, MethodCallHandler {
private String randomString = "lowercase";
private void initInstance(BinaryMessenger binaryMessenger, Context context) {
this.channel = new MethodChannel(binaryMessenger, ID);
this.channel.setMethodCallHandler(this);
am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mContext=context;
mNotificationManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
setupBroadcastReceiver();
startSession(mContext);
this.randomStirng = "UPPERCASE";
}
#Override
public void onAttachedToEngine(#NonNull FlutterPluginBinding binding) {
this.initInstance(binding.getBinaryMessenger(), binding.getApplicationContext());
}
#Override
public void onDestroy() {
this.randomStirng // here the random string i initialized is equal to "lowercase"
// and the same goes for all the variables initialized in initInstance, they are alll treated an null
(...)
}
(...)
}
what could the issue be ? is onDestroy being called from a child process ? when getting the parent of super it returns the same class name as the plugin
Related
I am using the quickstart-android code provided by google but after many attempts I cam unable to find a context that is not returning null. The BarcodeScannerProcessor is not itself an Activity, so I have attempted to create an instance of the LivePreviewActivity and use that as the context in the intent, but it's null.
The goal is to once a valid barcode is recognized I want to open a new activity that allows a user to verify value and on the push of a button call a webservice to post the barcode to a database via API. I am having a hard time finding a valid context and the app is crashing when it trys to execute the Intent.
Starting at line 97-107:
https://github.com/jamiekeefer/quickstart-android/blob/master/mlkit/app/src/main/java/com/google/firebase/samples/apps/mlkit/java/barcodescanning/BarcodeScanningProcessor.java
for (int i = 0; i < barcodes.size(); ++i) {
FirebaseVisionBarcode barcode = barcodes.get(i);
BarcodeGraphic barcodeGraphic = new BarcodeGraphic(graphicOverlay, barcode);
graphicOverlay.add(barcodeGraphic);
System.out.println(barcode.getRawValue());
if (!barcode.getRawValue().equals("") ) {
System.out.println("Got the number:" + barcode.getRawValue() + " Context: " + mContext); //OLD SCHOOL DEBUG OUTPUT
//enter code to start activity
Intent intent = new Intent(mContext, SendScannedBarcode.class);
String message = scannedBarcode;
intent.putExtra(EXTRA_MESSAGE, message);
mContext.startActivity(intent);
}
}
You can back up in the repo to see the instance of the LivePreviewActivity where I trying to get context.
I have tried a number of things and read about Context, Views and Activities and basically have completely confused myself. The only tuts I can find are using Kotlin, which is not helping clarify things.
I appreacite any help in indentifying or contruting a valid Intent from this Context. Thank you.
So I am assuming that in your LivePreviewActivity you are creating an object of the class BarcodeScanningProcessor. What you can do is change the constructor in the BarcodeScanningProcessor class to accept a context and then you pass in your LivePreviewActivity's context.
This is what the code should look like:
In BarcodeScanningProcessor:
public BarcodeScanningProcessor(Context context) {
// Note that if you know which format of barcode your app is dealing with, detection will be
// faster to specify the supported barcode formats one by one, e.g.
// new FirebaseVisionBarcodeDetectorOptions.Builder()
// .setBarcodeFormats(FirebaseVisionBarcode.FORMAT_QR_CODE)
// .build();
detector = FirebaseVision.getInstance().getVisionBarcodeDetector();
this.mContext = context;
}
Then in LivePreviewActivity:
In the particular case of your activity you would do:
case BARCODE_DETECTION:
Log.i(TAG, "Using Barcode Detector Processor");
cameraSource.setMachineLearningFrameProcessor(new BarcodeScanningProcessor(getApplicationContext()));
break;
Or if you just wanted to create an object of the class you could do:
BarcodeScanningProcessor bsp = new BarcodeScanningProcessor(getApplicationContext());
This should now give your BarcodeScanningProcessor class the context of your activity. Now, in BarcodeScanningProcessor, mContext should not be null and will have the context of your activity. I hope this answers your question.
try this create Application class
import android.app.Application;
public class MyApplication extends Application {
static MyApplication instance;
#Override
public void onCreate() {
super.onCreate();
instance=this;
}
public static MyApplication getInstance() {
return instance;
}
}
Register in manifest file
<application
..
android:name="com.yourpackage.MyApplication"
..>
.
.
.
</application>
start activity using this MyApplication.
Intent intent = new Intent(MyApplication.getInstance(), SendScannedBarcode.class);
String message = scannedBarcode;
intent.putExtra(EXTRA_MESSAGE, message);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MyApplication. getInstance().startActivity(intent);
Another way of handling the issue is create new constructor of BarcodeScanningProcessor which takes interface call back and once processing is done pass back result to caller.
public interface BarcodeUpdateListener {
#UiThread
void onBarcodeDetected(Barcode barcode);
}
private BarcodeUpdateListener callback;
public BarcodeScanningProcessor(BarcodeUpdateListener callback){
this.callback = callback;
detector = FirebaseVision.getInstance().getVisionBarcodeDetector();
}
Once you get the result pass result to caller
callback.onBarcodeDetected(<Barcode>)
You can get the context from graphicOverlay:
Context context = graphicOverlay.getContext();
lately i have been researching about memory leaks in java/android and pretty much everywhere it says that instead of anonymous classes i should use static inner classes with weak references.
so, in my android app i started doing that but very quickly got tired of it because it's a lot of boilerplate code... i think have an alternative solution which i would prefer to use, but i'm juts not sure that it is a valid alternative to static inner classes in terms of preventing memory leaks. as i said before, i haven't seen this solution suggested anywhere else (all say to use static inner classes) so thats why im not sure my alternative will work.
ill use a simple example from my app:
i have a class called WebClient which handles asynchronous web requests and it accepts an interface called iCallback which returns the response from the server to the caller, and in my activity once i get this callback i need to dismiss a dialog, and maybe perform some activity related things (like trigger onBackPressed() and setResult()).
so here is my static inner class i have created:
private static class CallBack implements WebClient.ICallback
{
private WeakReference<ProgressDialog> mProgDiag;
private WeakReference<BaseActivity> mActivity;
public CallBack(BaseActivity activity, ProgressDialog progDiag)
{
this.mProgDiag = new WeakReference<>(progDiag);
this.mActivity = new WeakReference<>(activity);
}
#Override
public void onCallback(String data)
{
String responseAsString = Utils.extractStringFromResponse(...);
final BaseActivity parentActivity = mActivity.get();
ProgressDialog dialog = mProgDiag.get();
if(dialog != null)
{
dialog.dismiss();
}
if (responseAsString == null)
{
if(parentActivity != null)
{
Utils.makeServerErrorDialog(parentActivity,
new iDialogButtonClickedListener()
{
#Override
public void onDialogButtonClicked()
{
parentActivity.onBackPressed();
}
});
}
return;
}
//everything is ok
if (responseAsString.equals("1"))
{
if(parentActivity != null)
{
Intent result = new Intent();
result.putExtra(...);
parentActivity.setResult(Activity.RESULT_OK, result);
}
}
else
{
Utils.reportErrorToServer(...);
if(parentActivity != null)
{
parentActivity.setResult(Activity.RESULT_CANCELED);
}
}
if(parentActivity != null)
{
parentActivity.onBackPressed();
}
}
}
so for every variable i need in this static inner class i have to create a new weak reference, then retrieve the object itself, and then every time i want to access it i need to check whether it's null... that seems like a lot of code to me.
and here is my suggested alternative:
public abstract class BaseActivity extends AppCompatActivity
implements WebClient.ICallback
{
private static final String TAG = "BaseActivity";
WebClient.ICallback mCallBack;
ProgressDialog mProgDiag;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(...);
mCallBack = this;
//some code to invoke a server request on button click
//and passing mCallBack to the request
}
#Override
public void onCallback(String data)
{
String responseAsString = Utils.extractStringFromResponse(...);
mProgDiag.dismiss();
if (responseAsString == null)
{
Utils.makeServerErrorDialog(this,
new iDialogButtonClickedListener()
{
#Override
public void onDialogButtonClicked()
{
onBackPressed();
}
});
return;
}
//everything is ok
if (responseAsString.equals("1"))
{
Intent result = new Intent();
result.putExtra(...);
setResult(Activity.RESULT_OK, result);
}
else
{
Utils.reportErrorToServer(...);
setResult(Activity.RESULT_CANCELED);
}
onBackPressed();
}
#Override
protected void onPause()
{
mCallBack = null;
super.onPause();
}
#Override
protected void onResume()
{
super.onResume();
mCallBack = this;
}
}
to me this seems much cleaner: no creating and retrieving instances of weak references for every variable i need access to, i can directly invoke activity methods (e.g. onBackPressed()), and no checking for null everywhere.
the only place i would now have to check for null is inside WebClient class before invoking the callBack method.
so my question is, does this approach achieve the same result in terms of preventing memory leaks? is it a "worthy" alternative to static inner classes?
Unfortunately, your approach does not work. By implementing the WebClient.ICallback in your activity, rather than an inner class, you don't get rid of the leak. The leak happens not because the references to activity and dialog are implicit in an anonymous class, or in lambda, or in a non-static inner class instance; the happens when the WebClient keeps this reference while the activity is gone (it is not destroyed, because there is a strong reference to it).
The special mCallBack that you set to null when the activity is paused, gains nothing. Just as well, you can simply pass your activity instance to the WebClient. Now there is a strong reference to your activity, which is managed by someone (async handlers of the WebClient), who is not under your control. If you are unlucky, the async handler will get stuck somewhere and will never release this reference.
Please read this detailed explanation.
Note that WebView itself can cause a memory leak, if special measures are not undertaken!
I have the following code derived from Service which I want to run as a service, in the background. In this class, I use a SharedPreferences.OnSharedPreferenceChangeListener class to check if a shared preference has change, in order to reinitialize a BroadcastReceiver to be run at some defined time intervals (like every 6 hours). But when I change the explicit shared preference in my main activity to 3 hours, for example, I need the code to be run in 3 hours (instead of 6 hours). Here is the code for the Service:
public class AlarmService extends Service
{
public Alarm alarm = new Alarm();
public SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
public void onCreate()
{
super.onCreate();
SharedPreferences.OnSharedPreferenceChangeListener spChanged = new
SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
// reinitialize the alarm
alarm.SetAlarm(this); // ERROR HERE
}
};
//SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
sharedPref.registerOnSharedPreferenceChangeListener(spChanged);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.d("!~!", "Service started.");
alarm.SetAlarm(this);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
The method SetAlarm is defined in a class derived from BroadcastReceiver and is implemented as follows:
public void SetAlarm(Context context)
{
// get the update cylce from the prefences
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
int updateInterval = Integer.parseInt(sharedPref.getString("updateInterval", "24"));
Log.d(LOGHEAD, String.format("Alarm set to update every %d hours.", updateInterval));
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, Alarm.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000 * 60 * updateInterval, pi); // Millisec * Second * Minute
}
But when I compile the code, I get the following error:
Error:(33, 26) error: method SetAlarm in class Alarm cannot be applied to given types;
required: Context
found: <anonymous OnSharedPreferenceChangeListener>
reason: actual argument <anonymous OnSharedPreferenceChangeListener> cannot be converted to Context by method invocation conversion
The place at the error's occurrence is marked above. Once more I get an error related to the 'context', a concept, I am still failing to understand...
I am especially puzzled by the fact, that futher below on the code the call
alarm.SetAlarm(this);
is working fine. Maybe the issue is because of the use of an inner class?
Any ideas how to fix this? Or fix the whole code? I have put together the code from examples and I am not sure if this is good android code...
In that particular context this does refer to the OnSharedPreferenceChangeListener as shown in the error message.
Replace this with AlarmService.this to reference the outer service instance and things should work.
Based on you comment you also need to initialize the preferences inside of onCreate().
First only declare the preferences
private SharedPreferences sharedPref;
and then inside onCreate() do
sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
Inside a listener this refers to the OnSharedPreferenceChangeListener object which does not extend the Context class. You need to pass a valid Context object to your setAlarm method.
However when you call setAlarm in the onStartCommand, this represents your AlarmService object.
For further reference - read the Oracle documentation
I'm programming an app with that Nuance SpeechKit that performs TTS. It requires the context (called from getApplicationContext()) to be passed into some functions. Unfortunately, I'm getting this error in my log: ANDROID_CONTEXT parameter is not passed in!!!
Let me give more background: There in a main activity, and it opens a dialog from a button. The dialog invokes the text-to-speech functionality. As a result, I call getApplicationContext() in the main activity and pass it to my DialogFragment as a parameter using setters. Unfortunately, I'm getting this error even though I am calling the setters. So what could be going wrong? Here's a bit of code:
In my main activity:
// Instance variables...
private SpeechKit speechKit;
private Context context;
protected void onCreate(Bundle savedInstanceState) {
...
context = getApplicationContext();
...
this.speechKit = SpeechKit.initialize(context,
"CORRECT_API_KEY",
"sslsandbox.nmdp.nuancemobility.net",
443,
true,
SpeechKitAPIKey);
speechKit.connect();
}
public void invokeDialog() {
...
dialogueFragment.setContext(context);
dialogueFragment.setSpeechKit(speechKit);
...
}
And here's my code for the dialog fragment:
public void setSpeechKit(SpeechKit speechKit) {
this.speechKit = speechKit;
}
private SpeechKit speechKit;
private Context context;
public void setSpeechKit(SpeechKit speechKit) {
this.speechKit = speechKit;
}
public void setContext(Context context) {
this.context = context;
}
// Called when a button is pushed...
public void narrateText(String voice, String phrase) {
Vocalizer vocalizer = speechKit.createVocalizerWithVoice(voice, this, handler);
vocalizer.speakString(phrase, context);
}
Now I have no idea why this error is called. The code compiles fine. Any suggestions please?
I had the same issue, turned out to be that my free account expired (it lasts 90 days).
You probably send the login that is not valid anymore.
How can I play sound from a class that DOES NOT extend activity? I've been searching for a while and in every tutorial or answers from stackoverflow I've seen that sounds are always implemented in an activity class.
But in this case I have a class thas has the logic of my game, in which I have a gameUpdate() function; and in that function I want to make a specific sound play if something happens (for example a collision). How can I possibly access the activity that is currently running, from this class? Is there any way to do that?
Thanks.
If you need to get the current Activity instance or context you need to pass it to your other classes so that you can use it. For example:
class ABC extends Activity
{
public void onCreate(Bundle b)
{
XYZ xyz=new XYZ(this); // Sending the current Activity instance
}
}
class XYZ
{
ABC activity;
public XYZ(ABC activity)
{
this.activity = activity; //Now you can use it in this class
}
}
getActivity() or if is inside a fragment getFragment().getActivity()
Or alternativelly you can make add a Context to your class and get the activity reference from the constructor of the class.
Ex:
public class MyClass {
Context mContext();
public MyClass(Context context){
this.context = context;
}
}
and in your Activity class when you call MyClass:
MyClass myClass = new MyClass(this);
Inside your custom lass you can reference activity methods using its context.
So you actually just need a Context, not specifically an Activity (which is a Context). I would recommend that the class that should play sounds has a constructor which requires a Context. Keep a reference, not directly to the Context that you receive, but to the Application context using getApplicationContext() to get a Context that is safe to retain without the risk of memory leaks.
public class MySoundPlayingClass {
private final Context mContext;
public MySoundPlayingClass(Context ctx) {
// Since ctx could be an Activity, and this class
// could exist outside of the lifecycle of the Activity,
// grab the Application context to get a safe reference.
mContext = ctx.getApplicationContext();
}
}
Have a Util class and do something similar to below one. You can pass the context (it can be Activity instance) and the resource id to play it
Usage:
Util.play(context, R.raw.soundFile);
Sample Util class:
public class Util {
public static void play(final Context context, int resource) {
MediaPlayer mp = MediaPlayer.create(context, resource);
if (null != mp) {
mp.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
mp.start();
}
}
}