Handler: Service中使用Toast
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 {
- @Override
- public 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() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(ServiceToastActivity.this,
- MyService.class);
- startService(intent);
- }
- });
- // 停止服务
- findViewById(R.id.button_stopservice).setOnClickListener(
- new OnClickListener() {
- @Override
- public 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;
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
- @Override
- public 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();
- }
- @Override
- public 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);
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
- }
- }
点击界面的“启动服务”,打印信息:
- D/mark ( 310): activity:
- D/mark ( 310): 当前线程名称:main,当前线程名称:1
- D/mark ( 310): Service:
- D/mark ( 310): 当前线程名称:main,当前线程名称:1
- D/mark ( 310): Service in Thread:
- D/mark ( 310): 当前线程名称:Thread-8,当前线程名称:8
从打印信息可以看出,Service 与 Activity 在同一个线程程(main线程)。
如果你复制我的代码实际运行一下,你会发现子线程中的 Toast 根本没有起作用,并且程序会崩溃,显示异常如下:

- 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中的代码修改如下:
- 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();
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() {
- @Override
- public void run() {
- Log.d("mark", "task: " + "\n" + "当前线程名称:"
- + Thread.currentThread().getName() + "," + "当前线程名称:"
- + Thread.currentThread().getId());
- Toast.makeText(getApplicationContext(), "呵呵,您好!",
- Toast.LENGTH_SHORT).show();
- }
- };
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
- @Override
- public void onCreate() {
- super.onCreate();
- // 当前Task中线程的名称为myservice
- timer = new Timer("myservice");
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- // 100ms之后,每隔5000ms启动定时器
- timer.scheduleAtFixedRate(task, 100, 5000);
- return super.onStartCommand(intent, flags, startId);
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
- timer.cancel();
- }
- }
启动服务之后,启动 TimerTask,在 TimerTask 中每隔5秒中 Toast 一下,在终止服务的时候取消 Timer。
打印信息如下:
- D/mark ( 441): activity:
- D/mark ( 441): 当前线程名称:main,当前线程名称:1
- D/mark ( 441): task:
- D/mark ( 441): 当前线程名称:myservice,当前线程名称:8
- D/mark ( 441): task:
- D/mark ( 441): 当前线程名称:myservice,当前线程名称:8
- D/mark ( 441): task:
- D/mark ( 441): 当前线程名称:myservice,当前线程名称:8
- D/mark ( 441): task:
- D/mark ( 441): 当前线程名称:myservice,当前线程名称:8
- D/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() {
- @Override
- public void run() {
- handler.post(new Runnable() {//**********在线程中使用Toast
- @Override
- public void run() {
- Toast.makeText(getApplicationContext(), "呵呵,您好!",
- Toast.LENGTH_SHORT).show();
- Log.d("mark", "service in Handler run: " + "\n" + "当前线程名称:"
- + Thread.currentThread().getName() + ","
- + "当前线程名称:" + Thread.currentThread().getId());
- }
- });
- }
- };
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
- @Override
- public void onCreate() {
- super.onCreate();
- // 为当前线程获得looper
- handler = new Handler(Looper.getMainLooper());
- // 当前Task中线程的名称为myservice
- timer = new Timer("myservice");
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- // 100ms之后,每隔5000ms启动定时器
- timer.scheduleAtFixedRate(task, 100, 5000);
- return super.onStartCommand(intent, flags, startId);
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
- timer.cancel();
- }
- }
运行程序,一切ok!我们还是分析一下打印信息吧!
- D/mark ( 495): activity:
- D/mark ( 495): 当前线程名称:main,当前线程名称:1
- D/mark ( 495): service in Handler run:
- D/mark ( 495): 当前线程名称:main,当前线程名称:1
- D/mark ( 495): service in Handler run:
- D/mark ( 495): 当前线程名称:main,当前线程名称:1
- D/mark ( 495): service in Handler run:
- 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 子线程中显示对话框,可以这样做:
- Looper.prepare();
- showMyDialog();
- Looper.loop();
或者使用 Handler,呵呵.
绝大部分参考:网站,小部分是自己在使用过程中的理解,感谢原作者!
Handler: Service中使用Toast的更多相关文章
- 在IntentService中使用Toast与在Service中使用Toast的异同
1. 表象 Service中能够正常显示Toast,IntentService中不能正常显示Toast.在2.3系统上,不显示toast,在4.3系统上,toast显示.可是不会消失. 2. 问题分析 ...
- [Android] Service和IntentService中显示Toast的区别
1. 表象 Service中可以正常显示Toast,IntentService中不能正常显示Toast,在2.3系统上,不显示toast,在4.3系统上,toast显示,但是不会消失. 2. ...
- (原创)在service中定时执行网络操作的几点说明
执行网络操作是耗时操作,即便是在service中也要放到子线程中执行 这里我用到了async-http-client框架来执行异步请求操作 计时用的java原生Timer和TimerTask类 本来这 ...
- Android 高级UI设计笔记17:Android在非UI线程中显示Toast
1. 子线程的Toast怎么显示不出来? 因为Toast在创建的时候会依赖于一个Handler,并且一个Handler是需要有一个Looper才能够创建,而普通的线程是不会自动去创建一个Looper对 ...
- 如何在子线程中使用Toast和更新UI
因为没一个Looper处理消息循环,所以子线程中无法使用Toast 方法: Looper.prepare(); Toast.makeText(getActivity(),"刷到底啦" ...
- Android开发之在子线程中使用Toast
在子线程中使用Toast的时候,出现Force close. 错误提示:Can't create handler inside thread that has not called Looper.pr ...
- Android悬浮框,在Service中打开悬浮窗;在Service中打开Dialog;
文章介绍了如何在Service中显示悬浮框,在Service中弹出Dialog,在Service中做耗时的轮询操作: 背景需求: 公司的项目现在的逻辑是这样的:发送一个指令,然后3秒一次轮询去查询这个 ...
- Toast的用法(可以设置显示时间,自定义布局的,线程中的Toast)
自定义的Toast类 布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLa ...
- Android线程中使用Toast、dialog、loading
代码改变世界 Android线程中使用Toast.dialog.loading Loading: Thread t1 = new Thread(new Runnable() { @Override p ...
随机推荐
- ssh动态转发小记
ssh,一般常用来做远程登录管理,也就是连上远程机器,得到一个shell,然后交互式地在上面敲命令-看结果-再敲命令. 偶尔也会用在脚本里,做些自动化批处理上传下载的操作,但本质上也是用shell来执 ...
- 1、Question: prep_reads.info vs. align_summary.txt
###参考:https://www.biostars.org/p/163356/ used TopHat to map my reads against their relative referenc ...
- 《精通Spring4.X企业应用开发实战》读后感第七章(AOP基础知识、jdk动态代理,CGLib动态代理)
- Robots.txt在项目中的运用
在开发公司一个项目的过程中,有这样一个需求 该网站上面有一个search功能,可以search该网站上的任何包括特定内容的网页 现在有一个需求,就是针对几个特定的页面,我们希望网站上的search功能 ...
- 连接mysql时报:message from server: "Host '192.168.76.89' is not allowed to connect to this MySQL server 处理方案
1.先用localhost方式连接到MySQL数据库,然后使用MySQL自带的数据库mysql; use mysql: 2.执行:select host from user where user = ...
- Dapper.Common基于Dapper的开源LINQ超轻量扩展
Dapper.Common Dapper.Common是基于Dapper的LINQ实现,支持.net core,遵循Linq语法规则.链式调用.配置简单.上手快,支持Mysql,Sqlserver(目 ...
- 转:基础篇|PHP如何解决网站大流量和高并发
基础篇 高并发架构基础概念和优化思路 高并发架构相关概念 并发,在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程 ...
- 浅谈JavaScript -- 正则表达式
什么是正则表达式? 正则表达式是由一个字符序列形成的搜索模式.可用于文本搜索和文本替换. 语法:/正则表达式主体/修饰符(可选) var patt=new RegExp(pattern,modifie ...
- 【转】如何解决C盘根目录无法创建或写入文件
源地址:http://blog.csdn.net/xinke453/article/details/7496545
- 解读人:李琼,Metabolic profiling of tumors, sera and skeletal muscles from an orthotopic murine model of gastric cancer associated-cachexia(胃癌相关恶病质的原位小鼠模型中肿瘤,血清和骨骼肌的代谢谱分析)
发表时间:(2019年4月) IF:3.950 单位: 厦门大学 厦门理工大学 物种:小鼠的肿瘤,血清和骨骼肌 技术:核磁共振代谢组学分析 一. 概述:(用精炼的语言描述文章的整体思路及结果) 本研究 ...