handler的基本用法

handler是什么:

 handler是Android给我们提供用于更新UI的一套机制,也是一套消息处理的机制,我们可以发送消息,也可以通过它处理消息。

为什么要用handler:

 Android在设计的时候,就封装了一套消息创建,传递,处理机制,如果不遵循这样的机制就没办法更新UI信息,就会抛出异常信息。

handler的用法:

 第一种:主线程中创建Handler对象,子线程中调用handler的post(Runnable runnable)方法更新UI。举例演示这种用法,2s后更新MainActivity中TextView的显示内容。

MainActivity的代码:

public class MainActivity extends AppCompatActivity {
    private TextView mTextView;

    private Handler mHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.text_view);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            mTextView.setText("hello, I am here");
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }
}
第二种:主线程中创建Handler对象;创建Runnable类,在Runnable类中的run方法中更新UI,调用handler的postDelayed(Runnable runnable,long delayMilis)方法指定具体的延时时间更新UI操作;在主线程中调用handler.postDelayed(Runnable runnable,long delayMilis); 举例说明每隔2s中更新MainActivity中的ImageView

public class MainActivity extends AppCompatActivity {
    private TextView mTextView;
    private ImageView mImageView;

    private Handler mHandler = new Handler();

    private int index;
    private int[] images = {R.drawable.image1,
                                R.drawable.image2,
                                R.drawable.image3};
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.text_view);
        mImageView = (ImageView) findViewById(R.id.image_view);
        mHandler.postDelayed(mMyRunnable,2000);
    }

    private MyRunnable mMyRunnable = new MyRunnable();

    class MyRunnable implements Runnable{
        @Override
        public void run() {
            index ++;
            index = index %3;
            mImageView.setBackgroundResource(images[index]);
            mHandler.postDelayed(mMyRunnable,2000);
        }
    }
}
第三种:主线程中创建Handler对象,并重写它的handleMessage方法,在handleMessage方法中根据子线程中发送过来的message信息更新UI;子线程创建Message对象,调用Messeng的参数封装要传递的信息,使用Handler的sendMessage()方法或者message的sendToTarget()方法发送消息。messsage的obj属性时传递一个对象,arg1和arg2是传递一个int型的参数,以第一个更新textview的内容作为演示:

public class MainActivity extends AppCompatActivity {
    private TextView mTextView;
    private ImageView mImageView;

    private  Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mTextView.setText(""+msg.obj);

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.text_view);
        mImageView = (ImageView) findViewById(R.id.image_view);
      
       new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   Thread.sleep(2000);
                   Message message = mHandler.obtainMessage();
                   Human human = new Human();
                   human.name = "Peter";
                   human.age = 18;
                   message.obj = human;
                   mHandler.sendMessage(message);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }

           }
       }).start();
    }
    class Human{
        private int age;
        private String name;

        @Override
        public String toString() {
            return "name: "+name +" age: "+age;
        }
    }
}
第四种:Handler中message的终止操作,把消息从Handler中移除。调用Handler的removeCallbacks(Runnable r)方法或者它的重载方法,以button的点击事件作为触发消息移除的操作动作,在第二种的基础上进行添加,代码如下:

public class MainActivity extends AppCompatActivity {
    private TextView mTextView;
    private ImageView mImageView;
    private Button mButton;

    private Handler mHandler = new Handler(); 
    private int index;
    private int[] images = {R.drawable.image1,
            R.drawable.image2,
            R.drawable.image3};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.text_view);
        mImageView = (ImageView) findViewById(R.id.image_view);
        mButton = (Button) findViewById(R.id.btu);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               mHandler.removeCallbacks(mMyRunnable);
            }
        });
          mHandler.postDelayed(mMyRunnable,2000);

    }

    private MyRunnable mMyRunnable = new MyRunnable();

    class MyRunnable implements Runnable{
        @Override
        public void run() {
            index ++;
            index = index %3;
            mImageView.setBackgroundResource(images[index]);
            mHandler.postDelayed(mMyRunnable,2000);
        }
    }

}
第五种:对Handler发送的消息进行截获,根据是否截获成功进行相应的处理。在创建Handler对象时,调用Handler的带参构造(Callback c)如下:

public class MainActivity extends AppCompatActivity {
    private TextView mTextView;
    private ImageView mImageView;
    private Button mButton;

    private Handler mHandler = new Handler(new Handler.Callback() {
        //在下面的返回值为boolean类型的handleMessage方法中进行截获,根据是否截获成功返回true或者false,
        //若返回为false则表示没有截获成功,下面的void  handleMessage方法会继续执行;若返回为true则表示截获成功,
        //就只会调用boolean handleMessage方法中的逻辑,而不会执行void handleMessage方法中的逻辑
        @Override
        public boolean handleMessage(Message msg) {
            //......
            Toast.makeText(MainActivity.this, "1", Toast.LENGTH_SHORT).show();
            return false;
        }
    }){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //.......
            Toast.makeText(MainActivity.this, "2", Toast.LENGTH_SHORT).show();
        }
    };

android为什么要设计只能通过Handler机制更新UI

 最根本的目的就是解决多线程并发问题。

 假如在一个activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样子的问题?

  更新界面错乱

 如果对更新ui的操作都进行加锁处理的话又会出现什么问题?

  性能下降

  处于对以上目的问题的考虑,Android给我们提供了一套更新ui的机制,我们只需遵循这样的机制就可以了,不用关心多线程问题,所有的更新ui的操作,都是在主线程的消息队列当中去轮询处理的

Handler的原理

 Handler负责发送消息,Looper负责接收handler发送的消息,并直接将消息回传给Handler自己,MessageQueue就是一个存储消息的容器

如何实现一个与线程相关的handler

public class SecondActivity extends AppCompatActivity {
    private static final String TAG = "SecondActivity";
    
    class MyThread extends Thread{
        public Handler mHandler;

        @Override
        public void run() {
            Looper.prepare();
            mHandler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    Log.e(TAG, "currentThread: "+Thread.currentThread());
                    //E/SecondActivity: currentThread: Thread[Thread-136,5,main]
                }
            };
            Looper.loop();
        }
    }

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.e(TAG, "handleMessage: currentThread"+Thread.currentThread() );
            //E/SecondActivity: handleMessage: currentThreadThread[main,5,main]
        }
    };

    private MyThread mThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        mThread = new MyThread();
        mThread.start();
        try {
            mThread.sleep(1000);
            mThread.mHandler.sendEmptyMessage(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        mHandler.sendEmptyMessage(1);
    }
}
HandlerThread解决多线程的并发问题,多线程并发导致的空指针问题

public class ThridActivity extends AppCompatActivity {
    private static final String TAG = "ThridActivity";
    
    class MyThread extends Thread{
        public Handler mHandler;
        @Override
        public void run() {
            Looper.prepare();
            mHandler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    Log.e(TAG, "currentThread: "+Thread.currentThread());
                    //E/SecondActivity: currentThread: Thread[Thread-136,5,main]
                }
            };
            Looper.loop();
        }
    }

    private MyThread mThread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thrid);
        mThread = new MyThread();
        mThread.start();
//构建Handler时调用了子线程中的Looper,引发并发问题的空指针现象
        Handler handler = new Handler(mThread.mHandler.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.e(TAG, "handleMessage: currentThread" + Thread.currentThread() );
            }
        };
         handler.sendEmptyMessage(1);
    }
}
报错如下:

com.example.sun.handlerdemo E/AndroidRuntime: FATAL EXCEPTION: main
                                                                           Process: com.example.sun.handlerdemo, PID: 2057
                                                                           java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.sun.handlerdemo/com.example.sun.handlerdemo.ThridActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.os.Looper android.os.Handler.getLooper()' on a null object reference
                                                                               at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
                                                                               at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
                                                                               at android.app.ActivityThread.-wrap11(ActivityThread.java)
                                                                               at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
                                                                               at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                               at android.os.Looper.loop(Looper.java:148)
                                                                               at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                               at java.lang.reflect.Method.invoke(Native Method)
                                                                               at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                               at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
                                                                            Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.os.Looper android.os.Handler.getLooper()' on a null object reference
                                                                               at com.example.sun.handlerdemo.ThridActivity.onCreate(ThridActivity.java:40)
                                                                               at android.app.Activity.performCreate(Activity.java:6237)
                                                                               at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
                                                                               at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369)
                                                                               at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
                                                                               at android.app.ActivityThread.-wrap11(ActivityThread.java)
                                                                               at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
                                                                               at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                               at android.os.Looper.loop(Looper.java:148)
                                                                               at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                               at java.lang.reflect.Method.invoke(Native Method)
                                                                               at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                               at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)


改用HandlerThread

public class ThridActivity extends AppCompatActivity {
    private static final String TAG = "ThridActivity";
    private Handler mHandler;
    private HandlerThread mThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thrid);

        mThread = new HandlerThread("handler Thread");
        mThread.start();

        Handler handler = new Handler(mThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.e(TAG, "handleMessage: currentThread: " + Thread.currentThread() );
                // E/ThridActivity: handleMessage: currentThread: Thread[handler Thread,5,main]
            }
        };
         handler.sendEmptyMessage(1);
    }
}
Android中更新UI的几种方式:
public class FiveActivity extends AppCompatActivity {
    public static final int UPDATE_UI = 1;
    private TextView mTextView;

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case UPDATE_UI:
                    mTextView.setText("ok");
                    break;
                default:
                    break;
            }
        }
    };

    //更新ui的方式一 handler.post(Runnable r)
    private void updateUI_1(){
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                mTextView.setText("ok");
            }
        });
    }

    //更新ui的方式二 handler.sendMessage(message m)
    private void updateUI_2(){
        Message message = new Message();
        message.what = UPDATE_UI;
        mHandler.sendMessage(message);
    }

    //更新ui的方式三 runOnUIThread(Runnable r)
    private void updateUI_3(){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mTextView.setText("ok");
            }
        });
    }

    //更新ui的方式四 view.post(Runnable r)
    private void updateUI_4(){
        mTextView.post(new Runnable() {
            @Override
            public void run() {
                mTextView.setText("ok");
            }
        });
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_five);
        mTextView = (TextView) findViewById(R.id.textView);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    //updateUI_1();
                    // updateUI_2();
                    // updateUI_3();
                    updateUI_4();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
 Handler使用过程中的两种常见的异常情况
第一种:在子线程中进行UI操作,报错如下:
 E/AndroidRuntime: FATAL EXCEPTION: Thread-175
                                                                           Process: com.example.sun.handlerdemo, PID: 4454
                                                                           android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
                                                                               at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556)
                                                                               at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:907)
...
第二种:在子线程中创建Handler时,并没有为这个Handler指定Looper
 E/AndroidRuntime: FATAL EXCEPTION: Thread-179
                                                                           Process: com.example.sun.handlerdemo, PID: 4529
                                                                           java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
                                                                               at android.os.Handler.<init>(Handler.java:200)
                                                                               at android.os.Handler.<init>(Handler.java:114)
                                                                               at com.example.sun.handlerdemo.FiveActivity$1.run(FiveActivity.java:72)
                                                                               at java.lang.Thread.run(Thread.java:818)

猜你喜欢

转载自blog.csdn.net/yao_94/article/details/79181689