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 代码:

  1. package mark.zhang;
  2. import android.app.Activity;
  3. import android.content.Intent;
  4. import android.os.Bundle;
  5. import android.util.Log;
  6. import android.view.View;
  7. import android.view.View.OnClickListener;
  8. public class ServiceToastActivity extends Activity {
  9. @Override
  10. public void onCreate(Bundle savedInstanceState) {
  11. super.onCreate(savedInstanceState);
  12. setContentView(R.layout.main);
  13. Log.d("mark", "activity: " + "\n" + "当前线程名称:"
  14. + Thread.currentThread().getName() + "," + "当前线程名称:"
  15. + Thread.currentThread().getId());
  16. // 启动服务
  17. findViewById(R.id.button_startservice).setOnClickListener(
  18. new OnClickListener() {
  19. @Override
  20. public void onClick(View v) {
  21. Intent intent = new Intent(ServiceToastActivity.this,
  22. MyService.class);
  23. startService(intent);
  24. }
  25. });
  26. // 停止服务
  27. findViewById(R.id.button_stopservice).setOnClickListener(
  28. new OnClickListener() {
  29. @Override
  30. public void onClick(View v) {
  31. Intent intent = new Intent(ServiceToastActivity.this,
  32. MyService.class);
  33. stopService(intent);
  34. }
  35. });
  36. }
  37. }

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

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

  1. package mark.zhang;
  2. import java.util.Timer;
  3. import java.util.TimerTask;
  4. import android.app.Service;
  5. import android.content.Intent;
  6. import android.os.Handler;
  7. import android.os.IBinder;
  8. import android.os.Looper;
  9. import android.util.Log;
  10. import android.widget.Toast;
  11. public class MyService extends Service {
  12. private Handler handler = null;
  13. private Timer timer;
  14. @Override
  15. public IBinder onBind(Intent intent) {
  16. return null;
  17. }
  18. @Override
  19. public void onCreate() {
  20. super.onCreate();
  21. new Thread(new Runnable() {
  22. public void run() {
  23. Log.d("mark", "Service in Thread: " + "\n" + "当前线程名称:"
  24. + Thread.currentThread().getName() + "," + "当前线程名称:"
  25. + Thread.currentThread().getId());
  26. Toast.makeText(MyService.this, "Service中子线程启动!", Toast.LENGTH_LONG).show();//程序奔溃
  27. }
  28. }).start();
  29. }
  30. @Override
  31. public int onStartCommand(Intent intent, int flags, int startId) {
  32. Log.d("mark", "Service: " + "\n" + "当前线程名称:"
  33. + Thread.currentThread().getName() + "," + "当前线程名称:"
  34. + Thread.currentThread().getId());
  35. Toast.makeText(this, "启动服务成功!", Toast.LENGTH_LONG).show();
  36. return super.onStartCommand(intent, flags, startId);
  37. }
  38. @Override
  39. public void onDestroy() {
  40. super.onDestroy();
  41. }
  42. }

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

  1. D/mark    (  310): activity:
  2. D/mark    (  310): 当前线程名称:main,当前线程名称:1
  3. D/mark    (  310): Service:
  4. D/mark    (  310): 当前线程名称:main,当前线程名称:1
  5. D/mark    (  310): Service in Thread:
  6. D/mark    (  310): 当前线程名称:Thread-8,当前线程名称:8

从打印信息可以看出,Service 与 Activity 在同一个线程程(main线程)。

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

  1. 02 05:25:32.828: ERROR/AndroidRuntime(325): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

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

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

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

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

  1. package mark.zhang;
  2. import java.util.Timer;
  3. import java.util.TimerTask;
  4. import android.app.Service;
  5. import android.content.Intent;
  6. import android.os.IBinder;
  7. import android.util.Log;
  8. import android.widget.Toast;
  9. public class MyService extends Service {
  10. private Timer timer;
  11. private TimerTask task = new TimerTask() {
  12. @Override
  13. public void run() {
  14. Log.d("mark", "task: " + "\n" + "当前线程名称:"
  15. + Thread.currentThread().getName() + "," + "当前线程名称:"
  16. + Thread.currentThread().getId());
  17. Toast.makeText(getApplicationContext(), "呵呵,您好!",
  18. Toast.LENGTH_SHORT).show();
  19. }
  20. };
  21. @Override
  22. public IBinder onBind(Intent intent) {
  23. return null;
  24. }
  25. @Override
  26. public void onCreate() {
  27. super.onCreate();
  28. // 当前Task中线程的名称为myservice
  29. timer = new Timer("myservice");
  30. }
  31. @Override
  32. public int onStartCommand(Intent intent, int flags, int startId) {
  33. // 100ms之后,每隔5000ms启动定时器
  34. timer.scheduleAtFixedRate(task, 100, 5000);
  35. return super.onStartCommand(intent, flags, startId);
  36. }
  37. @Override
  38. public void onDestroy() {
  39. super.onDestroy();
  40. timer.cancel();
  41. }
  42. }

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

打印信息如下:

  1. D/mark    (  441): activity:
  2. D/mark    (  441): 当前线程名称:main,当前线程名称:1
  3. D/mark    (  441): task:
  4. D/mark    (  441): 当前线程名称:myservice,当前线程名称:8
  5. D/mark    (  441): task:
  6. D/mark    (  441): 当前线程名称:myservice,当前线程名称:8
  7. D/mark    (  441): task:
  8. D/mark    (  441): 当前线程名称:myservice,当前线程名称:8
  9. D/mark    (  441): task:
  10. D/mark    (  441): 当前线程名称:myservice,当前线程名称:8
  11. D/mark    (  441): task:
  12. D/mark    (  441): 当前线程名称:myservice,当前线程名称:8

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

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

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

  1. package mark.zhang;
  2. import java.util.Timer;
  3. import java.util.TimerTask;
  4. import android.app.Service;
  5. import android.content.Intent;
  6. import android.os.Handler;
  7. import android.os.IBinder;
  8. import android.os.Looper;
  9. import android.util.Log;
  10. import android.widget.Toast;
  11. public class MyService extends Service {
  12. private Handler handler;
  13. private Timer timer;
  14. private TimerTask task = new TimerTask() {
  15. @Override
  16. public void run() {
  17. handler.post(new Runnable() {//**********在线程中使用Toast
  18. @Override
  19. public void run() {
  20. Toast.makeText(getApplicationContext(), "呵呵,您好!",
  21. Toast.LENGTH_SHORT).show();
  22. Log.d("mark", "service in Handler run: " + "\n" + "当前线程名称:"
  23. + Thread.currentThread().getName() + ","
  24. + "当前线程名称:" + Thread.currentThread().getId());
  25. }
  26. });
  27. }
  28. };
  29. @Override
  30. public IBinder onBind(Intent intent) {
  31. return null;
  32. }
  33. @Override
  34. public void onCreate() {
  35. super.onCreate();
  36. // 为当前线程获得looper
  37. handler = new Handler(Looper.getMainLooper());
  38. // 当前Task中线程的名称为myservice
  39. timer = new Timer("myservice");
  40. }
  41. @Override
  42. public int onStartCommand(Intent intent, int flags, int startId) {
  43. // 100ms之后,每隔5000ms启动定时器
  44. timer.scheduleAtFixedRate(task, 100, 5000);
  45. return super.onStartCommand(intent, flags, startId);
  46. }
  47. @Override
  48. public void onDestroy() {
  49. super.onDestroy();
  50. timer.cancel();
  51. }
  52. }

运行程序,一切ok!我们还是分析一下打印信息吧!

  1. D/mark    (  495): activity:
  2. D/mark    (  495): 当前线程名称:main,当前线程名称:1
  3. D/mark    (  495): service in Handler run:
  4. D/mark    (  495): 当前线程名称:main,当前线程名称:1
  5. D/mark    (  495): service in Handler run:
  6. D/mark    (  495): 当前线程名称:main,当前线程名称:1
  7. D/mark    (  495): service in Handler run:
  8. D/mark    (  495): 当前线程名称:main,当前线程名称:1

我们使用 Handler 的 post(Runnable r) 方法,在 run 方法中 Toast,根据 Timer 的定时每隔5秒就会 Toast 一下。

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

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

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

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

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

  1. Looper.prepare();
  2. showMyDialog();
  3. Looper.loop();

或者使用 Handler,呵呵.

绝大部分参考:网站,小部分是自己在使用过程中的理解,感谢原作者!

Handler: Service中使用Toast的更多相关文章

  1. 在IntentService中使用Toast与在Service中使用Toast的异同

    1. 表象 Service中能够正常显示Toast,IntentService中不能正常显示Toast.在2.3系统上,不显示toast,在4.3系统上,toast显示.可是不会消失. 2. 问题分析 ...

  2. [Android] Service和IntentService中显示Toast的区别

    1. 表象     Service中可以正常显示Toast,IntentService中不能正常显示Toast,在2.3系统上,不显示toast,在4.3系统上,toast显示,但是不会消失. 2. ...

  3. (原创)在service中定时执行网络操作的几点说明

    执行网络操作是耗时操作,即便是在service中也要放到子线程中执行 这里我用到了async-http-client框架来执行异步请求操作 计时用的java原生Timer和TimerTask类 本来这 ...

  4. Android 高级UI设计笔记17:Android在非UI线程中显示Toast

    1. 子线程的Toast怎么显示不出来? 因为Toast在创建的时候会依赖于一个Handler,并且一个Handler是需要有一个Looper才能够创建,而普通的线程是不会自动去创建一个Looper对 ...

  5. 如何在子线程中使用Toast和更新UI

    因为没一个Looper处理消息循环,所以子线程中无法使用Toast 方法: Looper.prepare(); Toast.makeText(getActivity(),"刷到底啦" ...

  6. Android开发之在子线程中使用Toast

    在子线程中使用Toast的时候,出现Force close. 错误提示:Can't create handler inside thread that has not called Looper.pr ...

  7. Android悬浮框,在Service中打开悬浮窗;在Service中打开Dialog;

    文章介绍了如何在Service中显示悬浮框,在Service中弹出Dialog,在Service中做耗时的轮询操作: 背景需求: 公司的项目现在的逻辑是这样的:发送一个指令,然后3秒一次轮询去查询这个 ...

  8. Toast的用法(可以设置显示时间,自定义布局的,线程中的Toast)

           自定义的Toast类 布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLa ...

  9. Android线程中使用Toast、dialog、loading

    代码改变世界 Android线程中使用Toast.dialog.loading Loading: Thread t1 = new Thread(new Runnable() { @Override p ...

随机推荐

  1. chef语法和案例

    1. 添加用户 $ vim create_user.rb -----------------------------------> user 'charlie' do //创建一个uid为88的 ...

  2. Spring的概况

    ----------------siwuxie095 Spring 的简介 Spring 是一个轻量级 控制反转(IoC) 和 面向切面(AOP) 的容器框架 年,它是为了解决企业应用开发的复杂性而诞 ...

  3. URL中#符号的作用

    转自http://blog.sina.com.cn/s/blog_6f9eb2dd0100sk97.html 一.#的涵义 #代表网页中的一个位置.其右面的字符,就是该位置的标识符.比如,       ...

  4. HTML5新增的结构元素

    HTML5的结构 一:新增的主体结构元素 在HTML5中,为了使文档的结构更加清晰明确,追加了几个与页眉,页脚内容区块等文档结构相关联的结构元素. 1.1article元素 article元素代表文档 ...

  5. Ubuntu 使用 heirloom-mail 调用外部邮箱 SMTP 服务器发送邮件

    使用本地服务发邮件,经常被过滤掉而且占用资源,发送成功率不高.所以使用外部SMTP服务器发送邮件成为了需求. SMTP认证的目的是为了使用户避免受到垃圾邮件的侵扰,简单地说就是要求必须在提供了账户名和 ...

  6. Linux操作系统的内存使用方法详细解析

    我是一名程序员,那么我在这里以一个程序员的角度来讲解Linux内存的使用. 一提到内存管理,我们头脑中闪出的两个概念,就是虚拟内存,与物理内存.这两个概念主要来自于linux内核的支持. Linux在 ...

  7. eigen3.3.3+opencv3.2跑orbslam3数据集出现usleep错误

    cd ORB_SLAM2 chmod +x build.sh ./build.sh的时候出现错误:/home/zhao/ORB_SLAM2-master/src/System.cc:236:28: e ...

  8. TMF SID中的角色模式

    角色模式 Copyright © TeleManagement Forum 2013. All Rights Reserved. This document and translations of i ...

  9. go培训课程都学什么?xorm框架学习系列(二):xorm结构体映射规则和表操作

    上节内容我们学习了基本的xorm框架的知识和基础配置的相关信息.本节课内容我们继续学习相关的知识和相关操作. 名称映射规则 名称映射规则主要负责结构体名称到表名和结构体field到表字段的名称映射. ...

  10. 深入解析Android Design包——Behavior

    已经说过了,在AndroidDesign包中主要有两个核心概念:一是NestedScroll,另一个就是Behavior. 相比于NestedScroll这个概念来说,Behavior分析起来会难很多 ...