Handler线程间通信
package com.hixin.appexplorer; import java.util.List; import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView; public class MainActivity extends Activity implements Runnable { GridView gv;
private List<PackageInfo> packageInfos;
private ProgressDialog pd;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
gv.setAdapter(new GridViewAdapter(MainActivity.this));
pd.dismiss();
setProgressBarIndeterminateVisibility(false);
} };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// requestWindowFeature(Window.FEATURE_NO_TITLE);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.show_app_grid); gv = (GridView)this.findViewById(R.id.gv_apps);
pd = ProgressDialog.show(this,"请稍候...", "正在搜索你所安装的应用程序",true,false);
setProgressBarIndeterminateVisibility(true);
Thread t = new Thread(this);
t.start();
} @Override
public void run() {
// TODO Auto-generated method stub
packageInfos = getPackageManager().getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mHandler.sendEmptyMessage(0);
} class GridViewAdapter extends BaseAdapter{ LayoutInflater inflater;
public GridViewAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return packageInfos.size();
} @Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return packageInfos.get(position);
} @Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub View view = inflater.inflate(R.layout.gv_item, null);
TextView tv = (TextView)view.findViewById(R.id.gv_item_appname);
ImageView iv = (ImageView)view.findViewById(R.id.gv_item_icon);
tv.setText(packageInfos.get(position).applicationInfo.loadLabel(getPackageManager()));
iv.setImageDrawable(packageInfos.get(position).applicationInfo.loadIcon(getPackageManager())); return view;
} } }
当需要显示的项目很多,会造成很大的延迟。这时候屏幕会一直黑屏。给用户很不好的体验。应该在获取信息的时候,显示一些进度信息给用户看,增加用户体验。
程序在期间获取安装程序信息,主界面上显示进度条,当信息获取完毕之后(获取信息在新线程里执行),发送一条消息通知主线程,主线程关掉进度条,加载列表。
基本概念
Looper:每一个线程都可以产生一个Looper,用来管理线程的Message,Looper对象会建立一个MessgaeQueue数据结构来存放message。
Handler:与Looper沟通的对象,可以push消息或者runnable对象到MessgaeQueue,也可以从MessageQueue得到消息。
线程A的Handler对象引用可以传递给别的线程,让别的线程B或C等能送消息来给线程A。
线程A的Message Queue里的消息,只有线程A所属的对象可以处理。
注意:Android里没有global的MessageQueue,不同进程(或APK之间)不能通过MessageQueue交换消息。
本例中线程A就是UI线程,线程B就是新建的处理线程
在handler.obtainMessage()的参数是这样写的:
Message android.os.Handler.obtainMessage(int what, int arg1, int arg2, Object obj)
public final Message obtainMessage (int what, int arg1, int arg2, Object obj)
Since: API Level 1
Same as obtainMessage(), except that it also sets the what, obj, arg1,and arg2 values on the returned Message.
Parameters
what Value to assign to the returned Message.what field.
arg1 Value to assign to the returned Message.arg1 field.
arg2 Value to assign to the returned Message.arg2 field.
obj Value to assign to the returned Message.obj field.
而Handler中obtainMessage与new Message的区别:
obtainmessage()是从消息池中拿来一个msg 不需要另开辟空间new
new需要重新申请,效率低,obtianmessage可以循环利用;
//use Handler.obtainMessage(),instead of msg = new Message();
//because if there is already an Message object,that not be used by
//any one ,the system will hand use that object,so you don't have to
//create and object and allocate memory.
//it is also another example of object recycling and reusing in android.
Message msg = mHandler.obtainMessage();
msg.what = UPDATE_LISTVIEW;
msg.obj = current + "/" + total + "songs";
//this method is called from worker Thread,so we cannot update UI from here.
msg.sendToTarget();
在看下面代码:
Message msg = handler.obtainMessage();
msg.arg1 = i;
msg.sendToTarget(); Message msg=new Message();
msg.arg1=i;
handler.sendMessage(msg);
第一种写法是message 从handler 类获取,从而可以直接向该handler 对象发送消息,第二种写法是直接调用 handler 的发送消息方法发送消息。
Handler相关说明:
解释:安卓的UI线程(即OnCreate函数创建的线程)是线程非安全的。也就是说,在UI线程中,使用sleep这样的函数会导致整个线程延迟,但是我们在安卓开发中,往往会经常遇到一些延迟比较厉害的操作,(例如通过HTTP获取数据信息)如果放在主线程中,则会影响UI界面的渲染。但是如果另外新开一个线程,则由于UI线程只能在主线程中修改,而导致无法修改主线程的UI界面。这个时候Handler就出来解决这个问题。
handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程), 也就是说Handler对象初始化后,就默认与对它初始化的进程的消息队列绑定,因此可以利用Handler所包含的消息队列,制定一些操作的顺序。
Handler主要两大作用:
1. 提供post操作。post操作主要将Runnable对象放进主线程(UI)线程中的队列中操作。post还支持延迟操作。使用post后,Runnable是按照队列的形式逐个执行的。
2. handlerMessage操作。主要用于新开一个线程与主线程中的通信。新开的线程执行完毕后,可以通过sendMessage给主线程发送消息,并且传递一些参数,然后主线程就可以修改UI界面了。
Handler提供的函数:
post类方法允许你排列一个Runnable对象到主线程队列中:
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendMessage类方法, 允许你安排一个带数据的Message对象到队列中:
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
应用实例:
1,传递Message。用于接受子线程发送的数据, 并用此数据配合主线程更新UI。
在Android中,对于UI的操作通常需要放在主线程中进行操作。如果在子线程中有关于UI的操作,那么就需要把数据消息作为一个Message对象发送到消息队列中,然后,用Handler中的handlerMessge方法处理传过来的数据信息,并操作UI。类sendMessage(Message msg)方法实现发送消息的操作。 在初始化Handler对象时重写的handleMessage方法来接收Messgae并进行相关操作。
2,传递Runnable对象。用于通过Handler绑定的消息队列,安排不同操作的执行顺序。
Handler对象在进行初始化的时候,会默认的自动绑定消息队列。利用类post方法,可以将Runnable对象发送到消息队列中,按照队列的机制按顺序执行不同的Runnable对象中的run方法。
另外,Android的CPU分配的最小单元是线程,Handler一般是在某个线程里创建的,因而Handler和Thread就是相互绑定的,一一对应。而Runnable是一个接口,Thread是Runnable的子类。所以说,他俩都算一个进程。xml文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" > <Button
android:id="@+id/startButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="start"
android:layout_centerInParent="true"
/>
<Button
android:id="@+id/endButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="end"
android:layout_below="@id/startButton"
/> </RelativeLayout>
package com.example.androidexpriment; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button; public class MainActivity extends Activity { private String TAG = "MainActivity";
//声明两个按钮控件
private Button startButton = null;
private Button endButton = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//根据控件的ID得到代表控件的对象,并为这两个按钮设置相应的监听器
startButton = (Button)findViewById(R.id.startButton);
startButton.setOnClickListener(new StartButtonListener());
endButton = (Button)findViewById(R.id.endButton);
endButton.setOnClickListener(new EndButtonListener());
Log.i(TAG,"Activity-->" + Thread.currentThread().getId()); }
class StartButtonListener implements OnClickListener{ @Override
public void onClick(View v) {
//调用Handler的post方法,将要执行的线程对象添加到队列当中
handler.post(updateThread);
} } class EndButtonListener implements OnClickListener{ @Override
public void onClick(View v) {
handler.removeCallbacks(updateThread);
} }
//创建一个Handler对象
Handler handler = new Handler();
//将要执行的操作写在线程对象的run方法当中
Runnable updateThread = new Runnable(){ @Override
public void run() {
Log.i(TAG,"UpdateThread");
Log.i(TAG,"Activity-->" + Thread.currentThread().getId());
//在run方法内部,执行postDelayed或者是post方法
handler.postDelayed(updateThread, 3000);
} };
}
点击start后,程序的运行结果就是每隔3秒钟,就会在控制台打印一行UpdateTread。这是因为实现了Runnable接口的updateThread对象进入了空的消息队列即被立即执行run方法,而在run方法的内部,又在3000ms之后将其再次发送进入消息队列中。
注意这种方法创建Handler对象并不需要重写handlerMessage方法。
从输出结果能看出来:
post方法虽然发送的是一个实现了Runnable接口的类对象,但是它并非创建了一个新线程,而是执行了该对象中的run方法。也就是说,整个run中的操作和主线程处于同一个线程。这样对于那些简单的操作,似乎并不会影响。但是对于耗时较长的操作,就会出现“假死”。为了解决这个问题,就需要使得handler绑定到一个新开启线程的消息队列上,在这个处于另外线程的上的消息队列中处理传过来的Runnable对象和消息。Runnable对象只是作为一个封装了操作的对象被传递,并未产生新线程。
下面这种写法也是可以的:
package com.example.androidexpriment; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button; public class MainActivity extends Activity { TestThread t = null;
private String TAG = "MainActivity";
//声明两个按钮控件
private Button startButton = null;
private Button endButton = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//根据控件的ID得到代表控件的对象,并为这两个按钮设置相应的监听器
startButton = (Button)findViewById(R.id.startButton);
startButton.setOnClickListener(new StartButtonListener());
endButton = (Button)findViewById(R.id.endButton);
endButton.setOnClickListener(new EndButtonListener());
Log.i(TAG,"onCreate-->" + Thread.currentThread().getId());
t= new TestThread(1); }
class StartButtonListener implements OnClickListener{ @Override
public void onClick(View v) {
//调用Handler的post方法,将要执行的线程对象添加到队列当中
handler.post(t);
} } class EndButtonListener implements OnClickListener{ @Override
public void onClick(View v) {
handler.removeCallbacks(t);
} }
//创建一个Handler对象
Handler handler = new Handler(); class TestThread extends Thread{
int prime;
TestThread(int prime) {
this.prime = prime;
}
@Override
public void run() {
//在run方法内部,执行postDelayed或者是post方法
handler.postDelayed(t, 3000);
Log.i(TAG,"TestThread-->" + Thread.currentThread().getId());
}
}
}
虽然创建了一个Thread,但是并没有执行Thread的start()方法。考虑到Thread和Runnable之间的关系,上面的两种代码并无实质差别,所以logcat中甚至都没出现启动新线程的日志。
然而,如果稍加修改:加上启动方法
class StartButtonListener implements OnClickListener{ @Override
public void onClick(View v) {
//调用Handler的post方法,将要执行的线程对象添加到队列当中
handler.post(t);
t.start();
}
}
可以明显看到,虽然启动了新线程,但post仍然可以把这个线程推到主线程里面去,线程由虚拟机自动结束。
所以,在UI线程(主线程)中:
mHandler=new Handler();
mHandler.post(new Runnable(){
void run(){
//执行代码..
}
});
这个线程其实是在UI线程之内运行的,并没有新建线程。
常见的新建线程的方法是:参考J2SE文档的
1、
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
} public void run() {
// compute primes larger than minPrime
. . .
}
}
The following code would then create a thread and start it running:
PrimeThread p = new PrimeThread(143);
p.start();
2、
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
} public void run() {
// compute primes larger than minPrime
. . .
}
}
The following code would then create a thread and start it running:
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
尽量按照上面给出的两种方式做,不要受网上影响简单的从Threa创建,那样不能做到传递参数。
static void |
sleep(long millis)
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers.
|
代码验证:
package com.example.androidexpriment; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button; public class MainActivity extends Activity { private String TAG = "MainActivity";
//声明两个按钮控件
private Button startButton = null;
private Button endButton = null;
TestThread t = null;
int flag = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//根据控件的ID得到代表控件的对象,并为这两个按钮设置相应的监听器
startButton = (Button)findViewById(R.id.startButton);
startButton.setOnClickListener(new StartButtonListener());
endButton = (Button)findViewById(R.id.endButton);
endButton.setOnClickListener(new EndButtonListener());
Log.i(TAG,"onCreate-->" + Thread.currentThread().getId()); t= new TestThread(1); }
class StartButtonListener implements OnClickListener{ @Override
public void onClick(View v) {
//调用Handler的post方法,将要执行的线程对象添加到队列当中
t.start();
} } class EndButtonListener implements OnClickListener{ @Override
public void onClick(View v) {
handler.sendEmptyMessage(33);
flag = 5;
} } class TestThread extends Thread{
int prime;
TestThread(int prime) {
this.prime = prime;
}
@Override
public void run() {
//在run方法内部,执行postDelayed或者是post方法
try {
while(true) {
Log.i(TAG,"TestThread-->" + Thread.currentThread().getId());
//handler.sendEmptyMessageDelayed(22,3000);
Thread.sleep(3000);
handler.sendEmptyMessage(22);
if(flag == 5) //线程最佳的退出方法,就是自杀,也就是在线程的函数里面自然的return 出来
return;
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
//创建一个Handler对象
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch(msg.what) {
case 22:
Log.i(TAG,"StartButton");
Log.i(TAG,"Handler-->" + Thread.currentThread().getId());
break;
case 33:
Log.i(TAG,"EndButton");
Log.i(TAG,"Handler-->" + Thread.currentThread().getId());
break;
} } }; }
Handler线程间通信的更多相关文章
- android 中的 Handler 线程间通信
一. 在MainActivity中为什么只是类似的写一行如下代码就可以使用handler了呢? Handler handler = new Handler() { @Override public v ...
- 源码分析Android Handler是如何实现线程间通信的
源码分析Android Handler是如何实现线程间通信的 Handler作为Android消息通信的基础,它的使用是每一个开发者都必须掌握的.开发者从一开始就被告知必须在主线程中进行UI操作.但H ...
- Android线程间通信机制——深入理解 Looper、Handler、Message
在Android中,经常使用Handler来实现线程间通信,必然要理解Looper , Handler , Message和MessageQueue的使用和原理,下面说一下Looper , Handl ...
- Android中线程间通信原理分析:Looper,MessageQueue,Handler
自问自答的两个问题 在我们去讨论Handler,Looper,MessageQueue的关系之前,我们需要先问两个问题: 1.这一套东西搞出来是为了解决什么问题呢? 2.如果让我们来解决这个问题该怎么 ...
- Net线程间通信的异步机制
线程间通信 我们看下面的图 我们来看线程间通信的原理:线程(Thread B)和线程(Thread A)通信, 首先线程A 必须实现同步上下文对象(Synchronization Context), ...
- 0038 Java学习笔记-多线程-传统线程间通信、Condition、阻塞队列、《疯狂Java讲义 第三版》进程间通信示例代码存在的一个问题
调用同步锁的wait().notify().notifyAll()进行线程通信 看这个经典的存取款问题,要求两个线程存款,两个线程取款,账户里有余额的时候只能取款,没余额的时候只能存款,存取款金额相同 ...
- 线程间通信 GET POST
线程间通信有三种方法:NSThread GCD NSOperation 进程:操作系统里面每一个app就是一个进程. 一个进程里面可以包含多个线程,并且我们每一个app里面有且仅有一 ...
- Java多线程编程核心技术---线程间通信(二)
通过管道进行线程间通信:字节流 Java提供了各种各样的输入/输出流Stream可以很方便地对数据进行操作,其中管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据,一个线程发送 ...
- Java多线程编程核心技术---线程间通信(一)
线程是操作系统中独立的个体,但这些个体如果不经过特殊处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一.线程间通信可以使系统之间的交互性更强大,在大大提高CPU利用率的同时还会使程序员对各 ...
随机推荐
- 老李谈HTTP1.1的长连接 2
HTTP1.1的长连接 但是HTTP1.1开始默认建立的是长连接,即一旦浏览器发起HTTP请求,建立的连接不会请求应答之后立刻断掉. 1. 一个复杂的具备很多HTTP资源的网页会建立多少TCP连接,如 ...
- 老李推荐:第6章1节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览 2
事件要到那里去? 每个事件源处理类都维护着一个自己的事件队列, 在Monkey中叫做CommandQueue,里面装的是每个具体的MonkeyEvent事件.当来自网络的字串命令被翻译成对应的Monk ...
- GPIO寄存器
GPIO寄存器描述 <STM32参考手册中文-p75> 1.端口配置低寄存器(GPIOx_CRL)(x = A...E)2.端口配置高寄存器(GPIOx_CRH)(x = A...E) 3 ...
- CSS3实现一束光划过图片、和文字特效
在打折图标里面 实现一道白光划过的动画效果 css: <!DOCTYPE html><html><head><meta charset="utf-8 ...
- ios 添加工程依赖只能生成Generic Xcode Archive 文件原因
问题说明:工程引用了外部类库, 默认生成的archive是 Generic Xcode Archive 格式的 无法发布和生成ipa文件. 解决处理: 1.将Build Settings->De ...
- Code First约定-Fluent API配置
转自:http://blog.163.com/m13864039250_1/blog/static/2138652482015283397609/ 用Fluent API 配置/映射属性和类型 简介 ...
- NOIP2001T4car的旅行计划
洛谷传送门 一看数据就是floyed(毕竟年代久远),然而建图不是那么好贱好建,只知道三个机场,需要判断斜边来求第4个机场坐标. 往后一些麻烦的建图. 最后floyed就好. --代码 #includ ...
- webpack搭建服务器,随时修改刷新
前提:1.对webpack有一定了解,本文不做介绍 2.安装node.js 手把手操作: 1.创建一个名为webpack-server的文件夹(随便取的) 2.cd到当前文件夹:cd webpack- ...
- 告别findViewById(),ButterKnife,使用Google Data Binding Library(1)
Data Binding Library 用数据绑定编写声名性布局,可以最大限度的减少findViewById(),setOnClickListener()之类的代码.并且比起findViewById ...
- Unity3D 正六边形,环状扩散,紧密分布,的程序
最近在做一个正六边形的游戏,被一开始的布局难倒了. 需求:中心有个正六边形,输入围绕中心扩散的环数,自动创建和摆放. 大概就是这样的吧,我觉得这个非常轻松的就可以搞定了.啊~~~~~啊~~~ 五环~~ ...