Android基础之四大组件-详解Service

1 介绍

  Service是android的一种机制。Service是运行在后台的代码,不能与用户交互,可以运行在自己的进程,也可以运行在其他应用程序的上下文里。需要通过某一个Activity或其他Context对象来调用。

1.1 Service的生命周期

这里写图片描述
  被启动的服务的生命周期:如果一个Service被某个Activity调用 Context.startService方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService方法多次启动,那么onCreate方法只会调用一次,onStartCommand将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此只需要一次调用stopService)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。
  被绑定的服务的生命周期:如果一个Service被某个Activity调用 Context.bindService方法绑定启动,不管调用bindService调用几次,onCreate方法都只会调用一次,onBind方法也只会调用一次,同时onStartCommand方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService断开连接或者之前调用bindService的Context不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。
  被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStartCommand便会调用多少次。调用unbindService将不会停止Service,需要unbindService与stopService同时调用或Service的stopSelf来停止服务。
  当服务被停止时清除服务:当一个Service被终止(①调用stopService;②调用stopSelf;③不再有绑定的连接,没有被启动)时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。

1.2 注册Service

  无论使用哪种启动方法,都需要在xml里注册你的Service:

<service
    android:name=".packnameName.youServiceName"
    android:enabled="true" />

1.3 什么时候使用Service

  • 播放多媒体的时候,用户启动了其他的Activity这个时候程序要在后台继续播放。
  • 后台service下载插件、更新包。
  • 检测SD卡上文件的变化。
  • 在后台记录你地理位置的改变等等。

1.4 Service分为本地服务(LocalService)和远程服务(RemoteService)

  默认情况下是在同一个主线程中。但可以通过配,android:process=”:remote” 属性让 Service 运行在不同的进程。
  本地服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。主进程被Kill后,服务便会终止。
  远程服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。

2 Service的具体问题

2.1 Service可以执行耗时操作吗

  不能,超过20s就会出现ARN。

2.2 Service的启动方法,互相的区别

(1)startService启动的服务:主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService;当用访问者启动之后,如果访问者不主动关闭,Service就不会关闭。
(2)bindService启动的服务:该方法启动的服务可以进行通信,停止服务使用unbindService。应用程序可以通过ServiceConnection进行数据交互。在实现Service时重写的onBind方法中,其返回的对象会传给ServiceConnection对象的onServiceConnected(ComponentName name, IBinder service)中的service参数;也就是说获取了serivce这个参数就得到了Serivce组件返回的值。

//其中只要与Service连接成功conn就会调用其onServiceConnected方法。
Context.bindService(Intent intent,ServiceConnection conn,int flag)

(3)startService同时也bindService 启动的服务:停止服务应同时使用stepService与unbindService。

2.3 Service与Activity怎么实现通信(进程内通信)

(1)通过Binder对象。同进程下Activity与Service双向通信,Activity调用bindService (Intent service, ServiceConnection conn, int flags)方法。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    <application
        <service android:name=".MyService" />
    </application>
</manifest>
/**
 * 第1步,新建一个继承自Service的类MyService;然后在AndroidManifest.xml里注册这个Service
 */
public class MyService extends Service {
    private static final String TAG = "xiaoguan";
    //第4.1步,实例化一个MyBinder对象
    private MyBinder mBinder = new MyBinder();
    private OnTestListener mListener;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        //第4.2步,在onBind回调方法里面返回这个mBinder对象
        return mBinder;
    }

    //第3步,新建一个继承自Binder的类MyBinder
    public class MyBinder extends Binder {
        //Activity通过Binder来调用Service的方法将消息传给Service
        public void testMethod(String msg) {
            Log.d(TAG, "receive message from activity: "+msg);
            //并回调mListener.onTest告诉Activity已收到消息
            mListener.onTest("hi, activity");
        }
        //MyBinder 里面提供一个注册回调的方法
        public void setOnTestListener(OnTestListener listener) {
            mListener = listener;
        }
    }

    //自定义一个回调接口
    public interface OnTestListener {
        void onTest(String str);
    }
}
public class MainActivity extends Activity {
    private static final String TAG = "xiaoguan";
    private MyService.MyBinder mBinder;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //第5步,所说的在Activity里面取得Service里的binder对象
            mBinder = (MyService.MyBinder) service;
            //第6步,注册自定义回调
            mBinder.setOnTestListener(new MyService.OnTestListener() {
                @Override
                public void onTest(String str) {
                    Log.d(TAG, "receive message from service: "+str);
                }
            });
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(MainActivity.this, MyService.class);
        //第2步,Activity里面使用bindService方式启动MyService,也就是绑定了MyService
        bindService(intent,mConnection,BIND_AUTO_CREATE);

        findViewById(R.id.bt1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //点击按钮调用mBinder里面的方法,发送消息给Service
                mBinder.testMethod("hi, service");
            }
        });
    }
}
//结果打印
receive message from activity: hi, service
receive message from service: hi, activity

(2)通过广播。Service向Activity发送消息,可以使用广播,当然Activity要注册相应的接收器。比如Service要向多个Activity发送同样的消息的话,用这种方法就更好。

2.4 Service里面可以弹出dialog或Toast

(1)Toast通常使用Activity和Application的context,也可以使用Service、ContentProvider和BroadcastReceiver的context。但是在IntentService的onHandleIntent()不能使用,因为其在子线程中。
(2)Dialog只能在Activity中使用,其他组件使用会抛出异常。也有方法可以实现,如下(加个权限,在manifest中添加此权限以弹出dialog)。

<uses-permission Android:name="android.permission.SYSTEM_ALERT_WINDOW" />

dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
Builder builder = new AlertDialog.Builder(this);
builder.setMessage("是否重启服务");
builder.setNegativeButton("取消", new OnClickListense() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
           // to do
    }
});
builder.setPositiveButton("确定", new OnClickListense() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
           // to do
    }
});
final AlertDialog dialog = buidler.create();
//在dialog show前添加此代码,表示该dialog属于系统dialog。
dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
new Thread() {
     public void run() {
            SystemClock.sleep(2000);
            hanlder.post(new Runnable() {
                    @Override
                    public void run() {
                          dialog.show();
                    }
            });
     };
}.start();

参考来源于:developerzjy/ServiceDemo
(3)参考链接
Android Context 上下文 你必须知道的一切

2.5 为什么开启Service去创建子线程而不是Activity中

  (1)若直接在Activity中新开一条线程来做耗时操作,当该Activity退出到桌面或其他情况时将成为一个后台进程。
  (2)若在Service中新启动线程,则此时Android会依据进程中当前活跃组件重要程度,将其判断为服务进程,优先级比(1)高。
  因为服务进程的优先级比后台进程的优先级高,所以对于一个需要启动一个长时间操作的activity来说,开启service去创建子线程比Activity中创建子线程更好,尤其是对于操作将很可能超出activity的持续时间时。
  比如要上传一个图片文件,应该开启一个service来进行上传工作,这样在用户离开activity时工作仍在进行。使用service将会保证操作至少有服务进程的优先级。

3 如何保证Service在后台不被杀死,进程保活

在我的另一篇博客详细解析:Android进阶之进程优先级及提高优先级的方法(进程保活)

4 IntentService

在我的另一篇博客详细解析:Android性能优化之IntentService

5 参考链接

Android面试之Android 篇

猜你喜欢

转载自blog.csdn.net/chenliguan/article/details/79223411