Handler.post解惑

作者: Anderson大码渣 | 来源:发表于2016-11-19 21:56 被阅读888次
    android美队.png

    今日修改bug拿到一份代码,其中网络请求方面由于功能很简单,就没有使用网络请求框架,ok,那按照我的想法,开启一个线程拿个数据,拿完发送handler更新UI即可了,但是代码中并不是这么写的,而是通过mNetHandler.post一个包含网络请求的runnable。mNetHandler的来源是这样的。

            HandlerThread handlerThread = new HandlerThread("NET");
            handlerThread.start();
            mNetHandler = new Handler(handlerThread.getLooper());
    

    而当我询问原作者原因时,解释是用mNetHandler来管理这些Runnable,在view结束时,

    mNetHandler.removeCallback

    移除这些runnable,解决掉在view,或者说activity中开启线程,而当view或者activity结束时 线程仍然存活的问题。

    但是目前存在一个问题,removeCallback并不是立即停止该线程,而是移除掉还未执行的callback,正在执行的是无法立即结束的。此问题我写了demo,证实的确是正在运行的程序无论通过
    mNetHandler.getLooper().quit();
    handlerThread.quitSafely();
    都无法停止该线程,我们来看一下我的demo
    功能很简单,activity1点击button进activity2,activity2一进去就开启线程干事情,然后点击finsh按钮,结束当前activity,回到activity1,无论哪种模式,activity开的那个线程都能存活。

    • Activity 1的代码 没什么东西 就是跳转
    public class MainActivity extends AppCompatActivity {
    
        private Button button;
        private TextView textView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = (TextView) findViewById(R.id.my_text);
            button = (Button) findViewById(R.id.button);
            textView.setText("第一个界面");
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Log.i("iii"," 跳转去activity2");
                    Intent intent = new Intent(MainActivity.this,SecondActivity.class);
                    startActivity(intent);
                }
            });
        }
    }
    
    • Activity2的代码
    /**
     * Created by xiamin on 11/19/16.
     */
    public class SecondActivity extends AppCompatActivity {
        private Button button;
        private TextView textView;
        private Handler mNetHandler;
        HandlerThread handlerThread;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Log.i("iii","进入activity2");
            textView = (TextView) findViewById(R.id.my_text);
            button = (Button) findViewById(R.id.button);
            textView.setText("第2个界面");
            button.setText("finish()");
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    finish();
                }
            });
    
            handlerThread = new HandlerThread("NET");
            handlerThread.start();
            mNetHandler = new Handler(handlerThread.getLooper());
            mNetHandler.post(mRunnable);
        }
    
        private static int count = 0;
        private Runnable mRunnable = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    Log.i("iii"," count = " + count++);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    
        @Override
        protected void onDestroy() {
            Log.i("iii","activity2 onDestroy() ");
            mNetHandler.removeCallbacksAndMessages(null);
            mNetHandler.getLooper().quit();
            handlerThread.quitSafely();
            super.onDestroy();
        }
    }
    

    打印为,我们可以见到runnable里在继续打印

    11-19 17:05:15.930 6280-6280/? I/iii: 跳转去activity2
    11-19 17:05:15.960 6280-6280/? I/iii: 进入activity2
    11-19 17:05:15.960 6280-6321/? I/iii: count = 0
    11-19 17:05:16.960 6280-6321/? I/iii: count = 1
    11-19 17:05:17.960 6280-6321/? I/iii: count = 2
    11-19 17:05:18.960 6280-6321/? I/iii: count = 3
    11-19 17:05:19.250 6280-6280/? I/iii: activity2 onDestroy()
    11-19 17:05:19.960 6280-6321/? I/iii: count = 4
    11-19 17:05:20.960 6280-6321/? I/iii: count = 5
    11-19 17:05:21.960 6280-6321/? I/iii: count = 6
    11-19 17:05:22.960 6280-6321/? I/iii: count = 7

    可见,使用handlerThread只充当了一个线程的单行执行器的作用,并没有能够起到所谓的控制runnable执行的作用。事实上,什么都不能控制runnable执行的。

    • 这个事情提醒我们几点:

    1.activity结束后线程还能存活,所以我们要记得结束它或者知道要管理好他们

    2.使用handlerThread.post runnable 只是让handlerThread充当了线程的顺序执行器

    • handler回顾

    Android程序员都知道不能在UI线程执行耗时的操作,Android引入handler就是为了解决这个问题,当然实现异步更新UI不仅仅只有这一种方法,还有AsyncTask也可以实现。

    Android有一个 Handler类,使用该类可以对运行在不同线程中的多个任务进行排队,并使用Message和Runnable对象安排这些任务。在javadoc中,对Handler是这样解释的:Handler可以发送和处理消息对象或Runnable对象,这些消息对象和Runnable对象与一个线程相关联。每个Handler的实例都关联了一个线程和线程的消息队列。当创建了一个Handler对象时,一个线程或消息队列同时也被创建,该Handler对象将发送和处理这些消息或Runnable对象。

    a、如果new一个无参构造函数的Handler对象,那么这个Handler将自动与当前运行线程相关联,也就是说这个Handler将与当前运行的线程使用同一个消息队列,并且可以处理该队列中的消息。
    我做过这样一个实验,在主用户界面中创建一个带有无参构造函数的Handler对象,该Handler对象向消息队列推送一个Runnable对象,在Runnable对象的run函数中打印当前线程Id,主用户界面线程ID和Runnable线程ID均为1。

    b、如果new一个带参构造函数的Handler对象,那么这个Handler对象将与参数所表示的Looper相关联。注意:此时线程类应该是一个特殊类HandlerThread类,一个Looper类的Thread类,它继承自Thread类。

    c、如果需要Handler对象去处理消息,那么就要重载Handler类的handleMessage函数。


    谢谢大家阅读,如有帮助,来个喜欢或者关注吧!


    本文作者:Anderson/Jerey_Jobs

    简书地址:[Anderson大码渣][1]

    github地址:[Jerey_Jobs][2]
    [1]: http://www.jianshu.com/users/016a5ba708a0/latest_articles
    [2]: https://github.com/Jerey-Jobs

    相关文章

      网友评论

        本文标题:Handler.post解惑

        本文链接:https://www.haomeiwen.com/subject/wptkpttx.html