从 IntentHandler 传递什么上下文到需要它的方法?

What context to pass from an IntentHandler to a method that needs one?

我刚刚添加了一个新条件检查,该条件可能出现在使用 "borrowed" 代码显示消息的长时间运行的应用程序中。

语句在 IntentHandler for class DbQuery 里面,其中 extends IntentService:

showMessage(getApplicationContext(), "title", "note", mDatabase); // LINE 576 ****

showMessage 在 class Utilities 中定义并且一直有效,直到我决定看到它在我的 Android 4 平板电脑 (19) 上有效:

  public static void showMessage(Context mContext, String tit, String mm, SQLiteDatabase m)
  {
    TextView message = new TextView(mContext);
    TextView title   = new TextView(mContext);
    title.setText(tit);
    message.setText(mm);
    showCenteredInfoDialog(mContext, title, message, Gravity.CENTER, m); // **** LINE 505
  }

调用异常发生的方法(标有*****):

  public static void showCenteredInfoDialog
      (
              Context context, TextView message, TextView title, int 
              gravity, SQLiteDatabase m
      )
  {
    context.getSystemService(Context.LAYOUTINFLATERSERVICE);

    int YELLOWFOREGROUND ;
    int BRIGHTBLUEBACKGROUND ;

    {
      YELLOWFOREGROUND = context.getResources().getColor(R.color.yellowforeground, null);
      BRIGHTBLUEBACKGROUND =
          context.getResources().getColor(R.color.brightbluebackground, null);

      title.setTextColor(YELLOWFOREGROUND);
      title.setBackgroundColor(BRIGHTBLUEBACKGROUND);
      title.setGravity(Gravity.CENTER);

      AlertDialog.Builder
          builder = new AlertDialog.Builder(context);
          builder.setPositiveButton("OK", null);
          builder.setCustomTitle(title);
          builder.setMessage(message.getText());

      AlertDialog
          dialog;
          dialog = builder.show(); // *************************** LINE 482
          dialog.setCanceledOnTouchOutside(false);

      TextView
      messageView = /*(TextView)*/ dialog.findViewById(android.R.id.message);
      messageView.setTextColor(YELLOWFOREGROUND);
      messageView.setBackgroundColor(BRIGHTBLUEBACKGROUND);
      messageView.setTypeface(Typeface.MONOSPACE);
      messageView.setGravity(gravity);
    }
  }

MainActivity 调用 showMessage,其中传递的 Contextthis

我想我不应该尝试从我的 IntentHandler 调用 showMessage,但我想知道我应该如何调用它。 即,我应该传递什么 Context

对于 showMessage 的第一个参数,我尝试了 thisgetApplicationContext()getApplication()getApplication().getApplicationContext()getBaseContext() .都是return下面的错误。我什至提供了一个监听器而不是 null 作为 builder.setPositiveButton 的第二个参数。同样的错误:

E: FATAL EXCEPTION: IntentService[QueryDb]
    Process: com.dslomer64.sqhell, PID: 24814
    android.view.WindowManager$BadTokenException: Unable to add window --
                                     token null is not for an application
        at android.view.ViewRootImpl.setView(ViewRootImpl.java:571)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:310)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
        at android.app.Dialog.show(Dialog.java:319)
        at android.app.AlertDialog$Builder.show(AlertDialog.java:1112)
        at com.dslomer64.sqhell.Utilities.showCenteredInfoDialog(Utilities.java:482)
        at com.dslomer64.sqhell.Utilities.showMessage(Utilities.java:505)
        at com.dslomer64.sqhell.QueryDb.onHandleIntent(QueryDb.java:576)
        at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:66)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:148)
        at android.os.HandlerThread.run(HandlerThread.java:61)
E: getSlotFromBufferLocked: unknown buffer: 0xb40fe560

下面是部分调试跟踪的打印屏幕。

您应该为这个错误使用 activity 上下文,我希望能解决这个错误。

私有Context上下文

像这样在 onCreate 方法中初始化此上下文

上下文=这个; // 并使用这个上下文

showMessage(context, "title", "note", mDatabase);

并且还在单例中创建一个上下文类型变量class用作全局变量

一般来说,Android Service 可能是 运行 周围没有活动的时候。它只是 Android 中的另一个核心实体,如 ActivityBroadcastReceiver

如果您的 Android Service 不负责更新 UI 而是向您的 Activity 发送足够的数据以用于更新 UI。例如,您的 Android Service 可以使用 Broadcast 发送消息,而 Activity 可以通过 BroadcastReceiver.

收听这些消息

在您的 Service 中定义 BROADCAST_FILTER 以便 BroadcastReceiver 可以识别消息意图来自服务:

public static String BROADCAST_FILTER = <Your service class>.class.getName();

从您的 Service:

发送意向
//send a broadcast intent from your service
Intent intent = new Intent();
intent.setAction(BROADCAST_FILTER);
intent.putExtra("message", "<your message here>")
sendBroadcast(intent);

在您的 Activity:

中注册 BroadcastReceiver
private BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String message = intent.getStringExtra();
        Utils.showMessage(<your activity>.this, ...);
        //do your update here
    }
};

@Override
protected void onResume() {
    super.onResume();

    IntentFilter filter = new IntentFilter();
    //make sure your onReceive receives only the messages with this action
    filter.addAction(<Your Service>.BROADCAST_FILTER);
    registerReceiver(receiver, filter);
}

@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(receiver);
}

由于我的应用程序的性质,在问我的问题之前我已经准备好 BroadcastManagerBroadcastReceiver,所以我已经尝试了自己的弱尝试,类似于@user805311 的建议。

我说我不应该在 IntentService 中尝试 showMessage 基本上是正确的,尽管 @nishant 的 link 展示了如何做到这一点。

我对现有代码所做的更改如下:

IntentService for QueryDb 中的 publishProgress 中,我在顶部的 if 中添加了代码以传递消息(存储在 progress ) 通过另一个 Intent 而不是 showMessage,这导致异常,并立即开始通过 onPostExecute 关闭进程:

public class QueryDb extends IntentService
{
  ...
  private void publishProgress(String progress) 
  {
    Intent intent = new Intent(MatchesActivity.SOME_ACTION);

    if(progress.toLowerCase().startsWith("Revise"))
    {
      intent.putExtra(MatchesActivity.STRING_EXTRA_NAME, progress);
      onPostExecute();
    }
    else ...
    ...
    LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}

在执行过程中这样调用是没有结果的:

  String messageText = "Revise code!";
  publishProgress(messageText);

已发布的消息文本在 MatchesActivity 中这样解释,它已启动 IntentService 并开始侦听消息。

这是第一个地方showMessage终于成功了。

public class MatchesActivity extends Activity implements QueryDb.DbProcListenerForQueryDb
{
  private BroadcastReceiver mMessageReceiver  = new BroadcastReceiver()
  {
    @Override public void onReceive(Context context, Intent intent)
    {
      String s = intent.getStringExtra(STRING_EXTRA_NAME) ;
      if(s.startsWith("Revise"))
      {
         showMessage(_context, "Oh, no...", s, null);
         MatchesActivity.this.setResult(RESULT_CANCELED);
      }
      else ...
      ...
    };  // BroadcastReceiver

但是为了以可接受的 Context 继续我对 showMessage 的探索,我从上面的代码中删除了 showMessage 并在 onActivityResult 中添加了一个 MainActivity,它也在 onActivityResult 中工作,其中收到并处理了关于取消的 "bad news":

public class MainActivity extends Activity
{ 
  ...
  @Override protected void onActivityResult(int requestCode, int resultCode, Intent intentData)
  {
    if(resultCode != RESULT_OK)
    {
      showMessage(this, "Bad news", "Only Scrabble", mDatabase);
      return;
    }
    ...

长话短说,将 showMessageQueryDb 移动到 MatchesActivityMainActivity 消除了错误Context.