I just added a check for a new condition that can arise in a long-working app that uses "borrowed" code to display a message.
The statement is inside the IntentHandler
for class DbQuery
, which extends IntentService
:
showMessage(getApplicationContext(), "title", "note", mDatabase); // LINE 576 ****
showMessage
is defined in class Utilities
and had always worked, until I decided to see that it works on my Android 4 tablet (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
}
It calls the method where the exception occurs (marked with *****):
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);
}
}
Calls to showMessage
work from MainActivity
, where the Context
passed is this
.
I suppose I just shouldn't try to call showMessage
from my IntentHandler
, BUT I'd like to know how I SHOULD call it.
I.e., what Context
should I pass?
For the first argument to showMessage
, I've tried this
, getApplicationContext()
, getApplication()
, getApplication().getApplicationContext()
, and getBaseContext()
. All return the following error. I even supplied a listener instead of null
as the 2nd argument to builder.setPositiveButton. Same error:
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
There follows a printscreen of part of a debug trace.
Generally speaking, Android Service
may be running when there're no activities around. It's just another core entity in Android like Activity
or BroadcastReceiver
.
It's cleaner, if your Android Service
doesn't take the responsibility for updating the UI but rather sends enough data to your Activity
that can use to update the UI. For instance, your Android Service
could use Broadcast
to send messages around and the Activity
could listen to those messages via BroadcastReceiver
.
Define BROADCAST_FILTER
in your Service
so that BroadcastReceiver
could identify that a message intent comes from the service:
public static String BROADCAST_FILTER = <Your service class>.class.getName();
Send an intent form your Service
:
//send a broadcast intent from your service
Intent intent = new Intent();
intent.setAction(BROADCAST_FILTER);
intent.putExtra("message", "<your message here>")
sendBroadcast(intent);
Register BroadcastReceiver
inside your Activity
:
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);
}