Android:Service组件及其简单应用
Posted 晚风Sensei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android:Service组件及其简单应用相关的知识,希望对你有一定的参考价值。
Service 组件
- 可在后台执行长时间运行操作而不提供界面的应用组件
- 用户切换到其他应用Service也在后台运行
- 不能主动运行,需要调用方法来运行Service
创建
- 创建继承Service类的类
public class MyService1 extends Service
private static final String TAG = MyService1.class.getName();
@Nullable
@Override
public IBinder onBind(Intent intent)
return null;
//生命周期相关函数
@Override
public void onCreate()
super.onCreate();
Log.i(TAG, "onCreate: ");
@Override
public int onStartCommand(Intent intent, int flags, int startId)
Log.i(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
@Override
public void onDestroy()
Log.i(TAG, "onDestroy: ");
super.onDestroy();
@Override
public boolean onUnbind(Intent intent)
Log.i(TAG, "onUnbind: ");
return super.onUnbind(intent);
- 在Mainifast中注册
<service android:name=".MyService1"/>
类别
-
Context.startService()
-
Context.bindService()
不同启动方式对应不同生命周期
startService(Intent)
startService启动的服务默认无限期执行(可以通过Context的stopService或Service的stopSelf方法停止运行)
//开启Service
Intent intent = new Intent();
intent.setClass(this, MyService1.class);
startService(intent);
如服务未被创建,会调用onCreate和OnStartCommand两个生命周期函数。若服务已被创建,则只调用onStartCommand
//停止Service
Intent intent = new Intent();
intent.setClass(this, MyService1.class);
stopService(intent);
停止服务时,调用onDestroy生命周期函数,服务被销毁。
bindService(Intent service, ServiceConnection conn, int flag)
bindService启动的服务在调用者和服务之间是典型的client-server的接口,即调用者是客户端,service是服务端,service就一个,但是连接绑定到service上面的客户端client可以是一个或多个。
这里特别要说明的是,这里所提到的client指的是组件,比如某个Activity。
当client销毁的时候,client会自动与Service解除绑定,当然client也可以通过明确调用Context的unbindService方法与Service解除绑定。
当没有任何client与Service绑定的时候,Service会自行销毁
若有client与Service绑定,则无法使用stopService销毁
//绑定服务
//若该服务未被创建则创建一个新服务
Intent intent = new Intent();
intent.setClass(this, MyService1.class);
//connection生命为MainActivity的成员变量
// private ServiceConnection connection = null;
connection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName name, IBinder service)
// when Service connect
@Override
public void onServiceDisconnected(ComponentName name)
//when Service disconnect
;
// flag = BIND_AUTO_CREATE 自动创建Service
bindService(intent, connection, BIND_AUTO_CREATE);
//解绑服务
unbindService(connection);
使用Service组件模拟下载app
1.新建项目并创建MyService类继承自Service类
public class MyService extends Service
@Nullable
@Override
public IBinder onBind(Intent intent)
Log.i(TAG, "onBind: ");
return null;
@Override
public void onCreate()
Log.i(TAG, "onCreate: ");
super.onCreate();
@Override
public int onStartCommand(Intent intent, int flags, int startId)
Log.i(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
@Override
public boolean onUnbind(Intent intent)
Log.i(TAG, "onUnbind: ");
return super.onUnbind(intent);
@Override
public void onDestroy()
Log.i(TAG, "onDestroy: ");
super.onDestroy();
这里一开始最好在每个重要的周期函数中都添加一个Log.i打印语句,测试运行Service服务的各个生命周期是否正常。
2.在MainAcitvity的onCreate中调用bindService绑定服务
//绑定服务
Intent bind_intent = new Intent();
bind_intent.setClass(this, MyService.class);
ServiceConnection connection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName name, IBinder service)
localBinder = (MyService.LocalBinder) service;
Log.i(TAG, "onServiceConnected: ");
@Override
public void onServiceDisconnected(ComponentName name)
Log.i(TAG, "onServiceDisconnected: ");
;
bindService(bind_intent, connection, BIND_AUTO_CREATE);
3.在activity_main.xml布局中添加两个按钮,分别为开始下载和取消下载,注意自定义的id和onClick点击事件函数名
<Button
android:id="@+id/DownloadButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="50dp"
android:layout_marginBottom="20dp"
android:layout_gravity="center_horizontal"
android:onClick="StartDownload"
android:text="开始下载" />
<Button
android:id="@+id/CancelDownloadButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:onClick="CancelDownload"
android:text="取消下载" />
4.在MainAcitivity.java中写入这两个函数
由于下载的程序是存放在Service中的,所以我们在点击事件StartDownload()中调用startService(),然后在Service的onStartCommand生命周期函数中执行下载的线程即可。
// MainActivity.java
//开始下载按钮点击事件
public void StartDownload(View view)
Intent intent = new Intent();
intent.setClass(this, MyService.class);
startService(intent);
// MyService.java
@Override
public int onStartCommand(Intent intent, int flags, int startId)
Log.i(TAG, "onStartCommand: ");
myThread = new MyThread();
myThread.start();
return super.onStartCommand(intent, flags, startId);
这里使用的MyThread类继承自线程类Thread(在Thread重写run方法,并且通过pause变量实现线程的挂起和解挂)。并且这里吧MyThread类写入MyService类中即可(内部类)
public class MyThread extends Thread
private final Object lock = new Object();
private boolean pause = false;
/**
* 调用该方法实现线程的暂停
*/
void pauseThread()
pause = true;
/*
调用该方法实现恢复线程的运行
*/
void resumeThread()
pause = false;
synchronized (lock)
lock.notify();
/**
* 这个方法只能在run 方法中实现,不然会阻塞主线程,导致页面无响应
*/
void onPause()
synchronized (lock)
try
lock.wait();
catch (InterruptedException e)
e.printStackTrace();
@Override
public void run()
super.run();
int index = 0;
setDownloadStatus(true);
ServiceSendStatusBroadCast();
try
//模拟下载线程:30秒睡眠,这里使用30次for循环是为了每隔一秒能在界面中显示下载进度
for(int i=0; i<30; i++)
//当pause为true时,调用onPause挂起该线程
while(pause)
onPause();
TimeUnit.SECONDS.sleep(1);
Log.i(TAG, "run: "+i);//可以先打印是否能运行成功
catch (InterruptedException e)
e.printStackTrace();
如果要取消下载,那么我们要使用Thread中的interrupt()方法将线程中断,但线程是存放在Service中的,没有办法通过按钮点击事件直接对线程进行中断。
所以我们用到了绑定服务中ServiceConnection的onServiceConnected方法(绑定Service时执行)。在绑定服务后,通过该方法中的IBinder类的参数service可以接收到Binder类实例(需要类型转换)。然后通过localBinder.getService()可以得到绑定的Service。
// MainActivity.java
Intent bind_intent = new Intent();
bind_intent.setClass(this, MyService.class);
ServiceConnection connection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName name, IBinder service)
// 通过localBinder.getService()即可得到绑定的Service
localBinder = (MyService.LocalBinder) service;
Log.i(TAG, "onServiceConnected: ");
@Override
public void onServiceDisconnected(ComponentName name)
Log.i(TAG, "onServiceDisconnected: ");
;
bindService(bind_intent, connection, BIND_AUTO_CREATE);
// MyService.java
@Nullable
@Override
public IBinder onBind(Intent intent)
Log.i(TAG, "onBind: ");
//onServiceConnected()返回的参数IBinder service 就是onBind的返回值
//这里返回一个Binder类的继承类LocalBinder
return new LocalBinder();
//创建本地Binder类用于返回MyService实例
public class LocalBinder extends Binder
//调用getService可以返回绑定的Service
public MyService getService()
return MyService.this;
因此,通过该方法可以在按钮点击事件中对Service中的下载线程进行中断。
// MainActivity.java
//取消下载
public void cancelDownload()
if(myThread != null)
myThread.interrupt();
至此,运行APP,观察LogCat界面是否可以开始下载和取消下载。(取消下载时正常会抛出一个中断异常InterruptedException)
5.正在下载时设置开始下载按钮禁用,不在下载时设置取消下载按钮禁用
这个不难,但是需要结合之间学习的Android本地广播机制。
基本思路是:在Service中写入一个成员变量,用来表示是否在下载(不要在MainActivity中定义,否则app切入后台再切回可能会将该变量重置)。在点击开始下载(取消下载)按钮时根据下载状态改变该变量值,并通过发送本地广播LocalBroadcast通知MainActivity改变按钮状态(前面讲了,通过localBinder获取到MyService中的属性或方法)
public class MyService extends Service
private boolean DownloadStatus = false;
public boolean getDownloadStatus()
return DownloadStatus;
public void setDownloadStatus(boolean downloadStatus)
DownloadStatus = downloadStatus;
public class MyThread extends Thread
// ...
@Override
public void run()
super.run();
int index = 0;
setDownloadStatus(true);//设置下载状态变量
ServiceSendStatusBroadCast();//发送本地广播
try
for(int i=0; i<30; i++)
//当pause为true时,调用onPause挂起该线程
while(pause)
onPause();
TimeUnit.SECONDS.sleep(1);
catch (InterruptedException e)
e.printStackTrace();
setDownloadStatus(false);//下载结束或被中断,设置下载状态变量
ServiceSendStatusBroadCast();//发送本地广播
//取消下载
public void cancelDownload()
if(myThread != null)
//中断下载线程,将下载状态置为false, 发送本地广播消息通知app更改按钮状态
myThread.interrupt();
setDownloadStatus(false);//设置下载状态变量
ServiceSendStatusBroadCast();//发送本地广播
// MainActivity.java
public class MainActivity extends AppCompatActivity
public Button DownloadButton;
public Button CancelButton;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DownloadButton = (Button) findViewById(R.id.DownloadButton);
CancelButton = (Button) findViewById(R.id.CancelDownloadButton);
//...
//注册用于接收Service下载状态通知的本地广播
IntentFilter statusFilter = new IntentFilter();
statusFilter.addAction("DOWNLOAD_STATUS_CHANGED");
BroadcastReceiver statusReceiver = new BroadcastReceiver()
@Override
public void onReceive(Context context, Intent intent)
//在接收时根据MyService中的下载状态成员变量改变按钮状态
MonitorDownloadStatus(localBinder.getService().getDownloadStatus());
;
LocalBroadcastManager.getInstance(this).registerReceiver(statusReceiver, statusFilter);
//根据下载状态布尔值参数改变布局中按钮的状态
public void MonitorDownloadStatus(boolean status)
if(status)
//正在下载
DownloadButton.setEnabled(false);
CancelButton.setEnabled(true);
else
//未在下载
DownloadButton.setEnabled(true);
CancelButton.setEnabled(false);
运行app,观察LogCat打印信息是否符合预期(如果没有改变按钮可用状态,检查是否发送了本地广播,是否在每次下载状态改变后根据下载状态变量改变按钮状态)。
6.显示下载进度条
下载进度条和改变下载状态的思路一样,在每一秒(线程中的每一次for循环)通过本地广播发送一个int类型的进度。然后MainActivity接收到广播后根据这个进度值改变ProgressBar控件的值即可。
7.使用Java线程类Thread的挂起wait()和恢复notify()实现暂停和继续下载
调用MyThread类中的挂起和解挂两个方法。然后老规矩,在MainActivity中使用localBinder.getService()。
// MyService.java
public 以上是关于Android:Service组件及其简单应用的主要内容,如果未能解决你的问题,请参考以下文章