安卓之handler处理机制与service组件

206 阅读4分钟

handler处理机制

安卓中子线程不能动态改变主线程中UI组件的属性。程序启动时,Activity会启动主线程,也就是UI线程,用于处理用户输入、将计算的结果展示给用户;在处理一些可能发生阻塞的操作时,需要启动子线程,防止主线程阻塞,比如:用户请求网络下载资源时,UI不能出现“卡顿”,多线程的例子比比皆是。

eg:

final TextView textView= findViewById(R.id.tv); 
Button button= (Button) findViewById(R.id.button);件
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //创建新线程
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                // 尝试更新主线程中Text View组件的属性
                textView.setText("mask");
            }
        });
        thread.start(); //开启线程
    }
});

运行结果:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8913)
        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1557)
        at android.view.View.requestLayout(View.java:24694)
        at android.view.View.requestLayout(View.java:24694)
        at android.view.View.requestLayout(View.java:24694)
        at android.view.View.requestLayout(View.java:24694)
        at android.view.View.requestLayout(View.java:24694)
        at android.view.View.requestLayout(View.java:24694)
        at android.view.View.requestLayout(View.java:24694)
        at android.widget.TextView.checkForRelayout(TextView.java:9750)
        at android.widget.TextView.setText(TextView.java:6314)
        at android.widget.TextView.setText(TextView.java:6142)
        at android.widget.TextView.setText(TextView.java:6094)
        at com.xxx.a02_handler.MainActivity$1$1.run(MainActivity.java:26)
        at java.lang.Thread.run(Thread.java:929)

报错的意思:只有创建这个view的线程才能操作这个view。TextVIew组件在主线程中创建,那子线程中就无法获取这个组件的控制权。所以必须有一种方式让子线程去通知主线程,然后主线程去修改组件的属性值。

Handler是安卓提供的一种消息处理机制,通过handler发送和处理 Message 对象到其所在线程的 MessageQueue 中,当Looper轮询到该Message时,使用handler.handlerMaeesage()来处理。

回到案例中,主线程要能用handler处理消息,可以注册一个回调。只需要重写 Handler 类中处理消息的方法。当新启动的线程发送消息时,Handler类中处理消息的方法就会被自动回调。

三种方法如下:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView textView = findViewById(R.id.tv); //获取文本框组件
        Button button=  findViewById(R.id.button); //获取按钮组件

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //创建新线程
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //要执行的操作
                        Message message = new Message();
                        message.what = 0;
                        Bundle bundle = new Bundle();
                        bundle.putString("name","mask");
                        message.setData(bundle);
                        mhandler.sendMessage(message);
                    }
                }).start(); //开启线程
            }

            Handler mhandler = new Handler() {
                // 注册处理消息的回调
                @Override
                public void handleMessage(@NonNull Message msg) {
                    if(msg.what == 0) {
                        textView.setText(msg.getData().getString("name"));
                    }
                }
            };
        });
    }
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final TextView textView = findViewById(R.id.tv);
    Button button=  findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {
        Message message = null;
        @Override
        public void onClick(View v) {
            //创建新线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //要执行的操作
                    message = new Message();
                    message.what = 0;
                    Bundle bundle = new Bundle();
                    bundle.putString("name","mask");
                    message.setData(bundle);
                    mhandler.post(t);
                }
            }).start(); //开启线程
        }

        Handler mhandler = new Handler();
        Thread t = new Thread() {
            @Override
            public void run() {
                textView.setText(message.getData().getString("name"));
            }
        };
    });
}
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView textView = findViewById(R.id.tv); //获取文本框组件
        Button button=  findViewById(R.id.button); //获取按钮组件

        button.setOnClickListener(new View.OnClickListener() {

            Message message = null;
            @Override
            public void onClick(View v) {
                //创建新线程
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //要执行的操作
                        message = new Message();
                        message.what = 0;
                        Bundle bundle = new Bundle();
                        bundle.putString("name","mask");
                        message.setData(bundle);
                        MainActivity.this.runOnUiThread(t);
                    }
                }).start(); //开启线程
            }

            Thread t = new Thread() {
                @Override
                public void run() {
                    textView.setText(message.getData().getString("name"));
                }
            };
        });
    }

Handler工作原理:

绘图1.png

Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper's message queue and execute them on that Looper's thread.

每个Handler实例对应一个线程和这个线程的消息队列。当创建一个Handler,就会和Looper绑定。handler发送消息和可运行对象传递到该Looper的消息队列,并在该Looper的线程上执行。

主线程中,会自动初始化一个对象,因此在程序中可以直接创建Handler,然后就可以通过 Handler 进行发送消息和处理消息。在子线程中,必须手动创建一个 Looper 对象,并通过 loop() 方法启动 Looper。 在子线程中使用 Handler 的步骤如下:

  • 调用 Looper 的 prepare() 方法为当前的线程创建 Looper 对象,在创建 Looper 对象的构造器中会创建与之配套的 MessageQueue。
  • 创建 Handler 子类的实例,重写 handlerMessage() 方法用来处理来自于其他线程的消息。
  • 调用 Looper 的 loop() 方法启动 Looper。

eg:在子线程1中再次创建子线程2,从而子线程1中创建一个looper,这时候子线程1中也有一套handler处理机制的流程。代码如下:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView textView = findViewById(R.id.tv); //获取文本框组件
        Button button=  findViewById(R.id.button); //获取按钮组件

        Looper.getMainLooper();
        button.setOnClickListener(new View.OnClickListener() {

            Message message = null;
            @Override
            public void onClick(View v) {
                Log.d("xixi","main:"+android.os.Process.myTid());
                //创建新线程
                new Thread(subThread).start(); //开启线程
            }
            Thread subThread = new Thread() {
                @Override
                public void run() {
                    Looper.prepare();
                    Log.d("xixi","outer:"+android.os.Process.myTid());
                    new Thread() {
                        @Override
                        public void run() {
                            Log.d("xixi","inner:"+android.os.Process.myTid());
                            message = new Message();
                            message.what = 0;
                            Bundle bundle = new Bundle();
                            bundle.putString("name","mask");
                            message.setData(bundle);
                            handler.sendMessage(message);
                        }
                    }.start();
                    Looper.loop();

                }

                Handler handler = new Handler() {
                    @Override
                    public void handleMessage(@NonNull Message msg) {
                        Log.d("xixi",message.getData().getString("name"));
                    }
                };
            };
        });
    }

运行结果:

D/xixi: main:25406
D/xixi: outer:26836
D/xixi: inner:26837
D/xixiu: mask

android.os.Looper主要函数:

Looper.prepare(); // 为当前线程创建一个Looper对象

Looper.getMainLooper(); // 返回当前应用主线程的Looper对象

Looper.myLooper(); // 返回当前线程的Looper对象

Looper.loop(); // 启动Looper。从消息队列中获取Message,并执行msg.target.dispatchMessage(msg),msg.target其实是绑定到消息队列的Handler