作为 Android 平台的新手,我开始将一些 PC Java 应用程序迁移到 Android 环境。
当我尝试将服务 引用用作Toast 的上下文时,我发现了一个问题消息。
这是我的服务代码的相关部分:
public class ServicePFPE extends Service {
Timer messageSimulator;
TimerTask messagePoll;
private class MessageReceptionTask extends TimerTask
{
public MessageReceptionTask(Context c) {
context = c;
}
@Override
public void run() {
String shownText = "Message received!! on " + (new Date(System.currentTimeMillis())).toString();
//doToast(shownText, context); //THIS LINE MAKES THE APP CRASH!
System.out.println(shownText); //But I can do this
}
private Context context;
}
public ServicePFPE() {
super();
messageSimulator = new Timer();
messagePoll = new MessageReceptionTask(this);
}
@Override
public IBinder onBind(Intent intent)
{
doToast("Service: onBind");
return null;
}
...
...
...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
doToast("Service: onStartCommand");
messageSimulator.schedule(messagePoll, 5000, 5000);
return super.onStartCommand(intent, flags, startId);
}
...
...
...
private void doToast(String msg) { doToast(msg, this); }
private void doToast(String msg, Context con) {
Toast.makeText(con,msg,Toast.LENGTH_SHORT).show();
}
}
当计划任务运行到 doToast 调用时,Android 通知“不幸的是,myAPP 已停止”。
我认为这与我在不同线程中使用服务上下文有关,但我不确定。
您能否确认是否是这种情况?从服务运行计时器并能够使用其上下文的正确方法是什么?如果那不可能,我能否获取该线程的上下文以便生成 Toasts 用户消息。
最佳答案
这取决于您真正需要什么,如果您打算显示简单的通知,也许您可以使用 Android 通知栏(这是显示它们的标准方式)而不是 toasts。例如你可以使用:
/**
* Show a notification while this service is running.
*/
private void showNotification() {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.local_service_started);
NotificationManager mNM;
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.stat_sample, text,
System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, LocalServiceActivities.Controller.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.local_service_label),
text, contentIntent);
// Send the notification.
mNM.notify(NOTIFICATION, notification);
}
但是,如果您只想要 toast ,您可以从服务中显示它们,您的问题是 timertask 是在与 UI 线程(运行服务的地方)不同的线程中执行的。要将此代码“发布”到 UI 线程,您可以直接使用如下代码:
Handler handler;
@Override
public void onCreate() {
// Handler will get associated with the current thread,
// which is the main thread.
handler = new Handler();
super.onCreate();
}
private void runOnUiThread(Runnable runnable) {
handler.post(runnable);
}
最后,如果您想在服务和 Activity 之间进行完全交互,您有几种方法:
- 使用 Binder ,用于简单的通信,这更不是您所需要的。
- 使用 Messenger 进行更复杂的交流。
- 如果您只需要对话,您始终可以在对话模式下启动新 Activity 。
- AIDL...
粘合剂: 它们允许您在应用程序中绑定(bind)不同的对象,让它们直接访问对象本身及其功能,来自 android 文档的示例:
公共(public)类 LocalService 扩展服务 { //给客户端的绑定(bind)器 private final IBinder mBinder = new LocalBinder(); //随机数生成器 private final Random mGenerator = new Random();
/** * Class used for the client Binder. Because we know this service always * runs in the same process as its clients, we don't need to deal with IPC. */ public class LocalBinder extends Binder { LocalService getService() { // Return this instance of LocalService so clients can call public methods return LocalService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } /** method for clients */ public int getRandomNumber() { return mGenerator.nextInt(100); } } public class BindingActivity extends Activity { LocalService mService; boolean mBound = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); // Bind to LocalService Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // Unbind from the service if (mBound) { unbindService(mConnection); mBound = false; } } /** Called when a button is clicked (the button in the layout file attaches to * this method with the android:onClick attribute) */ public void onButtonClick(View v) { if (mBound) { // Call a method from the LocalService. // However, if this call were something that might hang, then this request should // occur in a separate thread to avoid slowing down the activity performance. int num = mService.getRandomNumber(); Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); } } /** Defines callbacks for service binding, passed to bindService() */ private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { // We've bound to LocalService, cast the IBinder and get LocalService instance LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) { mBound = false; } }; }
信使: 更高级和复杂,通过这种方式,您可以将消息从一个对象发送到另一个对象:
公共(public)类 MessengerService 扩展服务 { /** 命令服务显示消息 */ static final int MSG_SAY_HELLO = 1;
/** * Handler of incoming messages from clients. */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } } /** * Target we publish for clients to send messages to IncomingHandler. */ final Messenger mMessenger = new Messenger(new IncomingHandler()); /** * When binding to the service, we return an interface to our messenger * for sending messages to the service. */ @Override public IBinder onBind(Intent intent) { Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); return mMessenger.getBinder(); } } public class ActivityMessenger extends Activity { /** Messenger for communicating with the service. */ Messenger mService = null; /** Flag indicating whether we have called bind on the service. */ boolean mBound; /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the object we can use to // interact with the service. We are communicating with the // service using a Messenger, so here we get a client-side // representation of that from the raw IBinder object. mService = new Messenger(service); mBound = true; } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mService = null; mBound = false; } }; public void sayHello(View v) { if (!mBound) return; // Create and send a message to the service, using a supported 'what' value Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); // Bind to the service bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // Unbind from the service if (mBound) { unbindService(mConnection); mBound = false; } } }
如果您想将 Activity 显示为精美的对话框以显示更新,您可以使用具有此主题的常规 Activity :
<activity android:theme="@android:style/Theme.Dialog" />
关于java - 从另一个线程访问上下文,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22440064/