Handler Processing mechanism
Android subthreads cannot dynamically change the properties of UI components in the main thread. When the program starts, the Activity starts the main thread, the UI thread, which processes user input and presents the results of the calculation to the user. When dealing with some operations that may block, it is necessary to start the sub-thread to prevent the main thread from blocking. For example, when a user requests to download resources from the network, the UI cannot appear “stuck”. Examples of multi-threading abound.
eg:
finalTextView textView= findViewById(R.id.tv); Button button= (Button) findViewById(R.id.button); A button. SetOnClickListener (new View.OnClickListener() {
@Override
public void onClick(View v) {
// Create new thread
Thread thread = new Thread(new Runnable() {
@Override
public void run(a) {
// Try updating the properties of the Text View component in the main thread
textView.setText("mask"); }}); thread.start();// Start the thread}});Copy the code
Running results:
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)
Copy the code
Only the thread that created the view can manipulate the view. If the TextVIew component is created in the main thread, the child thread cannot gain control of the component. So there must be a way for the child threads to notify the main thread, which then changes the component’s property values.
Handler is a Message processing mechanism provided by Android. The Handler sends and processes a Message object to the MessageQueue of its thread. When Looper polls for the Message, Use handler.handlermaeesage () to handle.
Back in the case, the main thread needs to be able to handle messages with handlers, so you can register a callback. You just need to override the methods in the Handler class that handle the message. When the newly started thread sends a message, the methods in the Handler class that handle the message are automatically called back.
The three methods are as follows:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = findViewById(R.id.tv); // Get the textbox component
Button button= findViewById(R.id.button); // Get the button component
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Create new thread
new Thread(new Runnable() {
@Override
public void run(a) {
// The operation to perform
Message message = new Message();
message.what = 0;
Bundle bundle = new Bundle();
bundle.putString("name"."mask");
message.setData(bundle);
mhandler.sendMessage(message);
}
}).start(); // Start the thread
}
Handler mhandler = new Handler() {
// Register to handle callbacks to messages
@Override
public void handleMessage(@NonNull Message msg) {
if(msg.what == 0) {
textView.setText(msg.getData().getString("name")); }}}; }); }Copy the code
@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) {
// Create new thread
new Thread(new Runnable() {
@Override
public void run(a) {
// The operation to perform
message = new Message();
message.what = 0;
Bundle bundle = new Bundle();
bundle.putString("name"."mask");
message.setData(bundle);
mhandler.post(t);
}
}).start(); // Start the thread
}
Handler mhandler = new Handler();
Thread t = new Thread() {
@Override
public void run(a) {
textView.setText(message.getData().getString("name")); }}; }); }Copy the code
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = findViewById(R.id.tv); // Get the textbox component
Button button= findViewById(R.id.button); // Get the button component
button.setOnClickListener(new View.OnClickListener() {
Message message = null;
@Override
public void onClick(View v) {
// Create new thread
new Thread(new Runnable() {
@Override
public void run(a) {
// The operation to perform
message = new Message();
message.what = 0;
Bundle bundle = new Bundle();
bundle.putString("name"."mask");
message.setData(bundle);
MainActivity.this.runOnUiThread(t);
}
}).start(); // Start the thread
}
Thread t = new Thread() {
@Override
public void run(a) {
textView.setText(message.getData().getString("name")); }}; }); }Copy the code
Handler works as follows:
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.
Each Handler instance corresponds to a thread and the message queue for that thread. When a Handler is created, it is bound to Looper. Handler sends messages and runnable objects to the message queue of the Looper and executes them on the thread of the Looper.
In the main thread, an object is automatically initialized, so you can create a Handler directly in your program, which can then be used to send and process messages. In the child thread, you must manually create a Looper object and start the Looper using the loop() method. The steps for using Handler in child threads are as follows:
- The prepare() method of Looper is called to create a Looper object for the current thread, and the corresponding MessageQueue is created in the constructor that created the Looper object.
- Create an instance of a Handler subclass that overrides the handlerMessage() method used to handle messages from other threads.
- Call Looper’s loop() method to start Looper.
Eg: Create child thread 2 again in child thread 1, thus creating a looper in child thread 1, then child thread 1 also has a process of handler processing mechanism. The code is as follows:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = findViewById(R.id.tv); // Get the textbox component
Button button= findViewById(R.id.button); // Get the button component
Looper.getMainLooper();
button.setOnClickListener(new View.OnClickListener() {
Message message = null;
@Override
public void onClick(View v) {
Log.d("xixi"."main:"+android.os.Process.myTid());
// Create new thread
new Thread(subThread).start(); // Start the thread
}
Thread subThread = new Thread() {
@Override
public void run(a) {
Looper.prepare();
Log.d("xixi"."outer:"+android.os.Process.myTid());
new Thread() {
@Override
public void run(a) {
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")); }}; }; }); }Copy the code
Running results:
D/xixi: main:25406
D/xixi: outer:26836
D/xixi: inner:26837
D/xixiu: mask
Copy the code
Android.os. Looper main functions:
Looper.prepare(); Create a Looper object for the current thread
Looper.getMainLooper(); // Returns the Looper object for the main thread currently applied
Looper.myLooper(); // Returns the Looper object of the current thread
Looper.loop(); // Start Looper. Get the Message from the Message queue, and perform the MSG. Target. DispatchMessage (MSG), MSG. The target is bound to the Message queue Handler
Copy the code