关于 android 的线程模型,建议阅读http://blog.csdn.net/androidbluetooth/article/details/6547166,这只是一个建议,你看不看这篇博客都不会影响阅读本篇博客。

Handler 的使用在 android App 开发中用的颇多,它的作用也很大,使用 Handler 一般也会使用到多线程,相信大家对 Handler 不会陌生,在这篇博客中,重点说一下 android 组件之一 Service 与 TaskTimer 结合 Handler 使用,共享之!


*****************************************

**************************************************************************************************************************

*****************************************


阅读这篇博客,需要你知道的知识:


<1> 知道在 Activity 中如何启动、停止 Service 以及 Service 的生命周期。


<2> 使用过 TimerTask 的Api,不过这个不难,如果之前没有接触过现在拿出几分钟学习一下吧!


<3> Handler 基本用法,推荐博客:http://blog.csdn.net/androidbluetooth/article/details/6384641


<4> Looper 基本用法,推荐下载:http://download.csdn.net/detail/AndroidBluetooth/3650576好好看看,肯定对你有用!


好嘞,开始说这篇博客的内容。

界面很简单,就是两个Button,启动之后,效果如下:




Activity 代码:

package mark.zhang;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;public class ServiceToastActivity extends Activity {@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);Log.d("mark", "activity: " + "\n" + "当前线程名称:"+ Thread.currentThread().getName() + "," + "当前线程名称:"+ Thread.currentThread().getId());// 启动服务findViewById(R.id.button_startservice).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(ServiceToastActivity.this,MyService.class);startService(intent);}});// 停止服务findViewById(R.id.button_stopservice).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(ServiceToastActivity.this,MyService.class);stopService(intent);}});}}

上述代码就是给两个 Button 设置监听器,启动和停止服务。

然后,在 Service 中 开启一个线程,并在该线程中 Toast 一下!代码如下:

package mark.zhang;import java.util.Timer;import java.util.TimerTask;import android.app.Service;import android.content.Intent;import android.os.Handler;import android.os.IBinder;import android.os.Looper;import android.util.Log;import android.widget.Toast;public class MyService extends Service {private Handler handler = null;private Timer timer;@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();new Thread(new Runnable() {public void run() {Log.d("mark", "Service in Thread: " + "\n" + "当前线程名称:"+ Thread.currentThread().getName() + "," + "当前线程名称:"+ Thread.currentThread().getId());Toast.makeText(MyService.this, "Service中子线程启动!", Toast.LENGTH_LONG).show();}}).start();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d("mark", "Service: " + "\n" + "当前线程名称:"+ Thread.currentThread().getName() + "," + "当前线程名称:"+ Thread.currentThread().getId());Toast.makeText(this, "启动服务成功!", Toast.LENGTH_LONG).show();return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {super.onDestroy();}}

点击界面的“启动服务”,打印信息:

D/mark    (  310): activity: D/mark    (  310): 当前线程名称:main,当前线程名称:1D/mark    (  310): Service: D/mark    (  310): 当前线程名称:main,当前线程名称:1D/mark    (  310): Service in Thread: D/mark    (  310): 当前线程名称:Thread-8,当前线程名称:8
从打印信息可以看出,Service 与 Activity 在同一个线程程(main线程)。

如果你复制我的代码实际运行一下,你会发现子线程中的 Toast 根本没有起作用,并且程序会崩溃,显示异常如下:



10-02 05:25:32.818: ERROR/AndroidRuntime(325): Uncaught handler: thread Thread-8 exiting due to uncaught exception10-02 05:25:32.828: ERROR/AndroidRuntime(325): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()10-02 05:25:32.828: ERROR/AndroidRuntime(325):     at android.os.Handler.<init>(Handler.java:121)10-02 05:25:32.828: ERROR/AndroidRuntime(325):     at android.widget.Toast.<init>(Toast.java:68)10-02 05:25:32.828: ERROR/AndroidRuntime(325):     at android.widget.Toast.makeText(Toast.java:231)10-02 05:25:32.828: ERROR/AndroidRuntime(325):     at mark.zhang.MyService$1.run(MyService.java:34)10-02 05:25:32.828: ERROR/AndroidRuntime(325):     at java.lang.Thread.run(Thread.java:1096)

这下应该明白,在子线程直接 Toast 是错误的!根据提示信息,我们需要调用 Looper.prepare(),根据 Looper 的 Api 说明,我们还应该调用 Looper.loop(),那麽我们修改一下代码,将 new Thread中的代码修改如下:

new Thread(new Runnable() {public void run() {Log.d("mark", "Service in Thread: " + "\n" + "当前线程名称:"+ Thread.currentThread().getName() + "," + "当前线程名称:"+ Thread.currentThread().getId());Looper.prepare();Toast.makeText(MyService.this, "Service中子线程启动!", Toast.LENGTH_LONG).show();Looper.loop();}}).start();

该部分完整示例代码下载:http://download.csdn.net/detail/AndroidBluetooth/3653420


ok,这次一切正常。关于 Looper 作用在 http://download.csdn.net/detail/AndroidBluetooth/3650576中说的很明白,这里不再赘述!


接着看看在Service中如何使用 TimerTask 以及 Toast。Activity 的代码不变,修改 Service 代码:

package mark.zhang;import java.util.Timer;import java.util.TimerTask;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.util.Log;import android.widget.Toast;public class MyService extends Service {private Timer timer;private TimerTask task = new TimerTask() {@Overridepublic void run() {Log.d("mark", "task: " + "\n" + "当前线程名称:"+ Thread.currentThread().getName() + "," + "当前线程名称:"+ Thread.currentThread().getId());Toast.makeText(getApplicationContext(), "呵呵,您好!",Toast.LENGTH_SHORT).show();}};@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();// 当前Task中线程的名称为myservicetimer = new Timer("myservice");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 100ms之后,每隔5000ms启动定时器timer.scheduleAtFixedRate(task, 100, 5000);return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {super.onDestroy();timer.cancel();}}

启动服务之后,启动 TimerTask,在 TimerTask 中每隔5秒中 Toast 一下,在终止服务的时候取消 Timer。

打印信息如下:

D/mark    (  441): activity: D/mark    (  441): 当前线程名称:main,当前线程名称:1D/mark    (  441): task: D/mark    (  441): 当前线程名称:myservice,当前线程名称:8D/mark    (  441): task: D/mark    (  441): 当前线程名称:myservice,当前线程名称:8D/mark    (  441): task: D/mark    (  441): 当前线程名称:myservice,当前线程名称:8D/mark    (  441): task: D/mark    (  441): 当前线程名称:myservice,当前线程名称:8D/mark    (  441): task: D/mark    (  441): 当前线程名称:myservice,当前线程名称:8

可以看出,TimerTask开启一个线程(名称为myservice,线程id是8),按照原来的想法一样,每隔五秒 TimerTask 的run() 方法会执行一次,直到 “停止服务”,但是 Toast 并没有起作用。看来我们需要修改代码。

当然,可以像上面那样使用 Looper 的两个静态方法prepare()、loop(),可以保证Toast 完美运行,但是肯定还有其它办法,仔细看来,呵呵!

修改 Service 代码,这次主要使用 Handler:

package mark.zhang;import java.util.Timer;import java.util.TimerTask;import android.app.Service;import android.content.Intent;import android.os.Handler;import android.os.IBinder;import android.os.Looper;import android.util.Log;import android.widget.Toast;public class MyService extends Service {private Handler handler;private Timer timer;private TimerTask task = new TimerTask() {@Overridepublic void run() {handler.post(new Runnable() {@Overridepublic void run() {Toast.makeText(getApplicationContext(), "呵呵,您好!",Toast.LENGTH_SHORT).show();Log.d("mark", "service in Handler run: " + "\n" + "当前线程名称:"+ Thread.currentThread().getName() + ","+ "当前线程名称:" + Thread.currentThread().getId());}});}};@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();// 为当前线程获得looperhandler = new Handler(Looper.getMainLooper());// 当前Task中线程的名称为myservicetimer = new Timer("myservice");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 100ms之后,每隔5000ms启动定时器timer.scheduleAtFixedRate(task, 100, 5000);return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {super.onDestroy();timer.cancel();}}
运行程序,一切ok!我们还是分析一下打印信息吧!

D/mark    (  495): activity: D/mark    (  495): 当前线程名称:main,当前线程名称:1D/mark    (  495): service in Handler run: D/mark    (  495): 当前线程名称:main,当前线程名称:1D/mark    (  495): service in Handler run: D/mark    (  495): 当前线程名称:main,当前线程名称:1D/mark    (  495): service in Handler run: D/mark    (  495): 当前线程名称:main,当前线程名称:1
我们使用 Handler 的 post(Runnable r) 方法,在 run 方法中 Toast,根据 Timer 的定时每隔5秒就会 Toast 一下。


该部分完整代码示例,下载地址:http://download.csdn.net/detail/AndroidBluetooth/3653469


这里还需要提醒大家一句:在子线程中我们不可以直接 new Handler(),但是在 TimerTask 的 run() 方法中直接 new Handler() 是没有问题的。TimerTask 的确开启一个子线程,但是为什么在这里可以直接创建 Handler 对象呢?


如果,你有兴趣可以继续:

学习 android 的 Looper 源码以及 TimerTask 设计理念。


多说一句:在 Service 中不可以显示对话框,如果想通过 Service 来显示对话框需要使用 Handler 通知 Activity 来显示对话框。

在 Activity 子线程中显示对话框,可以这样做:

Looper.prepare();showMyDialog();Looper.loop();
或者使用 Handler,呵呵!

好嘞,这篇博客只是借助 Service 来说明在子线程中如何使用TimerTask、Toast,在Activity 中类似,说到这里吧!








更多相关文章

  1. android 邮件开发(javax.mail)
  2. android缩放动画的两种实现方法
  3. Android客户端与java服务端AES加解密
  4. Android(安卓)Studio JNI开发
  5. Android(安卓)游戏开发中横竖屏切换问题
  6. Android中的消息机制-源码分析
  7. Android(安卓)TableLayout中的使用说明
  8. LoganSquare——快速解析和序列化JSON对象的Android类库
  9. Android架构实例分析之注册hello HAL的JNI方法表

随机推荐

  1. SQL Server游标的使用/关闭/释放/优化小
  2. Sql学习第四天——SQL 关于with cube,wit
  3. sql server 还原数据库时提示数据库正在
  4. sqlserver中delete、update中使用表别名
  5. Sql学习第三天——SQL 关于CTE(公用表达
  6. Sql学习第三天——SQL 关于with ties介绍
  7. Sql学习第二天——SQL DML与CTE概述
  8. Sql学习第一天——SQL 将变量定义为Table
  9. Sql学习第一天——SQL 练习题(建表/sql语
  10. Sql学习第一天——SQL UNION 和 UNION AL