★Handler的使用
不如我们先了解下什么是Handler机制,他的作用是什么吧!
Handler的作用:
关于Handler我刚开始学习Android开发的时候我也是没有搞懂,我是在第二次重新学习的时候,也找了很多资料才明白的,从英语单词上来解释是“处理者”的意思,我从百度上搜到一个解释还比较详细,他是这样解释的:
当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件, 进行事件分发, 比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。 如果此时需要一个耗时的操作,例如: 联网读取数据, 或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示。
我个人的理解:
我觉得这些解释让我有时候也不是很懂,我从小语文就差,后来根据了解到,我的个人理解是,有时候我们会把一些组件的点击动作都写进主线程里面,有些动作需要延迟,比如progressbar进度条有些时候是延续性动作,但是如果都加入到主线程,那么这个动作就会占用主线程,然后如果占用达到一定程度,就会导致程序发生错误或假死的状态。还有比如点击一个Button更新TextView显示文字,一般新手就会用主线程去更新UI,但是现在就有一个Handler机制用子线程去更新UI的操作,专门针对一些耗时的操作,如果没有这个机制,你多个线程操作就会造成堵塞,超过5秒就会出现程序崩溃,假死的现象,有了Handler机制还能控制多个子线程同时进行,所以大家应该了解到了Handler的作用和必要性了吧!
在这里我会用一个app来演示Handler的简单使用方法。
在上面我们可以看见一共有4个Button按钮,每个按钮都会跳转到一个新的Activity演示一种Handler的使用,下面是我的代码。
案例一:
点击start按钮以后TextView控件上一直反复打印更新中......直到点击end按钮停止
首先我们来看看戴安是怎么实现这个功能的
主要的关键代码是:
Handler handler = new Handler();
Runnable update = new Runnable() {
@Override
public void run() {
textview.append("\n更新中......");
handler.postDelayed(update,1000);
}
};
这里是创建一个handler对象,新建一个子线程,里面调用textview.append()方法,打印一串字符串,然后再调用handler.postDelayed()方法,达到延迟效果
然后是两个按钮的监听器立的方法
button_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.post(update);
}
});
button_end.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.removeCallbacks(update);
}
});
handler.post()方法是开始执行,handler.removeCallbacks()方法是停止里面的对象update就是子线程的对象
案例二:
点击start按钮显示一个进度条自动加载直到加载完成
我们来看一下关键的java代码:
Handler handler = new Handler(){
public void handleMessage(Message msg) {
progressbar.setProgress(msg.arg1);
handler.post(update);
}
};
Runnable update = new Runnable() {
int i = 0;
@Override
public void run() {
i += 10;
Message msg = handler.obtainMessage();
msg.arg1 = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendMessage(msg);
if(i == progressbar.getMax())
handler.removeCallbacks(update);
}
};
在这里比案例二的java代码中多了handlerMessage这个对象,这个对象主要作用就是传值的作用或者说是在子线程run()方法和Handler之间传递消息,让Handler判断什么时候开始执行子线程活该执行哪一个线程
Message msg = handler.obtainMessage();
msg.arg1 = i;
这里的msg.arg就是一个惨就是用这个参数来决定进度条的每次增加多少
handler.sendMessage(msg);
而这里的这个方法就是传递消息方法,当Handler接受到这个消息是就开启线程的耗时操作,当然也可以用这个方法来区分线程的队列。
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
progressbar.setVisibility(progressbar.VISIBLE);
handler.post(update);
}
});
然后这句代码就是按钮点击后的动作,先是显示进度条然后再开启子线程进行耗时操作。
案例三:
左边是点击执行按钮之后显示呵呵。。右边的是点击执行按钮之前的屏幕显示嘻嘻
这个例子相对来说很简单,主要是通过子线程来更新UI
我们还是来看看主要的java代码吧!
在这里还是同样地创建了一个Handler对象,这里就用到了上个例子我说过的当msg.wht=0x123的时候执行下面的语句,textview.setText()方法,把TextView控件的内容变为呵呵。。!
Handler handler = new Handler() {
// 该方法运行在主线程中
// 接收到handler发送的消息,对UI进行操作
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
if (msg.what == 0x123) {
textView.setText("呵呵。。");
}
}
};
这里点击按钮执行setButton()方法,这里用到了封装的技术,学过java的,我就不多介绍了吧!
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setButton();
}
});
我们来看看setButton这个方法的代码,就是创建一个Thread()方法来进行耗时操作,这里我们可以看到handler.senEmptyMessage()这个方法这里就是给队列放松消息,当Handler对象接收到这个消息后就会更新UI。
private void setButton(){
new Thread(new Runnable() {
@Override
public void run() {
// 在此执行耗时工作,执行完毕后调用handler发送消息
try {
Thread.sleep(6000);//睡眠6秒 模拟执行耗时任务
handler.sendEmptyMessage(0x123);//发送消息
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
案例四:
这个案例是倒计时5秒后自动跳转到另一个activity,这个例子对于上面的例子来说难度会大一些,主要是看跳转前的java代码来实现这个效果。
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what == UPDATE){
textView.setText(String.valueOf(msg.arg1));
}
}
};
这句代码我相信大家应该都清楚了其中的作用了吧!我们主要是来看看begin()这个方法里面的代码。
public void begin(){
new Thread(new Runnable() {
@Override
public void run() {
for (int i=5;i>0;i--){
Message msg = new Message();
msg.what = UPDATE;
msg.arg1 = i;
handler.sendMessage(msg);
try {
Thread.sleep(1000);//休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//计时结束后跳转到其他界面
startIntent();
//添加finish方法在任务栈中销毁倒计时界面,使新开界面在回退时直接退出而不是再次返回该界面
finish();
}
}).start();
}
可以看到这里用了for循环来实现倒计时的效果,然后定义一个msg.arg1参数用这个参数传递给外面显示在屏幕上数字,这里定义了msg.what=UPDATE,用这个来通知队列用handler.senMessage()方法将msg对象发送给Handler对象,当for循环完了执行startIntent()方法来实现跳转的效果,这里的这个方法是我封装的一个方法,然后finish()方法销毁这个Activity,这里还要给UPDATE这个参数进行赋值。
然后下面就是这个程序的XML布局文件的代码和java代码了。
主页面的XML页面布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="@string/TextView"
android:textSize="30dp"
android:layout_gravity="center"
android:layout_width="230dp"
android:layout_height="60dp" />
<Button
android:id="@+id/btn1"
android:text="@string/button1"
android:textSize="20dp"
android:layout_marginTop="20dp"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="60dp" />
<Button
android:id="@+id/btn2"
android:text="@string/button2"
android:textSize="20dp"
android:layout_marginTop="20dp"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="60dp" />
<Button
android:id="@+id/btn3"
android:text="@string/button3"
android:textSize="20dp"
android:layout_marginTop="20dp"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="60dp" />
<Button
android:id="@+id/btn4"
android:text="@string/button4"
android:textSize="20dp"
android:layout_marginTop="20dp"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="60dp" />
</LinearLayout>
主页面的java代码:
package com.example.handler;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity {
private Button button1;
private Button button2;
private Button button3;
private Button button4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainlayout);
button1 = (Button) findViewById(R.id.btn1);
button2 = (Button) findViewById(R.id.btn2);
button3 = (Button) findViewById(R.id.btn3);
button4 = (Button) findViewById(R.id.btn4);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setButton1();
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setButton2();
}
});
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setButton3();
}
});
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setButton4();
}
});
}
private void setButton1(){
Intent intent = new Intent();
intent.setClass(MainActivity.this,FirstDemo.class);
this.startActivity(intent);
}
private void setButton2(){
Intent intent = new Intent();
intent.setClass(MainActivity.this,SecondDemo.class);
this.startActivity(intent);
}
private void setButton3(){
Intent intent = new Intent();
intent.setClass(MainActivity.this,ThirdDemo.class);
this.startActivity(intent);
}
private void setButton4(){
Intent intent = new Intent();
intent.setClass(MainActivity.this,FourthDemo.class);
this.startActivity(intent);
}
}
案例一的XML页面布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/TextView"
android:layout_width="match_parent"
android:layout_height="280dp" />
<Button
android:id="@+id/btn_start"
android:text="@string/btn_start"
android:textSize="20dp"
android:layout_width="match_parent"
android:layout_height="60dp" />
<Button
android:id="@+id/btn_end"
android:text="@string/btn_end"
android:textSize="20dp"
android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="60dp" />
</LinearLayout>
案例一的java代码:
package com.example.handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class FirstDemo extends Activity {
private Button button_start;
private Button button_end;
private TextView textview;
Handler handler = new Handler();
Runnable update = new Runnable() {
@Override
public void run() {
textview.append("\n更新中......");
handler.postDelayed(update,1000);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstlayout);
button_start = (Button) findViewById(R.id.btn_start);
button_end = (Button) findViewById(R.id.btn_end);
textview = (TextView) findViewById(R.id.TextView);
button_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.post(update);
}
});
button_end.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.removeCallbacks(update);
}
});
}
}
案例二的XML的页面布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="fill_parent"
android:layout_height="100dip"
android:layout_alignParentTop="true"
style="?android:attr/progressBarStyleHorizontal"
android:visibility="gone"
/>
<Button
android:id="@+id/btn_start"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="80dp"
android:layout_alignParentBottom="true"
android:text="@string/btn_start"
/>
</LinearLayout>
案例二的java代码:
package com.example.handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
public class SecondDemo extends Activity {
private ProgressBar progressbar;
private Button start;
int Max = 200;
Handler handler = new Handler(){
public void handleMessage(Message msg) {
progressbar.setProgress(msg.arg1);
handler.post(update);
}
};
Runnable update = new Runnable() {
int i = 0;
@Override
public void run() {
i += 10;
Message msg = handler.obtainMessage();
msg.arg1 = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendMessage(msg);
if(i == progressbar.getMax())
handler.removeCallbacks(update);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.secondlayout);
progressbar = (ProgressBar) findViewById(R.id.progressbar);
start = (Button) findViewById(R.id.btn_start);
progressbar.setMax(Max);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
progressbar.setVisibility(progressbar.VISIBLE);
handler.post(update);
}
});
}
}
案例三的XML页面布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/TextView"
android:layout_marginTop="120dp"
android:layout_gravity="center"
android:textSize="30dp"
android:layout_width="150dp"
android:layout_height="50dp" />
<Button
android:id="@+id/btn_start"
android:text="执行"
android:layout_marginTop="50dp"
android:layout_gravity="center"
android:layout_width="120dp"
android:layout_height="60dp" />
</LinearLayout>
案例三的java代码:
package com.example.handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class ThirdDemo extends Activity {
private TextView textView;
private Button button;
Handler handler = new Handler() {
// 该方法运行在主线程中
// 接收到handler发送的消息,对UI进行操作
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
if (msg.what == 0x123) {
textView.setText("呵呵。。");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.thirdlayout);
textView = (TextView) findViewById(R.id.TextView);
button = (Button) findViewById(R.id.btn_start);
textView.setText("嘻嘻");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setButton();
}
});
}
private void setButton(){
new Thread(new Runnable() {
@Override
public void run() {
// 在此执行耗时工作,执行完毕后调用handler发送消息
try {
Thread.sleep(6000);//睡眠6秒 模拟执行耗时任务
handler.sendEmptyMessage(0x123);//发送消息
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
案例四还没有跳转的XML页面布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/TextView"
android:layout_marginTop="210dp"
android:layout_marginLeft="160dp"
android:layout_width="60dp"
android:layout_height="70dp"
android:textSize="60dp" />
</LinearLayout>
案例四跳转后的XML页面布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/TextView"
android:textSize="30dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
案例四没有跳转的java代码:
package com.example.handler;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
/*
* Handler:
* 1 处理的消息对象就是Message,理解为要传递的消息数据的封装对象
* Message what : 标记,用来区分多个消息
* Message arg1,arg2 : 用来传递int类型的数据
* Message obj : 可以传递任何类型的对象(Object)
*/
public class FourthDemo extends AppCompatActivity {
public static final int UPDATE = 0x1;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fourthlayout);
textView = (TextView) findViewById(R.id.TextView);
begin();
}
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what == UPDATE){
textView.setText(String.valueOf(msg.arg1));
}
}
};
public void begin(){
new Thread(new Runnable() {
@Override
public void run() {
for (int i=5;i>0;i--){
Message msg = new Message();
msg.what = UPDATE;
msg.arg1 = i;
handler.sendMessage(msg);
try {
Thread.sleep(1000);//休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//计时结束后跳转到其他界面
startIntent();
//添加finish方法在任务栈中销毁倒计时界面,使新开界面在回退时直接退出而不是再次返回该界面
finish();
}
}).start();
}
private void startIntent(){
Intent intent = new Intent();
intent.setClass(FourthDemo.this,FourthDemo2.class);
startActivity(intent);
}
}
案例四跳转后的java代码:
package com.example.handler;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class FourthDemo2 extends Activity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fourth2layout);
textView = (TextView) findViewById(R.id.TextView);
textView.setText("成功完成自动跳转");
}
}
这次的例子就讲完了,其实要想更加容易记住就得多动手!