Sunday, November 2, 2014

Android : Why must a callback to a method in an Android activity be static?



In a barebones Android application, I want to call a method in the MainActivity instance, from an asynchronous callback in the instance of another class. Why does Java force me to call a static method in the class, rather than a non-static method in the instance itself?


My app has a MainActivity class and a TextToSpeech (TTS) class. From the main activity, I instantiate the class, passing a pointer to the MainActivity instance. Instantiation of the TTS engine is an asynchronous operation. I cannot interact with the TTS instance until it has triggered an onInit() method.


Below is code that works. However, I had imagined that I would be able to call a non-static method in the MainActivity instance, and this appears not to be possible. The code below uses a static call to the MainActivity class itself, so no instance variables are available.


Here are the changes I made to a basic Hello World application in Android Studio.



//MainActivity.java
package com.example.callback;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;


public class MainActivity extends ActionBarActivity {

private static TTS tts; // apparently this has to be static

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void onResume() {
super.onResume();
tts = new TTS();
Log.d("onResume", this.toString());
// D/Main﹕ com.example.callback.MainActivity@4a014e50

tts.init(this);
}

// Apparently this method has to be static
public static void ttsReady() {
tts.speakText("Hello world");
}
}


Custom class.



// TTS.java
package com.example.callback;

import android.app.Activity;
import android.content.Context;
import android.speech.tts.TextToSpeech;
import android.util.Log;

import java.util.HashMap;
import java.util.Locale;

public class TTS implements TextToSpeech.OnInitListener {

private TextToSpeech tts;
private Activity activity;

public void init(Activity currentActivity) {
activity = currentActivity;
Context context = activity.getApplicationContext();
tts = new android.speech.tts.TextToSpeech(context, this);
}

@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
tts.setLanguage(Locale.UK);
Log.d("onInit", activity.toString());
// D/onInit﹕ com.example.callback.MainActivity@4a014e50

// activity.ttsReady();
// The line above does not compile. ttsReady() is not recognized as a
// method of activity, regardless of whether the tts variable and the
// ttsReady() method in the MainActivity class are made static or not,
// Making the activity variable static here has no effect either.

// The commented line below throws Error:(31, 35)
// error: non-static method toString() cannot be referenced from a static context
// So presumably MainActivity is a static variable.
// Log.d("onInit", MainActivity.toString())

// This works, if the tts variable and the ttsReady() method in the
// MainActivity class are made static. Is there a non-static alternative?
MainActivity.ttsReady();
}
}

// Deprecated signature for speak() used for compatibility with API 20
// and earlier
public void speakText(String toSpeak) {
int mode = android.speech.tts.TextToSpeech.QUEUE_FLUSH;
// Object hashMap = null; // Causes a "no suitable method error".
// How is HashMap null not the same as Object null? Using just plain null
// instead of hashMap also works with no problems.
HashMap hashMap = null;
tts.speak(toSpeak, mode, hashMap);
}
}

No comments:

Post a Comment