2014-11-02 46 views
2

在准系统的Android应用程序中,我想通过另一个类的实例中的异步回调来调用MainActivity实例中的方法。为什么Java迫使我在类中调用静态方法,而不是实例本身中的非静态方法?为什么一个Android活动中的方法回调是静态的?

我的应用程序有一个MainActivity类和一个TextToSpeech(TTS)类。从主要活动中,我实例化这个类,传递一个指向MainActivity实例的指针。 TTS引擎的实例化是一种异步操作。直到它触发了一个onInit()方法,我才能与TTS实例交互。

下面是可用的代码。然而,我曾想象过我可以在MainActivity实例中调用一个非静态方法,而这看起来不可能。下面的代码使用对MainActivity类本身的静态调用,所以没有实例变量可用。

以下是我在Android Studio中对基本Hello World应用程序所做的更改。

//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﹕ [email protected] 

    tts.init(this); 
    } 

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

自定义类。

// 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﹕ [email protected] 

     // 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); 
    } 
} 

回答

3
@Override 
public void onInit(int status) { 
    if (status == TextToSpeech.SUCCESS) { 
    tts.setLanguage(Locale.UK); 
    Log.d("onInit", activity.toString()); 

    ((MainActivity)activity).ttsReady(); 
    } 
} 

,当你说你MainActivity.ttsReady()是不是指的MainActivity实例。这就是为什么你只能访问静态方法。您需要打电话给您存储在activity变量中的实例本身。

编辑: 对于基于你想完成什么完整的解决方案,我可能会设置它是这样的:

// TTSActivity.java 
public abstract class TTSActivity extends Activity implements TextToSpeech.OnInitListener { 

    private TextToSpeech tts; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     tts = new TextToSpeech(this, this); 
    } 

    @Override 
    public void onInit(int status) { 
     if (status == TextToSpeech.SUCCESS) { 
      tts.setLanguage(Locale.UK); 

      ttsReady(); 
     } 

    } 

    public void speakText(String toSpeak) { 
     int mode = android.speech.tts.TextToSpeech.QUEUE_FLUSH; 
     tts.speak(toSpeak, mode, null); 
    } 

    protected TextToSpeech getTts() { 
     return tts; 
    } 

    protected abstract void ttsReady(); 
} 

然后MainActivity变为:

// MainActivity.java 
public class MainActivity extends TTSActivity { 

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

    @Override 
    protected void ttsReady() { 
     speakText("Hello world"); 
    } 
} 

任何你想使用TTS的Activity,你所要做的只是扩展TTSActivity并实现ttsReady()

正如您所看到的,这更简洁明了,更易于理解。但是,您的具体实现当然取决于您在特定应用程序中的所有要求。

+0

有了这样'私人MainActivity活性的声明;'和像'公共无效初始化初始化签名( MainActivity currentActivity)','activity.ttsReady();'编译得很好。但对MainActivity的硬编码是不可行的。在我的完整应用程序中,我想创建从TTS实例到多个不同活动的回调。一个快速测试表明,解决方案是对每个需要回调ttsReady()的活动使用'TTSUser'接口。然后我在init方法的签名中使用TTSUser,然后在调用'getApplicationContext()'前调用'(Activity)'' – 2014-11-02 19:53:30

+0

没错。你原来的问题只是如何以非静态方式访问该方法。设计得更好,但你提出的解决方案听起来是正确的。你也可以让你的接口扩展'Activity',这样就不需要重新调用'getApplicationContext()',假设你只会从'Activity'使用它,当然不会有其他的东西,例如'Fragment '。 – 2014-11-02 20:04:24

+0

我已经发布了我的建议解决方案的实现作为此问题的答案。遵循你的建议,我尝试过使用'interface TTSUser extends Activity',但是这会导致编译错误:'错误:(5,27)错误:此处需要的接口。我误解了吗?我在[这里](https://community.oracle.com/thread/2080730)看到的答案编号3表示在这种情况下似乎不可用的技术。我有兴趣了解如何实现您的建议,以便我可以调用'getApplicationContext()'而不必重新回到'Activity'。 – 2014-11-02 21:18:22

0

@ zaventh的回答帮助我认识到,需要将Java实例转换为正确的类或接口,以便在继承层次结构中找到它的方法。我现在重写了我的准系统项目,以包含声明ttsReady()方法的接口。

这是允许我以通用方式访问MainActivity实例的方法的解决方案。

TTSUser接口

package com.example.callback; 

interface TTSUser { 
    void ttsReady(); 
} 

MainActivity

package com.example.callback; 

import android.app.Activity; 
import android.os.Bundle; 

// TTUser Interface ensures the existence of the ttsReady() method in every instance 
public class MainActivity extends Activity implements TTSUser { 

    private TTS tts; // non-static 

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

    public void onResume() { 
    super.onResume(); 
    tts = new TTS(); 
    tts.init(this); 
    } 

    public void ttsReady() { // non-static 
    tts.speakText("Hello world"); 
    } 
} 

TTS类

package com.example.callback; 

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

public class TTS implements TextToSpeech.OnInitListener { 

    private TextToSpeech tts; 
    private TTSUser activity; 

    // Use the TTSUser interface in the signature 
    public void init(TTSUser activity) { 
    this.activity = activity; 

    // Cast to generic Activity, to access .getApplicationContext() 
    Context context = ((Activity) activity).getApplicationContext(); 
    tts = new android.speech.tts.TextToSpeech(context, this); 
    } 

    @Override 
    public void onInit(int status) { 
    if (status == TextToSpeech.SUCCESS) { 
     tts.setLanguage(Locale.UK); 
     activity.ttsReady(); // accessible through the TTSUser interfaceg 
    } 
    } 

    // 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; 
    tts.speak(toSpeak, mode, null); 
    } 
} 
相关问题