简体   繁体   English

Android计时器崩溃取消

[英]Android Timers Crashing On Cancel

I have a timer running that then goes off and does a few things and the timer starts fine. 我有一个计时器运行,然后关闭并做一些事情,计时器启动正常。

What I then want to do is click a button, then have that create an email. 我当时想要做的是点击一个按钮,然后创建一个电子邮件。 Everything is working, apart from when clicking the button when the timer is running, the application is crashing. 一切正常,除了在计时器运行时单击按钮时,应用程序崩溃了。 When trying to cancel the timer when hitting the button, that also crashes the application. 当按下按钮时尝试取消定时器时,也会使应用程序崩溃。

Any help would be appreciated. 任何帮助,将不胜感激。

Here's the code snippet: 这是代码片段:

public class myApplication extends Activity {
    StringBuilder str;
    Timer t;
    Button mailbutton;

    public void onCreate(Bundle savedInstanceState) {

        final StringBuilder str = new StringBuilder(1000);
        super.onCreate(savedInstanceState);


        setContentView(R.layout.main);

        addListenerOnButton();


        TimerTask task = new TimerTask() {

            @Override
            public void run() 
            {
            /// do stuff here
            }
        }

        t = new Timer();
        t.schedule(task,2000,2000);

    } 


    public void addListenerOnButton() {

        mailbutton = (Button) findViewById(R.id.emailbutton);

        mailbutton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {

                t.cancel();

                Intent i = new Intent(Intent.ACTION_SEND);
                i.setType("text/plain");
                i.putExtra(Intent.EXTRA_EMAIL  , new String[]{"email@domain.com"});
                i.putExtra(Intent.EXTRA_SUBJECT, "subject");
                i.putExtra(Intent.EXTRA_TEXT   , str.toString());
                try {
                    startActivity(Intent.createChooser(i, "Send mail"));
                } catch (android.content.ActivityNotFoundException ex) {
                }
            }
        });
    }
}

LogCat output: LogCat输出:

04-19 09:13:11.143: W/dalvikvm(12613): threadid=1: thread exiting with uncaught exception (group=0x40c421f8)
04-19 09:13:11.148: E/AndroidRuntime(12613): FATAL EXCEPTION: main
04-19 09:13:11.148: E/AndroidRuntime(12613): java.lang.NullPointerException
04-19 09:13:11.148: E/AndroidRuntime(12613):    at uk.co.application.applicationActivity$2.onClick(applicationActivity.java:94)
04-19 09:13:11.148: E/AndroidRuntime(12613):    at android.view.View.performClick(View.java:3591)
04-19 09:13:11.148: E/AndroidRuntime(12613):    at android.view.View$PerformClick.run(View.java:14263)
04-19 09:13:11.148: E/AndroidRuntime(12613):    at android.os.Handler.handleCallback(Handler.java:605)
04-19 09:13:11.148: E/AndroidRuntime(12613):    at android.os.Handler.dispatchMessage(Handler.java:92)
04-19 09:13:11.148: E/AndroidRuntime(12613):    at android.os.Looper.loop(Looper.java:137)
04-19 09:13:11.148: E/AndroidRuntime(12613):    at android.app.ActivityThread.main(ActivityThread.java:4507)
04-19 09:13:11.148: E/AndroidRuntime(12613):    at java.lang.reflect.Method.invokeNative(Native Method)
04-19 09:13:11.148: E/AndroidRuntime(12613):    at java.lang.reflect.Method.invoke(Method.java:511)
04-19 09:13:11.148: E/AndroidRuntime(12613):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
04-19 09:13:11.148: E/AndroidRuntime(12613):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
04-19 09:13:11.148: E/AndroidRuntime(12613):    at dalvik.system.NativeStart.main(Native Method)

What exactly is the purpose of this timer? 这个计时器究竟是什么目的? I can't imagine what you'd be using it for with it constantly repeating to run every 2 seconds. 我无法想象你会使用它,因为它不断重复每2秒运行一次。 One thing you could be running into is threading issues with Android. 您可能遇到的一件事是Android的线程问题。 That timer task runs on a different thread than the main thread used by the Android UI. 该计时器任务在与Android UI使用的主线程不同的线程上运行。 If you are touching android widgets or other instance variables then you'll end up with thread safety problems, and Android will often throw exceptions if you try to do that. 如果您正在触摸Android小部件或其他实例变量,那么您最终会遇到线程安全问题,如果您尝试这样做,Android通常会抛出异常。 So it comes down to what you are trying to accomplish with that thread as to what might work better than creating another thread like this. 所以它归结为你试图用那个线程完成什么可能比创建这样的另一个线程更好。 Typically we'd use a Handler to run a task on the main thread periodically rather than using a Timer like this because of the threading issues it introduces. 通常我们使用Handler来定期在主线程上运行任务,而不是像这样使用Timer,因为它引入了线程问题。

Update: 更新:

You just confirmed a serious thread safety issue in what you just described. 您刚刚在刚才描述的内容中确认了严重的线程安全问题。 What I mean by thread safety is you have the potential for two threads to read/write to the same memory location at the same time. 线程安全的意思是你有两个线程可以同时读/写同一个内存位置。 That is a big problem. 这是一个大问题。 In fact, by your own admission, your StringBuilder instance is shared between the timer thread and button callback which means you most certainly have a thread safety problem from reading that StringBuilder. 事实上,你自己承认,你的StringBuilder实例是在定时器线程和按钮回调之间共享的,这意味着你在读取StringBuilder时肯定会遇到线程安全问题。

You are going to have constant unpredictable problems doing that, and your code will never be stable when operated by users. 您将会遇到持续不可预测的问题,并且在用户操作时您的代码永远不会稳定。 First there is no reason to read GPS from another thread since the LocationManager will call you periodically as it changes. 首先,没有理由从另一个线程读取GPS,因为LocationManager会随着它的变化定期给你打电话。 Just simply append that onto the a StringBuilder or ArrayList when it's called. 只需在调用时将其附加到StringBuilder或ArrayList上即可。 The GPS callback runs on the main thread so there is no chance for the button logic that reads that variable to occur at the same time as modifying that variable. GPS回调在主线程上运行,因此读取该变量的按钮逻辑无法在修改该变量的同时发生。

You need to remove that timer from your code, and use the main thread to do all of your work. 您需要从代码中删除该计时器,并使用主线程完成所有工作。 Or you'll have to synchronize access to your data structures. 或者您必须同步对数据结构的访问。 Most of the callbacks that occur in Android run on the main thread. Android中发生的大多数回调都在主线程上运行。 So if you'll just move the logic of recording the historical values into a data structure(s) there, instead of in your timer, you'll be fine. 因此,如果您只是将记录历史值的逻辑移动到那里的数据结构中,而不是在计时器中,那么你会没事的。 You might have to record GPS separately from whatever else your are trying to record, but you can then format them into your String in the button click handler. 您可能必须单独记录您尝试记录的其他任何内容,但您可以在按钮单击处理程序中将它们格式化为您的字符串。

Your NPE is on line 94 of your file. 您的NPE位于文件的第94行。 Without knowing line numbers to source code we can't help you. 如果不知道源代码的行号,我们无法帮助您。 But these are the types of problems you'll encounter when you have Thread safety issues. 但这些是您遇到线程安全问题时遇到的问题类型。 And they'll drive you crazy so it makes sense to head my warning, and learn more about threading and controlling it safely. 它们会让你发疯,因此有必要发出警告,并更多地了解线程并安全地控制它。

   mailbutton = (Button) findViewById(R.id.emailbutton);

        mailbutton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {

                t.cancel();

                Intent i = new Intent(Intent.ACTION_SEND);
                i.setType("text/plain");
                i.putExtra(Intent.EXTRA_EMAIL  , new String[]{"email@domain.com"});
                i.putExtra(Intent.EXTRA_SUBJECT, "subject");
                i.putExtra(Intent.EXTRA_TEXT   , str.toString());
                try {
                    startActivity(Intent.createChooser(i, "Send mail"));
                } catch (android.content.ActivityNotFoundException ex) {
                }
            }
        });

the above line must be in oncreate() because you have a view(R.layout.main) set in oncreate and you function addListenerOnButton() doesn't know about main layout. 上面的行必须在oncreate()中,因为你在oncreate中设置了一个视图(R.layout.main),你的函数addListenerOnButton()不知道主要的布局。

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

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