Service
1、何谓“Service”?“Service” 意思即“服务”的意思, 像 Windows 上面的服务一样,服务是在后台上运行,承担着静悄悄的不为人所注意的工作。Service运行在后台,它是不可见的、无界面的程序。Service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity,这个时候程序要在后台继续播放;比如检测SD卡上文件的变化;再或者在后台记录用户的地理信息位置的改变;或者启动一个服务来运行并一直监听某种动作等等。
2、Service概念的总结:
3、为什么不使用后台线程而使用Service?
4、Service 与 Activity 的相同点与不同点:
1、本地服务Local Service:Local Service 用于应用程序内部。用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。
启动service有两种方法:1)、 Context.startService()调用者与服务之间没有关联,即使调用者退出,服务仍可运行2)、 Context.bindService()调用者与服务绑定在一起,调用者一旦退出,服务也就终止
A、根据启动方式将本地服务分为:启动服务Started Service和绑定服务Bound Service。【重点】
- Started Service:被启动的服务- 被启动的服务是由其它组件调用startService()方法而启动的,该方法会导致被启动服务的生命周期方法onStartCommand()被回调。当服务是被启动状态后,其生命周期与启动它的组件无关,即使启动服务的组件(Activity,BroadcastReceiver)已经被销毁,该服务还可以在后台无限期运行。除非调用stopSelf()或stopService()来停止该服务。- Bound Service:被绑定的服务- 绑定服务是允许其它应用程序绑定并且与之交互的Service的实现类。为了提供绑定,必须实现onBind()回调方法。该方法返回IBinder对象,它定义了服务类与Activity交互的程序接口。- Activity通过bindService()方法绑定到服务类,同时Activity必须提供ServiceConnection接口的实现类,它监视Activity与服务类之间的连接。在重写ServiceConnection接口的onServiceConnected()方法时,实现了将服务类顺利赋值到了Activity中,实现了在Activity中使用该服务类并执行其中的方法。
B、根据onStartCommand()回调方法的返回值,将Service分为粘性Service和非粘性Service:onStartCommand()方法有三种返回值:- START_STICKY(常量值:1):sticky的意思是“粘性的”。使用这个返回值时,我们启动的服务跟应用程序"粘"在一起,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务。当再次启动服务时,传入的第一个参数将为null;- START_NOT_STICKY(常量值:2):“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后, 服务被异常kill掉,系统不会自动重启该服务。- START_REDELIVER_INTENT(常量值:3):重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
【备注:】以上三种情况,可以理解为发生车祸后的人:
2、远程服务Remote Service:Remote Service 用于android系统内部的应用程序之间。可以定义接口并把接口暴露出来,以便其他应用进行操作。可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。
(三)、Service的生命周期:
1、Started Service的生命周期:
【详细说明:】
2、Bound Service的生命周期:
【详细说明:】
【备注:】
1、操作步骤:
2、XML布局文件核心代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/button_main_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="clickButton"
android:text="播放音乐" />
<Button
android:id="@+id/button_main_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="clickButton"
android:text="暂停音乐" />
<Button
android:id="@+id/button_main_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="clickButton"
android:text="停止音乐" />
<Button
android:id="@+id/button_main_exit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="clickButton"
android:text="关闭当前窗体" />
<Button
android:id="@+id/button_main_stopservice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="clickButton"
android:text="停止服务" />
LinearLayout>
3、MainActivty核心代码:
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private Intent intent = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.i (TAG, "==onCreate执行");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent(this, StartServicePlayMusic.class);
}
@Override
protected void onDestroy() {
Log.i ("MainActivty", "==onDestroy()");
super.onDestroy();
if (intent != null) {
// 停止服务可以通过stopService(intent)来停止,也可以intent到Service程序中,通过stopSelf()来停止。
stopService(intent);
}
}
public void clickButton(View view) {
int type = 0;
switch (view.getId()) {
case R.id.button_main_play:
type = 1;
break;
case R.id.button_main_pause:
type = 2;
break;
case R.id.button_main_stop:
type = 3;
break;
case R.id.button_main_exit:
finish();
break;
case R.id.button_main_stopservice:
// 停止服务可以通过stopService(intent)来停止,也可以intent到Service程序中,通过stopSelf()来停止
// stopService(intent);
// finish();
type = 4;
break;
}
Bundle bundle = new Bundle();
bundle.putInt("type", type);
intent.putExtras(bundle);
startService(intent);
}
}
4、StartServicePlayMusic核心代码:
public class StartServicePlayMusic extends Service {
private static final String TAG = "StartServicePlayMusic";
private MediaPlayer mediaPlayer;
@Override
public IBinder onBind(Intent intent) {
Log.i (TAG, "==onBind执行");
return null;
}
@Override
public void onCreate() {
Log.i (TAG, "==onCreate执行");
super.onCreate();
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(this, R.raw.hitta);
mediaPlayer.setLooping(false);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i (TAG, "==onStartCommand执行");
if (intent != null) {
Bundle bundle = intent.getExtras();
int type = bundle.getInt("type");
switch (type) {
case 1:
play();
break;
case 2:
pause();
break;
case 3:
stop();
break;
case 4:
stopSelf();
break;
}
}
return START_STICKY;
}
@Override
public void onDestroy() {
Log.i (TAG, "==onDestroy执行");
super.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
}
}
public void play() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
public void pause() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
public void stop() {
if (mediaPlayer != null) {
mediaPlayer.stop();
}
}
}
5、Manifest清单配置文件核心代码:
<service
android:name=".StartServicePlayMusic">
<intent-filter>
<action android:name="com.steven.startservice.playmusic"/>
intent-filter>
service>android:name=".StartServicePlayMusic">
<intent-filter>
<action android:name="com.steven.startservice.playmusic"/>
intent-filter>
service>
6、Started Service总结:
7、IntentService与Service:
不管是何种Service,它默认都是在应用程序的主线程(亦即UI线程)中运行的。所以,如果你的Service将要运行非常耗时或者可能被阻塞的操作时,你的应用程序将会被挂起,甚至会出现ANR错误。为了避免这一问题,你应该在Service中重新启动一个新的线程来进行这些操作。现有两种方法大家参考:① 直接在Service的onStartCommand()方法中新建一个线程来执行;
② Android SDK 中为我们提供了一个现成的Service类来实现这个功能,它就是IntentService,它主要负责以下几个方面:
IntentService使用队列的方式将请求的Intent加入队列,然后开启一个worker thread(线程)来处理队列中的Intent,对于异步的startService请求,IntentService会处理完成一个之后再处理第二个,每一个请求都会在一个单独的worker thread中处理,不会阻塞应用程序的主线程。这里就给我们提供了一个思路,如果有耗时的操作可以在Service里面开启新线程,也可以使用IntentService来处理耗时操作。 但你若是想在Service中让多个线程并发的话,就得使用第一种方法,在Service内部起多个线程,但是这样的话,你可要处理好线程的同步。
(1)、Service实现加载图片的核心代码:
public class DownloadService extends IntentService {
private static final String TAG = "DownloadService";
private String urlString = "https://www.google.com.hk/images/srpr/logo11w.png";
private NotificationCompat.Builder builder = null;
private NotificationManager manager = null;
public DownloadService() {
super("");
}
@Override
protected void onHandleIntent(Intent intent) {
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
builder = new NotificationCompat.Builder(getApplicationContext());
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle("提示:");
builder.setContentText("图片加载完成,请点击查看!");
builder.setTicker("图片加载完成");
builder.setAutoCancel(true);
Intent intent2 = new Intent(getApplicationContext(), MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent2,PendingIntent.FLAG_ONE_SHOT);
builder.setContentIntent(pIntent);
byte[] data = HttpClientHelper.loadByteFromURL(urlString);
boolean flag = SDCardHelper.saveFileToSDCard(data, "Download", "logo11w.png");
if (flag) {
manager.notify(1, builder.build());
}
}
}
(2)、IntentService实现加载图片的核心代码:
public class DownloadService extends Service {
private static final String TAG = "DownloadService";
private String urlString = "https://www.google.com.hk/images/srpr/logo11w.png";
private NotificationCompat.Builder builder = null;
private NotificationManager manager = null;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
builder = new NotificationCompat.Builder(getApplicationContext());
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle("提示:");
builder.setContentText("图片加载完成,请点击查看!");
builder.setTicker("图片加载完成");
builder.setAutoCancel(true);
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent,PendingIntent.FLAG_ONE_SHOT);
builder.setContentIntent(pIntent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
byte[] data = HttpClientHelper.loadByteFromURL(urlString);
boolean flag = SDCardHelper.saveFileToSDCard(data, "Download","logo11w.png");
if (flag) {
manager.notify(1, builder.build());
stopSelf();
}
}
}).start();
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
1、操作步骤:
2、XML布局文件:同上
3、MainActivty核心代码:
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private Intent intent;
private ServiceConnection conn = null;
private BindServicePlayMusic musicService;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.i (TAG, "==onCreate执行");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 启动服务有多种写法:
// 可以通过new Intent(action字符串)来实现;
// intent = new Intent("com.steven.bindservice.playmusic");
intent = new Intent(this, BindServicePlayMusic.class);
conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
musicService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
musicService = ((BindServicePlayMusic.MyBinder) service).getService();if (musicService != null) {
musicService.play();
}
}
};
}
public void clickButton(View view) {
switch (view.getId()) {
case R.id.button_main_play:
if (musicService == null) {
bindService(intent, conn, Context.BIND_AUTO_CREATE);
} else {
musicService.play();
}
break;
case R.id.button_main_pause:
if (musicService != null) {
musicService.pause();
}
break;
case R.id.button_main_stop:
if (musicService != null) {
musicService.stop();
}
break;
case R.id.button_main_exit:
finish();
break;
case R.id.button_main_stopservice:
// BindService中stopService(intent)不起作用,要通过undbindService来停止服务
// stopService(intent);
// musicService = null的目的是如果停止服务后,再次”播放“,可以正常执行。
// 如果不将musicService设置为null,再次播放时,将直接执行musicService.play(),
//而不执行bindService(),那么会导致异常
musicService = null;
unbindService(conn);
break;
}
}
}
4、BindServicePlayMusic核心代码:
public class BindServicePlayMusic extends Service {
private static final String TAG = "BindServicePlayMusic";
private MediaPlayer mediaPlayer;
private IBinder binder = null;
@Override
public void onCreate() {
Log.i (TAG, "==onCreate执行");
super.onCreate();
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(this, R.raw.heavencity);
mediaPlayer.setLooping(false);
}
binder = new MyBinder();
}
class MyBinder extends Binder {
public BindServicePlayMusic getService() {
return BindServicePlayMusic.this;
}
};
@Override
public IBinder onBind(Intent intent) {
Log.i (TAG, "==onBind执行");
play();
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i (TAG, "==onUnbind执行");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.i (TAG, "==onDestroy执行");
super.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
}
}
public void play() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
public void pause() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
public void stop() {
if (mediaPlayer != null) {
mediaPlayer.stop();
}
}
}
5、Manifest清单配置文件核心代码:
<service
android:name=".BindServicePlayMusic">
<intent-filter>
<action android:name=“com.steven.bindservice.playmusic"/>
intent-filter>
service>
<service android:name=".BindServicePlayMusic"><intent-filter>
<action android:name=“com.steven.bindservice.playmusic"/>
intent-filter>
service>
<service android:name=".BindServicePlayMusic"><intent-filter>
<action android:name=“com.steven.bindservice.playmusic"/>
intent-filter>
service>
6、Bound Service总结:
Android操作系统尝试尽可能长时间保持应用的进程,但当可用内存很低时要移走一部分进程。哪些程序可以运行,哪些要被销毁?答案是:重要级别低的进程可能被淘汰。按照重要性排列,一共可以分成5级:
1、前台运行进程:用户此时需要处理和显示的进程。符合下列条件任何一个,这个进程就被认为是前台运行进程。
销毁前台运行进程是系统万不得已的、最后的选择——当内存不够系统继续运行下去时,杀掉一些前台进程来保证能够响应用户的需求。2、可用进程:一个可用进程没有任何前台组件,但它仍然可以影响到用户的界面。下面情况发生时,可以称该进程为可用进程。它是一个非前台的activity,但对用户仍然可用(onPause()方法已经被调用)。例如:前台的activity是一个允许上一个activity可见的对话框。也就是说当前activity中是一个对话框,对话框之外的地方能看到前一个activity的界面。
3、服务进程:服务进程是一个通过调用startService()方法启动的服务,并且不属于前两种情况。尽管服务进程没有直接被用户看到,但他们确实是用户所关心的,比如后台播放音乐或网络下载数据,所以系统保证他们的运行。
4、后台进程:一个后台进程就是非当前正在运行的activity(activity的onStop()方法已经被调用),他们不会对用户体验造成直接的影响,当没有足够内存来运行前台可见程序时,他们将会被终止。通常,后台进程会有很多个在运行,LRU最近使用程序列表来保证经常运行的activity能最后一个被终止。
5、空线程:一个空线程没有运行任何可用应用程序,保留他们的唯一原因是为了设立一个缓存机制,来加快组件启动的时间。系统经常杀死这些内存来平衡系统的整个系统的资源,进程缓存和基本核心缓存之间的资源。
public class InCallReceiver extends BroadcastReceiver {
private TelephonyManager manager = null;
@Override
public void onReceive(Context context, Intent intent) {
manager = (TelephonyManager) context .getSystemService(Service.TELEPHONY_SERVICE);
switch (manager.getCallState()) {
case TelephonyManager.CALL_STATE_RINGING:
String incomingNumber = intent.getStringExtra("incoming_number");
if ("12345678".equals(incomingNumber)) {
try {
// 获得TelephonyManager的class对象
Class
telephonyManagerClass = TelephonyManager.class; // 获得TelephonyManager.getITelephony方法的Method对象
Method telephonyMethod = telephonyManagerClass
.getDeclaredMethod("getITelephony", (Class[]) null);
// 允许访问私有的方法
telephonyMethod.setAccessible(true);
// 调用getITelephony()方法返回ITelephony对象
ITelephony telephony = (com.android.internal.telephony.ITelephony) telephonyMethod
.invoke(manager, (Object[]) null);
// 挂断电话
telephony.endCall();
} catch (Exception e) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();}
}
break;
}
}
}