java - 从另一个线程访问上下文

标签 java android multithreading

作为 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);
    }

Source

最后,如果您想在服务和 Activity 之间进行完全交互,您有几种方法:

  1. 使用 Binder ,用于简单的通信,这更不是您所需要的。
  2. 使用 Messenger 进行更复杂的交流。
  3. 如果您只需要对话,您始终可以在对话模式下启动新 Activity 。
  4. AIDL...

关于 1 和 2 的文档 herehere

  1. 粘合剂: 它们允许您在应用程序中绑定(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;
            }
        };
    }
    
  2. 信使: 更高级和复杂,通过这种方式,您可以将消息从一个对象发送到另一个对象:

    公共(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/

相关文章:

java - 我如何断言一个List恰好包含一个特定类的一个实例?

java - 将日期字符串解析为某个 Java 对象

android - Arduino Mega ADK 初学者教程

c++ - 线程同步 : Wait on two bool variables

ios - 细胞使用随机图像

java - 弗林克 : how to write null values as empty through writeAsCsv in sink

java - Facebook 在重新安装后登录时给出无效 token Facebook 异常

android - 使用 React Native WebView 访问 localhost:9000/login 时出现 net::ERR_CONNECTION_REFUSED

java - 我收到 The method onListItemClick(ListView, View, int, long) is undefined for the type ActionBarActivity 错误

Python - 从多个线程附加到同一个文件