暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Android快速切换到主线程更新UI的几种方法

WTech 2018-04-05
913

Android快速从子线程切换到主线程更新UI的方法:

1、在子线程中,用view自带的方法view.post(Runnable action)

2、在子线程中,用activity.runOnUiThread(Runnable action)

3、使用Handler

4、使用AsyncTask

方法一:view.post(Runnable action)

这是view自带的方法,如果子线程里可以得到要更新的view的话,可以用此方法进行更新。view还有一个方法view.postDelayed(Runnable action, long delayMillis)用来延迟发送。

textView.post(new Runnable() {

        @Override

        public void run() {

            textView.setText("更新textView");

            //还可以更新其他的控件

            imageView.setBackgroundResource(R.drawable.update);

        }

    });


方法二:activity.runOnUiThread(Runnable action)


((MainActivity) context).runOnUiThread(new Runnable() {

            @Override

            public void run() {

                //此时已在主线程中,可以更新UI了

            }

        });

    

注意:context 对象必须是主线程中的Activity,要强转才可以。

如果没有上下文(context),试试下面的方法:

1.用view.getContext()可以得到上下文。

2.跳过context直接用new Activity().runOnUiThread(Runnable action)来切换到主线程。


方法三:使用Handler

(1)首先在主线程中定义Handler,Handler mainHandler = new Handler()(必须要在主线程中定义才能操作主线程,如果想在其他地方定义声明时要这样写Handler mainHandler = new Handler(Looper.getMainLooper()),来获取主线程的 Looper 和 Queue );

(2)获取到 Handler 后,用handler.post(Runnable r)方法把消息处理放在该 handler 依附的消息队列中(即主线程消息队列)。

(1)如果是在子线程中创建Handler:

    Handler mainHandler = new Handler(Looper.getMainLooper());

    mainHandler.post(new Runnable() {

        @Override

        public void run() {

            //已在主线程中,可以更新UI

        }

    });

Handler还有下面的方法: 

1.postAtTime(Runnable r, long uptimeMillis); //在某一时刻发送消息 

2.postAtDelayed(Runnable r, long delayMillis); //延迟delayMillis毫秒再发送消息


(2)如果在主线程中创建Handler:

    Handler mainHandler = new Handler() {

        @Override

        public void handleMessage(Message msg) {

            switch(msg.what) {

                case 0:

                    //更新UI等

                    break;

                case 1:

                     //更新UI等

                    break;

                default:

                    break;

            }

        }

    }

之后可以把 mainHandler 当做参数传递在各个类之间,当需要更新UI时,可以调用sendMessage一系列方法来执行handleMessage里的操作,假设现在在子线程里:

    Message msg = myHandler.obtainMessage();

    msg.what = 0; //消息标识

    myHandler.sendMessage(msg); //发送消息

获取消息,尽量用obtainMessage()方法,该方法内存开销更小。如果每次都用Messenger msg=new Messenger()去创建一个消息对象,会增加内存开销。如上代码,只是发送了个消息标识,并没有传其他参数,如果想传递参数,可以这样:

      msg.what = 1;  //消息标识

      msg.arg1=2;   //存放整形数据,如果携带数据简单,优先使用arg1和arg2,比Bundle更节省内存。

      msg.arg2=3;   //存放整形数据

      Bundle bundle=new Bundle();

      bundle.putString("name","Tom");

      bundle.putInt("tag",66);

      msg.setData(bundle);

      msg.obj=bundle;   //用Object类型来存放任意对象

      myHandler.sendMessage(msg); //发送消息 

还可以用简化方法sendEmptyMessage(int what)来减少不必要的代码:

 myHandler.sendEmptyMessage(0); //其实内部实现还是和上面一样

其他发送消息的方法有:

endEmptyMessageAtTime(int what, long uptimeMillis); //定时发送空消息

sendEmptyMessageDelayed(int what, long delayMillis); //延时发送空消息

sendMessageAtTime(Message msg, long uptimeMillis); //定时发送消息

sendMessageDelayed(Message msg, long delayMillis); //延时发送消息

sendMessageAtFrontOfQueue(Message msg); //最先处理消息(慎用)

    

方法四:使用AsyncTask

这个类中的方法执行顺序依次为onPreExecute, doInBackground, onPostExecute

private class MyAsyncTask extends AsyncTask<String, Integer, String> {

        /**

         * 在主线程中执行execute()后首先执行onPreExecute()

         * 一般用来在执行后台任务前对UI做一些标记

         */

        @Override

        protected void onPreExecute() {

            super.onPreExecute();

            Log.i(“methodTag:::”,"onPreExecute");

        }


        /**

         * 子线程中执行,执行一些耗时操作,

         * 在执行过程中可以调用publishProgress(Progress... values)来更新进度信息。

         */

        @Override

        protected String doInBackground(String... params) {

            Log.i(“methodTag::::",”doInBackground");

            int count = 0;

            for (int i = 0; i < 10; i++) {

                try {

                    count++;

                    publishProgress((count % 100) * 10);

                    Thread.sleep(3000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

            return "耗时操作执行完毕";

        }


        /**

         * onProgressUpdate是在主线程中执行的

         * 在 doInBackground 方法中调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件中

         */

        @Override

        protected void onProgressUpdate(Integer... values) {

            super.onProgressUpdate(values);

            progressBar.setProgress(values[0]);

            textView.setText("loading..." + values[0] + "%");

            Log.i(“methodTag",”onProgressUpdate");

        }


        /**

         * 当后台操作结束时,调用此方法

         * 计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。

         */

        @Override

        protected void onPostExecute(String msg) {

            super.onPostExecute(msg);

            Log.i(“method”,"onPostExecute msg=" + msg);

            textView.setText(msg);

        }


        /**

         * 在主线程中执行

         * 当异步任务取消后的,会回调该函数。在该方法内可以更新UI

         */

        @Override

        protected void onCancelled() {

            super.onCancelled();

            Log.i(“methodtag“,"onCancelled");

            progressBar.setProgress(0);

            textView.setText("0");

        }


        @Override

        protected void onCancelled(String s) {

            super.onCancelled(s);

        }

    }

注意:doInBackground方法是在子线程中运行的,所以,在这个方法里面执行耗时操作。由于其返回结果会传递到onPostExecute方法中,而onPostExecute方法工作在UI线程,这样我们就在这个方法里面更新ui,达到了异步更新ui的目的。对于android的异步加载数据及更新ui,除了AsyncTask之外,还有xUtils3,AsyncHttpClient,Okhttp,Volley等这些优秀的网络框架


更多精彩内容请关注微信公众号:山楂干Linux


文章转载自WTech,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论