关于Service,你要知道的一些知识

来源:互联网 发布:飞书互动 知乎 编辑:程序博客网 时间:2024/06/06 09:57

Service到底是什么? 服务,它是在后台运行一些耗时的任务同时不与用户交互或用于其他应用程序使用,比如下载,更新天气等等。必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态。

Service的基本用法

首先创建服务:MyService.java

这里写图片描述

并重写父类的onCreate()、onStartCommand()和onDestroy()方法

public class MyService extends Service {    public static final String TAG = "MyService";    public MyService() {    }    @Override    public void onCreate() {        super.onCreate();        Log.e(TAG, "onCreate: executed" );    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.e(TAG, "onStartCommand() executed");        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        super.onDestroy();        Log.e(TAG, "onDestroy: executed");    }    @Override    public IBinder onBind(Intent intent) {        return  null;    }}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.example.eason.servicetest.MainActivity">    <Button        android:id="@+id/start_service"        android:layout_width="379dp"        android:layout_height="60dp"        android:text="start service"        tools:layout_editor_absoluteY="0dp"        android:layout_marginRight="8dp"        app:layout_constraintRight_toRightOf="parent"        android:layout_marginLeft="8dp"        app:layout_constraintLeft_toLeftOf="parent"        tools:layout_editor_absoluteX="-143dp"/>    <Button        android:id="@+id/stop_service"        android:layout_width="400dp"        android:layout_height="53dp"        android:text="Stop Service"        android:layout_marginTop="45dp"        app:layout_constraintTop_toBottomOf="@+id/start_service"        android:layout_marginLeft="8dp"        app:layout_constraintLeft_toLeftOf="parent"        android:layout_marginRight="8dp"        app:layout_constraintRight_toRightOf="parent"        tools:layout_editor_absoluteX="-164dp"/></android.support.constraint.ConstraintLayout>

MainActivity.java:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private Button startService;    private Button stopService;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        startService = (Button)findViewById(R.id.start_service);        stopService = (Button)findViewById(R.id.stop_service);        startService.setOnClickListener(this);        stopService.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.start_service:                Intent startIntent = new Intent(this,MyService.class);                startService(startIntent);                break;            case R.id.stop_service:                Intent stopIntent = new Intent(this,MyService.class);                stopService(stopIntent);                break;            default:                break;        }    }}

运行程序:

这里写图片描述

点击第一个按钮:

这里写图片描述

再次点击第一个按钮:

这里写图片描述

发现 onCreate()方法没有执行,因为在第一次点击的时候,service就已经被创建了,所以无论你点击多少次,都只有onStartCommand()方法在执行。

点击第二个按钮:

这里写图片描述
服务被销毁。

Service和Activity通信

如何使Service和Activity通信呢? 仔细点你会发现在MyService里有个onBind()方法,比如现在实现下载(非真实,下篇会写)的功能,

接下修改 MyService.java:

public class MyService extends Service {    public static final String TAG = "MyService";    private DownloadBinder down = new DownloadBinder();    public MyService() {    }    @Override    public void onCreate() {        super.onCreate();        Log.e(TAG, "onCreate: executed" );    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.e(TAG, "onStartCommand() executed");        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        super.onDestroy();        Log.e(TAG, "onDestroy: executed");    }    @Override    public IBinder onBind(Intent intent) {        return  down;    }    class DownloadBinder extends Binder{        public void startDownload(){            Log.e(TAG, "startDownload" );        }        public int getProgress(){            Log.e(TAG, "getProgress");            return 0;        }    }}

添加两个按钮,一个绑定活动,另一个解除绑定。

在 activity_main.xml 添加这两个按钮:

<Button        android:id="@+id/bind_service"        android:layout_width="439dp"        android:layout_height="55dp"        android:text="Bind Service"        android:layout_marginTop="8dp"        app:layout_constraintTop_toBottomOf="@+id/stop_service"        android:layout_marginBottom="8dp"        app:layout_constraintBottom_toTopOf="@+id/unbind_service"        app:layout_constraintVertical_bias="0.395"        android:layout_marginLeft="8dp"        app:layout_constraintLeft_toLeftOf="parent"        android:layout_marginRight="8dp"        app:layout_constraintRight_toRightOf="parent"/>    <Button        android:id="@+id/unbind_service"        android:layout_width="387dp"        android:layout_height="57dp"        android:text="Unbind Service"        android:layout_marginRight="8dp"        app:layout_constraintRight_toRightOf="parent"        android:layout_marginLeft="8dp"        app:layout_constraintLeft_toLeftOf="parent"        app:layout_constraintHorizontal_bias="0.473"        app:layout_constraintBottom_toBottomOf="parent"        android:layout_marginBottom="208dp"/>

修改MainActivity.java:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private Button startService;    private Button stopService;    private Button bindService;    private Button unbindService;    private MyService.DownloadBinder mDownloadBinder;    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            mDownloadBinder = (MyService.DownloadBinder)service;            mDownloadBinder.startDownload();            mDownloadBinder.getProgress();        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        startService = (Button)findViewById(R.id.start_service);        stopService = (Button)findViewById(R.id.stop_service);        bindService = (Button)findViewById(R.id.bind_service);        unbindService = (Button)findViewById(R.id.unbind_service);        startService.setOnClickListener(this);        stopService.setOnClickListener(this);        bindService.setOnClickListener(this);        unbindService.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.start_service:                Intent startIntent = new Intent(this,MyService.class);                startService(startIntent);                break;            case R.id.stop_service:                Intent stopIntent = new Intent(this,MyService.class);                stopService(stopIntent);                break;            case R.id.bind_service:                Intent bindIntent = new Intent(this,MyService.class);                bindService(bindIntent,mConnection,BIND_AUTO_CREATE);//绑定服务                break;            case R.id.unbind_service:                unbindService(mConnection);//解除绑定                break;            default:                break;        }    }}

这里我们创建了ServiceConnection的匿名类,在里面重写了onServiceConnected()和onServiceDisconnected()的方法,这两个方法分别在 连接和断开时候启用。

bindService()方法,第一个参数接受Intent的对象,第二个参数就是前面创建的ServiceConnection的实例。
第三个参数则是一个标志位,这里的BIND_AUTO_CREATE表示在活动和服务进行绑定后自动创建服务,这会使MyService内的onCreate()方法得到执行,而onStartCommand()不会。

效果图:

这里写图片描述

点击 bindService:

这里写图片描述

注意:

任何一个服务都是通用的,可以和任何活动进行绑定而且绑定后都可以获取到相同的实例。

进阶Service

创建前台Service:


修改MyService.java:

@Override    public void onCreate() {        super.onCreate();        Log.e(TAG, "onCreate: executed" );        Intent intent = new Intent(this,MainActivity.class);        PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);        Notification notification = new NotificationCompat.Builder(this)                .setContentTitle("标题")                .setContentText("内容")                .setWhen(System.currentTimeMillis())                .setSmallIcon(R.mipmap.ic_launcher_round)                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))                .setContentIntent(pi)                .build();        startForeground(1,notification);    }

其他和使用通知的流程一样。但是这里要注意的是 startForeground的方法会让MyService变成一个前台服务,并且在系统通知栏显示出来。

效果图:

这里写图片描述

IntentService

前面提到服务用来做耗时的事情,如果在主线程内这么默认使用,很容易出现 ANR 即应用无响应。
所以这时候就要用到多线程的知识。

在MyService:

 @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.e(TAG, "onStartCommand() executed");        new Thread(new Runnable() {            @Override            public void run() {                //处理具体的逻辑                stopSelf();//停止服务,自动停止功能。            }        });        return super.onStartCommand(intent, flags, startId);    }

另一种用法:

创建IntentService:

public class MyIntentService extends IntentService {    private static final String TAG = "MyIntentService";    public MyIntentService(){        super("MyIntentService");    }    @Override    protected void onHandleIntent(@Nullable Intent intent) {        //打印当前线程id        Log.e(TAG, "onHandleIntent: id is "+Thread.currentThread().getId());    }    @Override    public void onDestroy() {        super.onDestroy();        Log.e(TAG, "onDestroy");    }}

修改MainActivity:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {            ....................    private Button intentService;    private MyService.DownloadBinder mDownloadBinder;    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            mDownloadBinder = (MyService.DownloadBinder)service;            mDownloadBinder.startDownload();            mDownloadBinder.getProgress();        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        ................        intentService = (Button)findViewById(R.id.start_intent_service);        ...............        intentService.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            ..........            case R.id.start_intent_service:                Log.e("MainActivity", "onHandleIntent: id is "+Thread.currentThread().getId());                Intent intentServices = new Intent(this,MyIntentService.class);                startService(intentServices);                break;            default:                break;        }    }}

在AndroidManifest.xml 添加一个按钮:

<Button        android:id="@+id/start_intent_service"        android:layout_width="380dp"        android:layout_height="49dp"        android:text="start intentService"        tools:layout_editor_absoluteX="2dp"        android:layout_marginTop="8dp"        app:layout_constraintTop_toBottomOf="@+id/unbind_service"        app:layout_constraintBottom_toBottomOf="parent"        android:layout_marginBottom="8dp"        app:layout_constraintVertical_bias="0.293"/>

在onHandleIntent()方法内是用来处理一些具体的逻辑的;

下面的效果图可以看出,服务是在子线程内启动的,并且会自动关闭。

效果图:

这里写图片描述

服务的生命周期

服务也有自己的生命周期,一旦在项目的任何位置调用了Context的startService()方法,相应的服务就会启动起来,并且会调用onStartCommand()的方法。如果这个服务没有被创建,则onCreate()方法则会优先于onStartCommand()方法。服务启动后,会一直保持运行状态,直到stopService()或stopSelf()方法被调用。注意虽然每调用一次satrtService()方法,onStartCommand()方法会被调用一次,但是实际上只有一个该服务的实例。

同样,bindService()方法会调用onBind()方法,类似的模式和上面相同。

注意一点,我们有时候一个服务既使用startService()和bindService()方法,这种情况下销毁服务,要同时调用stopService()和unbindServcie()方法才会执行onDestroy()方法。