简体   繁体   English

从另一个线程访问上下文

[英]Accessing context from another thread

I am starting to migrate some PC Java applications to Android environments being a complete newbie concerning Android platform. 我开始将一些PC Java应用程序迁移到Android环境,这是有关Android平台的完整的新手。

I found a problem when I tried to use a Service reference as context for a Toast message. 当我尝试将服务引用用作Toast消息的上下文时,发现一个问题。

This is the relevant part of my Service code: 这是我的服务代码的相关部分:

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

When the scheduled task runs reaching doToast call Android notifies that "Unfortunatelly, myAPP has stopped". 当计划的任务运行时,到达doToast调用的Android会通知“不幸的是,myAPP已停止”。

I think it has something to do with the fact I am using the service context in a different thread but I don't know for sure. 我认为这与我在其他线程中使用服务上下文有关,但我不确定。

Could you confirm if that is the case? 你能确定是这样吗? What is the right way to run a timer from a service and be able to use its context? 从服务运行计时器并能够使用其上下文的正确方法是什么? If that is not possible, can I get a context for that thread so I can generate Toasts user messages. 如果那不可能,那么我可以获取该线程的上下文,以便生成Toasts用户消息。

It depends on what you really need, if you are planning to show simple notifications, maybe instead of toasts you can use Android notification bar (which is the standard way to show them). 这取决于您的实际需求,如果您打算显示简单的通知,则可以使用Android通知栏(这是显示通知的标准方式)来代替敬酒。 For example you can use: 例如,您可以使用:

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

however, if you just want toasts, you can show them from the service, your problem is that the timertask is being executed in a different thread that the UI thread (where the service is running). 但是,如果您只是想敬酒,则可以从服务中显示它们,您的问题是,timertask是在与UI线程(运行服务的位置)不同的线程中执行的。 to "post" this code to the UI thread you can do it directly with something like this: 将此代码“发布”到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 资源

And finally if you want fully interaction between service and activities, you have several ways: 最后,如果要在服务和活动之间进行完全交互,则可以采用以下几种方法:

  1. Use binders, for simple communications, this is moreless what you need. 使用活页夹,进行简单的通信,这正是您所需要的。
  2. Use a messenger, to more complicated communications. 使用Messenger进行更复杂的通信。
  3. If you only need dialogs you are always able to launch new activities in dialog mode. 如果只需要对话框,则始终可以在对话框模式下启动新活动。
  4. AIDL... AIDL ...

Documentation about 1 & 2 here and here 此处此处有关1&2的文档

  1. Binders: They let you bind different objects in your application letting them access directly to the object itself and its functions, example from android doc: 绑定器:它们使您可以绑定应用程序中的不同对象,从而使它们直接访问对象本身及其功能,例如android doc中的示例:

    public class LocalService extends Service { // Binder given to clients private final IBinder mBinder = new LocalBinder(); 公共类LocalService扩展Service {//向客户提供的活页夹私有的最终IBinder mBinder = new LocalBinder(); // Random number generator private final Random mGenerator = new Random(); //随机数生成器私有最终的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. Messenger: More advanced & complicated, in this way you can send messages from one object to another: Messenger:更高级,更复杂,通过这种方式,您可以将消息从一个对象发送到另一个对象:

    public class MessengerService extends Service { /** Command to the service to display a message */ static final int MSG_SAY_HELLO = 1; 公共类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; } } } 

In case you want to show activities as fancy dialogs to show the updates you can use a regular activity with this theme: 如果您想将活动显示为精美的对话框以显示更新,则可以对此主题使用常规活动:

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

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

you should set a global context like this: 您应该像这样设置全局上下文:

public static Activity currentActivity=null;

and after run your main activity or any activity that runs service set context like this: 在运行您的主要活动或任何运行服务集上下文的活动后,如下所示:

MainActivity.currentActivity = this;

after that in toast use this context: 之后,在敬酒中使用以下上下文:

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

hope use full 希望用尽

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM