Service

深入理解Android Service
Service一

一、Service:
(一)、Service 简介:
1、何谓“Service”?
“Service” 意思即“服务”的意思, 像 Windows 上面的服务一样,服务是在后台上运行,承担着静悄悄的不为人所注意的工作。Service运行在后台,它是不可见的、无界面的程序。
Service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity,这个时候程序要在后台继续播放;比如检测SD卡上文件的变化;再或者在后台记录用户的地理信息位置的改变;或者启动一个服务来运行并一直监听某种动作等等。

2、Service概念的总结:
  • Service在后台运行,不可以与用户直接交互;
  • 一个服务不是一个单独的进程。服务对象本身并不意味着它是在自己的进程中运行,除非另有规定,否则它与运行程序是同在一个进程中;
  • 一个服务不是一个单独的线程Service和其他组件一样,默认情况下,Service中的所有代码都是运行在主线程中;
  • Service存在的价值虽然不如Activity那么清晰。但是一般都让Service执行耗时较长的操作。例如:播放音乐下载文件、上传文件等等。但是因为Service默认运行在主线程中,因此不能直接用它来做耗时的请求或者动作,最好在Service中启动新线程来运行耗时的任务
  • 需要通过某一个Activity或其他Context对象来启动Service。context.startService() 或 context.bindService();
  • Service很大程度上充当了应用程序后台线程管理器的角色。(如果Activity中新开启一个线程,当该Acitivyt关闭后,该线程依然在工作,但是与开启它的Activity失去联系。也就是说此时的这个线程处于失去管理的状态。但是使用Service,则可以对后台运行的线程有效地管理。)

3、为什么不使用后台线程而使用Service?
  • 1、Service可以放在独立的进程中,所以更安全;
  • 2、使用Service可以依赖现有的binder机制,不需要在应用层面上处理线程同步的繁杂工作;
  • 3、系统可以重新启动异常死去的Service

4、Service 与 Activity 的相同点与不同点:
  • 不同点:Activity是与用户交互的组件,即可以看到UI界面,而Service是在后台运行、无需界面;
  • 相同点:使用Activity 时我们需要在配置文件中声明<activity>标签,同样的使用Service 也需要在配置文件中声明<service>标签。都具有一定的生命周期。

(二)、服务的分类:
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的值传入。
【备注:】
以上三种情况,可以理解为发生车祸后的人:
  • START_STICKY(常量值:1)车祸后自己苏醒,但是失忆;
  • START_NOT_STICKY(常量值:2)车祸后再也没有苏醒;
  • START_REDELIVER_INTENT(常量值:3)车祸后自己苏醒,依然保持记忆。
2、远程服务Remote Service: 
Remote Service 用于android系统内部的应用程序之间。可以定义接口并把接口暴露出来,以便其他应用进行操作。可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。


(三)、Service的生命周期:




1、Started Service的生命周期:
  • onCreate():创建服务
  • onStartCommand():服务开始运行(在2.0以前版本中,使用onStart()回调方法)
  • onDestroy() :服务被停止
【详细说明:】
  • 在程序中调用:context.startService() 会触发执行Service生命周期中的onCreate()、onStartCommand()回调方法,此时服务就开始正式运行;
  • 如果Service还没有运行,则android先调用onCreate()然后调用onStartCommand();如果Service已经运行,则只调用onStartCommand(),所以一个Service的onStartCommand方法可能会重复调用多次
  • 如果在程序中调用:context.stopService()会触发执行Service生命周期中的onDestroy()回调方法,会让服务停止;
  • stopService()的时候直接onDestroy,如果是调用者自己直接退出而没有调用stopService()的话,Service会一直在后台运行。该Service的调用者再启动该Service后可以通过stopService关闭Service;stopSelf()
  • 所以StartService的生命周期为:onCreate --> onStartCommand(可多次调用) --> onDestroy。
 
2、Bound Service的生命周期:
  • onCreate():创建服务
  • onBind():绑定服务,服务开始运行
  • onUnbind():取消绑定
  • onDestroy() :服务被停止
【详细说明:】
  • 在程序中调用:context.bindService()会触发执行Service生命周期中的onCreate()、onBind()回调方法,此时服务开始运行;
  • onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。此后调用者(Context,例如Activity)会和Service绑定在一起;
  • 如果调用Service的调用者Context退出了,那么会依次调用Service生命周期中的onUnbind()、onDestroy()回调方法,会让服务停止;    
  • 所以BindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。
 【备注:】
  • Service是不能自己启动的,只有通过 Context 对象调用startService() bindService() 方法来启动。
  • 在Service每一次的开启关闭过程中,只有onStartCommand()可被多次调用(通过多次startService调用),其他onCreate()、onBind()、onUnbind()、onDestory()在一个生命周期中只能被调用一次

  • Service可以在和多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务总是藏在后头的。


二、本地Service:

(一)、Started Service启动MediaPlayer播放音乐:
1、操作步骤:
  • 1、写xml布局文件;
  • 2、写MainActivity文件,通过按钮点击事件启动Service;
  • 3、写继承于Service的StartService类文件:重写onCreate()/onStartCommand()/onDestroy()回调方法。

2、XML布局文件核心代码:

<LinearLayoutxmlns: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核心代码:

publicclass MainActivity extends Activity {

privatestaticfinal String TAG = "MainActivity";

private Intent intent = null;




@Override

protectedvoid onCreate(Bundle savedInstanceState) {

Log.i(TAG, "==onCreate执行");

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

intent = new Intent(this, StartServicePlayMusic.class);

}




@Override

protectedvoid onDestroy() {

Log.i("MainActivty", "==onDestroy()");

super.onDestroy();

if (intent != null) {

// 停止服务可以通过stopService(intent)来停止,也可以intent到Service程序中,通过stopSelf()来停止。

stopService(intent);

}

}




publicvoid 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核心代码:

publicclass StartServicePlayMusic extends Service {

privatestaticfinal String TAG = "StartServicePlayMusic";

private MediaPlayer mediaPlayer;




@Override

public IBinder onBind(Intent intent) {

Log.i(TAG, "==onBind执行");

returnnull;

}




@Override

publicvoid onCreate() {

Log.i(TAG, "==onCreate执行");

super.onCreate();

if (mediaPlayer == null) {

mediaPlayer = MediaPlayer.create(this, R.raw.hitta);

mediaPlayer.setLooping(false);

}

}




@Override

publicint 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;

}

}
returnSTART_STICKY;
}




@Override

publicvoid onDestroy() {

Log.i(TAG, "==onDestroy执行");

super.onDestroy();

if (mediaPlayer != null) {

mediaPlayer.stop();

mediaPlayer.release();

}

}




publicvoid play() {

if (!mediaPlayer.isPlaying()) {

mediaPlayer.start();

}

}




publicvoid pause() {

if (mediaPlayer.isPlaying()) {

mediaPlayer.pause();

}

}




publicvoid stop() {

if (mediaPlayer != null) {

mediaPlayer.stop();

}

}

}

5、Manifest清单配置文件核心代码:

<service

android:name=".StartServicePlayMusic">

<intent-filter>

<actionandroid:name="com.steven.startservice.playmusic"/>

</intent-filter>

</service>

6、Started  Service总结:
  • Activity页面中需要startService(intent) 和 stopService(intent)两个方法来启动Service和停止Service;
  • 继承于Service类的自定义子类——MyStartService类中,生命周期回调方法有:onCreate() 、onStartCommand() 、onDestroy();
  • 如果停止服务,可以在Activity中调用stopService(intent),也可以intent到Service中执行stopSelf()方法;
  • 执行停止服务方法,会回调Service生命周期中的onDestroy()方法;
  • 如果希望关闭Activity窗体,服务也停止,那么在Activity的onDestroy()方法中执行stopService()方法。如果希望关闭窗体后,服务还继续,那么Activity的onDestroy()中不执行停止服务即可;
  • 在StartService中不会回调onBind()方法;
  • 在停止服务后,如果再次点击“播放”,可以重新启动StartService。

7、IntentService与Service:

不管是何种Service,它默认都是在应用程序的主线程(亦即UI线程)中运行的。所以,如果你的Service将要运行非常耗时或者可能被阻塞的操作时,你的应用程序将会被挂起,甚至会出现ANR错误。为了避免这一问题,你应该在Service中重新启动一个新的线程来进行这些操作。现有两种方法大家参考:
① 直接在Service的onStartCommand()方法中新建一个线程来执行;

② Android SDK 中为我们提供了一个现成的Service类来实现这个功能,它就是IntentService,它主要负责以下几个方面:
  • Creates a default worker thread that executes all intents delivered to onStartCommand() separate from your application's main thread.
  •     生成一个默认的且与主线程互相独立的工作者线程来执行所有传送至 onStartCommand() 方法的Intetnt
  • Creates a work queue that passes one intent at a time to your onHandleIntent() implementation, so you never have to worry about multi-threading.
  •     生成一个工作队列来传送Intent对象给你的onHandleIntent()方法,同一时刻只传送一个Intent对象,这样一来,你就不必担心多线程的问题。
  • Stops the service after all start requests have been handled, so you never have to call stopSelf().
  •     在所有的请求(Intent)都被执行完以后会自动停止服务,所以,你不需要自己去调用stopSelf()方法来停止该服务
  • Provides default implementation of onBind() that returns null.
  •     提供了一个onBind()方法的默认实现,它返回null
  • Provides a default implementation of onStartCommand() that sends the intent to the work queue and then to your onHandleIntent() implementation
  •     提供了一个onStartCommand()方法的默认实现,它将Intent先传送至工作队列,然后从工作队列中每次取出一个传送至onHandleIntent()方法,在该方法中对Intent对相应的处理

IntentService使用队列的方式将请求的Intent加入队列,然后开启一个worker thread(线程)来处理队列中的Intent,对于异步的startService请求,IntentService会处理完成一个之后再处理第二个,每一个请求都会在一个单独的worker thread中处理,不会阻塞应用程序的主线程。
这里就给我们提供了一个思路,如果有耗时的操作可以在Service里面开启新线程,也可以使用IntentService来处理耗时操作。 但你若是想在Service中让多个线程并发的话,就得使用第一种方法,在Service内部起多个线程,但是这样的话,你可要处理好线程的同步。

(1)、Service实现加载图片的核心代码:

publicclass DownloadService extends IntentService {

privatestaticfinal 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

protectedvoid 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实现加载图片的核心代码:

publicclass DownloadService extends Service {

privatestaticfinal 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) {

returnnull;

}




@Override

publicvoid 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

publicint onStartCommand(Intent intent, int flags, int startId) {

new Thread(new Runnable() {

@Override

publicvoid run() {

byte[] data = HttpClientHelper.loadByteFromURL(urlString);

boolean flag = SDCardHelper.saveFileToSDCard(data, "Download",

"logo11w.png");

if (flag) {

manager.notify(1, builder.build());

stopSelf();

}

}

}).start();

returnSTART_STICKY;

}




@Override

publicvoid onDestroy() {

super.onDestroy();

}

}

(二)、Bound Service启动MediaPlayer播放音乐:
1、操作步骤:
  • 1、写xml布局文件;
  • 2、写MainActivity文件,构建ServiceConnection对象,重写其中的抽象方法onServiceDisconnected()和onServiceConnected()
  • 3、写继承于Service的BindService类文件,定义继承于Binder的内部类MyBinder,在其中定义方法getService();
  • 4、BindService类中重写onCreate()方法、重写onBind()回调方法,onBind()方法返回MyBinder对象,重写onDestroy()方法;

2、XML布局文件:同上

3、MainActivty核心代码:

publicclass MainActivity extends Activity {
privatestaticfinal String TAG = "MainActivity";
private Intent intent;
private ServiceConnection conn = null;
private BindServicePlayMusic musicService;


@Override
protectedvoid 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
publicvoid onServiceDisconnected(ComponentName name) {
musicService = null;
}


@Override
publicvoid onServiceConnected(ComponentName name, IBinder service) {
musicService = ((BindServicePlayMusic.MyBinder) service)
.getService();
if (musicService != null) {
musicService.play();
}
}
};
}


publicvoid 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核心代码:

publicclass BindServicePlayMusic extends Service {

privatestaticfinal String TAG = "BindServicePlayMusic";

private MediaPlayer mediaPlayer;




private IBinder binder = null;




@Override

publicvoid 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();

returnbinder;

}




@Override

publicboolean onUnbind(Intent intent) {

Log.i(TAG, "==onUnbind执行");

returnsuper.onUnbind(intent);

}




@Override

publicvoid onDestroy() {

Log.i(TAG, "==onDestroy执行");

super.onDestroy();

if (mediaPlayer != null) {

mediaPlayer.stop();

mediaPlayer.release();

}

}




publicvoid play() {

if (!mediaPlayer.isPlaying()) {

mediaPlayer.start();

}

}




publicvoid pause() {

if (mediaPlayer.isPlaying()) {

mediaPlayer.pause();

}

}




publicvoid stop() {

if (mediaPlayer != null) {

mediaPlayer.stop();

}

}

}

5、Manifest清单配置文件核心代码:

<service

android:name=".BindServicePlayMusic">

<intent-filter>

<actionandroid:name=com.steven.bindservice.playmusic"/>

</intent-filter>

</service>

6、Bound Service总结:



三、Android系统服务:

        getSystemService(String name)是Android很重要的一个方法,根据NAME来取得对应的Object,然后转换成相应的服务对象。以下介绍系统相应的服务。
===============================================================
name                                     返回的对象                                   说明
WINDOW_SERVICE               WindowManager                     管理打开的窗口程序
LAYOUT_INFLATER_SERVICE     LayoutInflater                     取得xml里定义的view
ACTIVITY_SERVICE               ActivityManager                      管理应用程序的系统状态
POWER_SERVICE                  PowerManger                         电源的服务
ALARM_SERVICE                   AlarmManager                       闹钟的服务
NOTIFICATION_SERVICE        NotificationManager                状态栏的服务
KEYGUARD_SERVICE             KeyguardManager                   键盘锁的服务
LOCATION_SERVICE              LocationManager                    位置的服务,如GPS
SEARCH_SERVICE                 SearchManager                      搜索的服务
VIBRATOR_SERVICE              Vibrator                                 手机震动的服务
CONNECTIVITY_SERVICE        Connectivity                           网络连接的服务
WIFI_SERVICE                      WifiManager                           Wi-Fi服务
TELEPHONY_SERVICE             TeleponyManager                   电话服务


四、 拓展知识(进程和生命周期):
Android操作系统尝试尽可能长时间保持应用的进程,但当可用内存很低时要移走一部分进程。哪些程序可以运行,哪些要被销毁?答案是:重要级别低的进程可能被淘汰。
按照重要性排列,一共可以分成5级:

1、前台运行进程
用户此时需要处理和显示的进程。符合下列条件任何一个,这个进程就被认为是前台运行进程。
  • 与用户正发生交互;
  • 它控制一个与用户交互的必须的基本的服务;
  • 一个正在调用生命周期回调方法的ervice(如onCreate()、onStar()、onDestroy());
  • 一个正在运行onReceive()方法的广播接收对象。        
销毁前台运行进程是系统万不得已的、最后的选择——当内存不够系统继续运行下去时,杀掉一些前台进程来保证能够响应用户的需求。
2、可用进程
一个可用进程没有任何前台组件,但它仍然可以影响到用户的界面。下面情况发生时,可以称该进程为可用进程。
        它是一个非前台的activity,但对用户仍然可用(onPause()方法已经被调用)。例如:前台的activity是一个允许上一个activity可见的对话框。也就是说当前activity中是一个对话框,对话框之外的地方能看到前一个activity的界面。

3、服务进程
服务进程是一个通过调用startService()方法启动的服务,并且不属于前两种情况。尽管服务进程没有直接被用户看到,但他们确实是用户所关心的,比如后台播放音乐或网络下载数据,所以系统保证他们的运行。

4、后台进程
一个后台进程就是非当前正在运行的activity(activity的onStop()方法已经被调用),他们不会对用户体验造成直接的影响,当没有足够内存来运行前台可见程序时,他们将会被终止。
通常,后台进程会有很多个在运行,LRU最近使用程序列表来保证经常运行的activity能最后一个被终止。

5、空线程
一个空线程没有运行任何可用应用程序,保留他们的唯一原因是为了设立一个缓存机制,来加快组件启动的时间。系统经常杀死这些内存来平衡系统的整个系统的资源,进程缓存和基本核心缓存之间的资源。




 


Service二——Android系统服务
一、Android系统服务:
        getSystemService(String name)是Android很重要的一个方法,根据NAME来取得对应的Object,然后转换成相应的服务对象。以下介绍系统相应的服务。
===============================================================
name                                     返回的对象                                   说明
WINDOW_SERVICE               WindowManager                     管理打开的窗口程序
LAYOUT_INFLATER_SERVICE     LayoutInflater                     取得xml里定义的view
【LayoutInflater layoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);】

ACTIVITY_SERVICE                ActivityManager                      管理应用程序的系统状态
POWER_SERVICE                  PowerManger                         电源的服务
ALARM_SERVICE                   AlarmManager                       闹钟的服务
NOTIFICATION_SERVICE        NotificationManager                状态栏的服务
KEYGUARD_SERVICE             KeyguardManager                   键盘锁的服务
LOCATION_SERVICE              LocationManager                    位置的服务,如GPS
SEARCH_SERVICE                  SearchManager                      搜索的服务
VIBRATOR_SERVICE               VibratorManager                                 手机震动的服务
CONNECTIVITY_SERVICE        Connectivity                           网络连接的服务
WIFI_SERVICE                         WifiManager                           Wi-Fi服务
TELEPHONY_SERVICE             TeleponyManager                   电话服务
SENSOR_SERVICE                   SensorManager                       传感器服务

二、DownloadManager介绍
(一)、DownloadManager简介
        DownloadManager是Android 2.3引入的,基于http协议,用于处理长时间下载
        DownloadManager是系统开放给第三方应用使用的类,包含两个静态内部类DownloadManager.Query和DownloadManager.Request
        DownloadManager.Request用来请求一个下载,DownloadManager.Query用来查询下载信息。

(二)、DownloadManager主要提供了下面几个接口:
1、public long enqueue(Request request)执行下载,返回downloadId,downloadId可用于后面查询下载信息。若网络不满足条件、Sdcard挂载中、超过最大并发数等异常会等待下载,正常则直接下载。
2、public int remove(long… ids)删除下载,若下载中取消下载。会同时删除下载文件和记录。
3、public Cursor query(Query query)查询下载信息。
4、public static LonggetRecommendedMaxBytesOverMobile(Context context通过移动网络下载的最大字节数
5、public String getMimeTypeForDownloadedFile(long id)得到下载的mimeType,如何设置后面会进行介绍

其它:通过查看代码我们可以发现还有个CursorTranslator私有静态内部类。这个类主要对Query做了一层代理。将DownloadProvider和DownloadManager之间做个映射。将DownloadProvider中的十几种状态对应到了DownloadManager中的五种状态,DownloadProvider中的失败、暂停原因转换为了DownloadManager的原因。


(二)、DownloadManager用法【使用步骤:】
1、调用DownloadManager.Request开始下载

DownloadManager downloadManager =  (DownloadManager)getSystemService(DOWNLOAD_SERVICE);

String apkUrl =  "http://img.meilishuo.net/css/images/AndroidShare/Meilishuo_3.6.1_10006.apk";

DownloadManager.Request request = new  DownloadManager.Request(Uri.parse(apkUrl));

request.setDestinationInExternalPublicDir("Trinea",  "abc.mp4");

设置下载文件保存的路径


//  request.setTitle("文件下载");

设置下载中通知栏提示的标题


//  request.setDescription("保存的文件名称");

设置下载中通知栏提示的介绍


// request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);

表示下载进行中和下载完成的通知栏是否显示。默认只显示下载中通知。VISIBILITY_VISIBLE_NOTIFY_COMPLETED表示下载完成后显示通知栏提示。


//  request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);

VISIBILITY_HIDDEN表示不显示任何通知栏提示,这个需要在AndroidMainfest中添加权限android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.


//  request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);

表示下载允许的网络类型,默认在任何网络下都允许下载。有NETWORK_MOBILE、NETWORK_WIFI、NETWORK_BLUETOOTH三种及其组合可供选择。如果只允许wifi下载,而当前网络为3g,则下载会等待。


// request.setMimeType("video/mp4");

设置下载文件的mineType。因为下载管理Ui中点击某个已下载完成文件及下载完成点击通知栏提示都会根据mimeType去打开文件,所以我们可以利用这个属性。比如上面设置了mimeType为application/cn.trinea.download.file,我们可以同时设置某个Activity的intent-filter为application/cn.trinea.download.file,用于响应点击的打开文件。


long downloadId =  downloadManager.enqueue(request);

调用downloadManager的enqueue接口进行下载,返回唯一的downloadId。

2、下载成功监听
        下载完成后,下载管理会发出DownloadManager.ACTION_DOWNLOAD_COMPLETE这个广播,并传递downloadId作为参数。通过接收广播我们可以打开对下载完成的内容进行操作。

3、响应通知栏点击 ( 响应下载完成通知栏点击)
        下载完后,点击通知栏可以阅读已经下载文件。系统其实会调用Intent的action属性,根据mimeType去调用相应的系统内置文件阅读器。所以一定要设置好DownloadManager.Request的setMimeType()方法。


三、AlarmManager:
(一)、简介:
        AlarmManager将应用与服务分割开来后,使得应用程序开发者不用关心具体的服务,而是直接通过AlarmManager来使用这种服务。这也许就是客户/服务模式的好处。AlarmManager与 AlarmManagerServie之间是通过Binder来通信的,他们之间是多对一的关系。

(二)、AlarmManager API:
1、AlarmManage提供了几个常用方法:
1)、取消已经注册的与参数匹配的闹铃    
        void   cancel(PendingIntent operation)  
2)、注册一个新的闹铃  
        void   set(int type, long triggerAtTime, PendingIntent operation)  
3)、注册一个重复类型的闹铃  
        void   setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation)  
4)、设置时区  
        void   setTimeZone(String timeZone) 

2、五种类型的闹铃服务:【区分的是时间标准和是否在睡眠状态下唤醒设备。】
1)、public   static   final   int  ELAPSED_REALTIME  
// 当系统进入睡眠状态时,这种类型的闹铃不会唤醒系统。直到系统下次被唤醒才传递它,该闹铃所用的时间是相对时间,是从系统启动后开始计时的,包括睡眠时间,可以通过调用SystemClock.elapsedRealtime()获得。系统值是3    (0x00000003)。    
 
2)、public   static   final   int  ELAPSED_REALTIME_WAKEUP  
//能唤醒系统,用法同ELAPSED_REALTIME,系统值是2 (0x00000002) 。    
 
3)、public   static   final   int  RTC  
//当系统进入睡眠状态时,这种类型的闹铃不会唤醒系统。直到系统下次被唤醒才传递它,该闹铃所用的时间是绝对时间,所用时间是UTC时间,可以通过调用 System.currentTimeMillis()获得。系统值是1 (0x00000001) 。    
 
4)、public   static   final   int  RTC_WAKEUP  
//能唤醒系统,用法同RTC类型,系统值为 0 (0x00000000) 。    
5)、Public static   final   int  POWER_OFF_WAKEUP  
//能唤醒系统,它是一种关机闹铃,就是说设备在关机状态下也可以唤醒系统,所以我们把它称之为关机闹铃。使用方法同RTC类型,系统值为4(0x00000004)。


四、Vibrator:
(一)、基本用法:
Vibrator  mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
mVibrator.vibrate(new long[] { 500, 200, 500, 200 }, -1);
【参数说明:】
1、只有1个参数的时候,第一个参数用来指定振动的毫秒数;
2、传递2个参数的时候,第1个参数用来指定振动时间的样本,第2个参数用来指定是否需要循环,-1为不重复,非-1则从pattern的指定下标开始重复 。
3、振动时间的样本是指振动时间和等待时间的交互指定的数组,即节奏数组。
例如:
震动节奏分别为:OFF/ON/OFF/ON… 
long[] pattern = {3000, 1000, 2000, 5000, 3000, 1000}; 
表示的含义:在程序起动后等待3秒后,振动1秒,再等待2秒后,振动5秒,再等待3秒后,振动1秒


五、SensorManager:【详见Sensor一章】
(一)、基本用法:

一般使用传感器都有以下5个步骤

1、调用Context的getSystemService(Context.SENSOR_SERVICE)方法获取SensorManager对象;

2、调用SensorManager的getDefaultSensor(int type)方法获取指定类型的传感器;

3、在onCreate()生命周期方法中调用SensorManager的registerListener()方法为指定的传感器注册监听;

4、实例化SensorEventListener接口,作为registerListener()方法的第一个参数。重写SensorEventListener接口中onSensorChanged()方法;

5、在onDestroy()生命周期方法中调用SensorManager对象的unregisterListener()方法释放资源。


六、TelephonyManager:【详见监听来电广播一章】

(一)、基本用法:

1、获取电话状态:

TelephonyManager manager = (TelephonyManager) context  .getSystemService(Service.TELEPHONY_SERVICE);

int state = manager.getCallState();
switch (state) {
 case TelephonyManager.CALL_STATE_IDLE:
  Log.i("MyPhoneReceiver", "--->电话挂了!!");
  break;
 case TelephonyManager.CALL_STATE_OFFHOOK:
  Log.i("MyPhoneReceiver", "--->电话接听了!!");
  break;
 case TelephonyManager.CALL_STATE_RINGING:
  Log.i("MyPhoneReceiver", "--->电话铃声响了!!");
  break;
}

2、利用AIDL及反射挂断电话:
Class<TelephonyManager> teleClass = TelephonyManager.class;
try {
 Method teleMethod = teleClass.getDeclaredMethod(
   "getITelephony", null);
 teleMethod.setAccessible(true);
 ITelephony telephony = (ITelephony) teleMethod.invoke(
   telephonyManager, null);
 boolean flag = telephony.endCall();
} catch (Exception e) {
 e.printStackTrace();
}


七、WindowManager:
(一)、简介:
        window manager (窗口管理器)   Window Manager是Android中一个重要的服务(Service )。Window Manager Service 是全局的,是唯一的。它将用户的操作,翻译成为指令,发送给呈现在界面上的各个Window。Activity会将顶级的控件注册到 Window Manager 中,当用户触碰屏幕或键盘的时候,Window Manager就会通知到,而当控件有一些请求产生,也会经由ViewParent送回到Window Manager中。从而完成整个通信流程。
        Window Manager API用于与 window manager (窗口管理器) 进行交互。可以添加 view 到屏幕,也可以从屏幕删除 view 。它面向的对象一端是屏幕,另一端就是 view ,直接忽略我们以前的 Activity 或者 Dialog 。




Service二——跨进程访问AIDL

一、AIDL:
(一)、概念:
1、相关知识:
在Android中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢? 
显然, Java中是不支持跨进程内存共享的。因此要传递对象, 需要把对象解析成操作系统能够理解的数据格式, 以达到跨界对象访问的目的。这就是远程程序调用Remote Procedure Call , RPC)。
在JavaEE中,采用RMI通过序列化传递对象。在Android中, RPC解决方案是采用AIDL(Android Interface Definition Language:接口定义语言)方式实现。 
Activity、BroadCast、ContentProvider和Service都可以实现跨进程的访问。
ICP:interprocess communication :内部进程通信

2、什么是AIDL
AIDL是 Android Interface definition language的缩写,它是一种android内部进程通信接口的描述语言,通过它定义进程间的通信接口,实现进程间通信。AIDL就是Android中的一种实现RPC的解决方案。


(二)、建立AIDL服务和实现AIDL调用的步骤:[重点]
A、服务器端:
1、在Eclipse Android工程的Java包目录中建立一个扩展名为aidl的文件。该文件的语法类似于Java代码,但会稍有不同。
2、如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(*.java)。
3、建立一个服务类(Service的子类)。
4、实现由aidl文件生成的Java接口。
5、在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,<action>标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。
B、客户端:
1、将aidl文件及其包引入该APP工程;
2、绑定服务器
3、通过远程Service类的实例调用aidl中定义的方法。

二、AIDL实现挂断电话:

(一)、核心代码:

publicclass InCallReceiver extends BroadcastReceiver {

private TelephonyManager manager = null;




@Override

publicvoid 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<TelephonyManager> 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;

}

}

}

【备注:设置权限】
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
    <uses-permission android:name="android.permission.READ_CALL_LOG"/>
    <uses-permission android:name="android.permission.CALL_PHONE"/>











### Service的生命周期状态 Service在Android系统中可以分为两种主要状态:启动状态绑定状态。当一个Service被启动后,它会在后台长时间运行,即使启动它的组件已经被销毁,Service仍然可以继续运行[^2]。如果一个Service既被启动又被绑定,那么它会同时处于这两种状态。 ### Service的生命周期方法 自定义的Service需要继承`Service`基类,并且通常需要重写一些生命周期方法来实现特定的功能: - `onCreate()`:当Service第一次创建时调用。如果Service已经存在,则不会调用此方法。 - `onStartCommand(Intent intent, int flags, int startId)`:当其他组件通过调用`startService()`方法请求启动Service时调用。在这个方法里可以处理长时间运行的操作。 - `onBind(Intent intent)`:当其他组件通过调用`bindService()`方法绑定到Service时调用。该方法返回一个`IBinder`接口,允许客户端与Service进行通信。 - `onUnbind(Intent intent)`:当所有绑定到Service的客户端都解绑后调用。 - `onDestroy()`:当Service不再使用并被销毁时调用。这是释放资源的好时机。 ### 启动状态下的Service 当一个Service通过调用`startService()`方法启动时,它进入启动状态。在这种状态下,Service独立于启动它的组件运行,直到它自己停止或被系统终止。要停止这样的Service,可以在Service内部调用`stopSelf()`方法,或者从外部组件调用`stopService()`方法[^1]。 ### 绑定状态下的Service 当组件通过调用`bindService()`方法绑定Service时,Service进入绑定状态。此时,组件可以通过`ServiceConnection`对象获取到Service提供的`IBinder`对象,从而与Service进行交互。绑定状态下的Service可以被多个组件绑定,只有当所有绑定的组件都解绑后,Service才会被销毁[^3]。 ### Service的声明 无论哪种类型的Service,都需要在`AndroidManifest.xml`文件中声明。声明格式如下: ```xml <service android:name=".Demo2Service" /> ``` 其中`.Demo2Service`是你的Service类名[^2]。 ### Service的绑定过程 为了与Service进行交互,组件需要创建一个`ServiceConnection`实例,并实现其`onServiceConnected()``onServiceDisconnected()`回调方法。例如: ```java private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // 这里 IBinder类型的service 就是我们要绑定的那个service // 通过这个serviceActivity就可以调用MyBindService.MyBinder中的方法 } @Override public void onServiceDisconnected(ComponentName name) { Log.i(TAG, "onServiceDisconnected: "); } }; ``` 然后,组件可以通过调用`bindService()`方法来绑定Service,并在不需要时调用`unbindService()`方法来解绑[^5]。 ### Service的应用场景 Service非常适合用来执行那些不需要用户界面但需要长时间运行的任务。比如播放音乐、下载文件、处理网络请求等。此外,Service还可以与其他组件进行交互,包括跨进程通信(IPC)[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值