从另一个线程访问上下文

时间:2014-03-16 17:12:09

标签: java android multithreading

我开始将一些PC Java应用程序迁移到Android环境,这是一个关于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用户消息。

3 个答案:

答案 0 :(得分:1)

这取决于你真正需要什么,如果你打算显示简单的通知,也许你可以使用Android通知栏(这是显示它们的标准方式)而不是祝酒词。例如,您可以使用:

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

但是,如果你只是想要敬酒,你可以从服务中显示它们,你的问题是时间任务是在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);
    }

Source

最后,如果你想要在服务和活动之间进行充分的互动,你有几种方法:

  1. 使用粘合剂,对于简单的通信,这是您所需要的。
  2. 使用信使,进行更复杂的通信。
  3. 如果您只需要对话框,则始终可以在对话框模式下启动新活动。
  4. AIDL ...
  5. 关于1& 2 herehere

    1. 粘合剂: 它们允许您绑定应用程序中的不同对象,让它们直接访问对象本身及其功能,例如来自android doc:

      public class LocalService extends Service {         //给客户的Binder         私人决赛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;
              }
          };
      }
      
    2. 信使: 更高级的&复杂,通过这种方式,您可以将消息从一个对象发送到另一个对象:

      公共类MessengerService扩展Service {         / **命令服务显示消息* /         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;
                  }
              }
          }
      
    3. 如果您想将活动显示为花哨的对话框以显示更新,您可以使用此主题的常规活动:

      <activity android:theme="@android:style/Theme.Dialog" />
      

答案 1 :(得分:0)

任何与UI相关的代码都应该使用RunOnUiThread方法在UI线程上运行。

答案 2 :(得分:0)

你应该像这样设置一个全局上下文:

public static Activity currentActivity=null;

并在运行主活动或运行服务集上下文的任何活动之后:

MainActivity.currentActivity = this;
之后在吐司中使用此上下文:

 Toast.makeText(MainActivity.currentActivity," text", Toast.LENGTH_LONG);

希望使用完整