Android如何启动service

2年前 (2022) 程序员胖胖胖虎阿
194 0 0

启动service的两种方式

1. 通过StartService启动Service

通过startService启动后,service会一直无限期运行下去,

  • 当外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁
  • 当系统资源不足时, 会回收一些不重要的service,service被系统回收也会停止运行并被销毁
生命周期
  • onCreate()
    1.如果service没被创建过,调用startService()后会执行onCreate()回调;
    2.如果service已处于运行中,调用startService()不会执行onCreate()方法。
    此方法适合完成一些初始化工作。

  • onStartCommand()
    如果多次执行了Context的startService()方法,那么Service的onStartCommand()方法也会相应的多次调用。

  • onBind()
    Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。

  • onDestory()
    在销毁的时候会执行Service该方法。

代码实例

MyActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 启动service
        Intent mIntent=new Intent(MainActivity.this,MyService.class) ;
        startService(mIntent);
    }
}

MyServvice.java

public class MyService extends Service {
    private static final String TAG = "MyService";
    private NotificationManager notificationManager;
    private String notificationId = "channel_Id";
    private String notificationName = "channel_Name";

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate: ...");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: ...");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy: ....");
        super.onDestroy();
    }

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.iauto.demo">
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MainActivity ">
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"
            ></service>

        <activity
            android:name=".MainActivity"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

2. 通过bindService启动Service

bindService启动服务特点:

  • bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
  • client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
  • bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。当没有任何client与Service绑定时,Service会自行销毁
生命周期
  • onCreate()
    当服务通过onStartCommand()和onBind()被第一次创建的时候,系统调用该方法。该调用要求执行一次性安装。

  • onBind()
    当其他组件想要通过bindService()来绑定服务时,系统调用该方法。如果你实现该方法,你需要返回IBinder对象来提供一个接口,以便客户来与服务通信。你必须实现该方法,如果你不允许绑定,则直接返回null。

  • onUnbind()
    当客户中断所有服务发布的特殊接口时,系统调用该方法。

  • onRebind()
    当新的客户端与服务连接,且此前它已经通过onUnbind(Intent)通知断开连接时,系统调用该方法。

  • onDestroy()
    当服务不再有用或者被销毁时,系统调用该方法。你的服务需要实现该方法来清理任何资源,如线程,已注册的监听器,接收器等。

代码实例

MainAcivity.java

public class MainAcivity extends Activity{
    private Myservice = null;
    private boolean isBind = false;

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            isBind = true;
            TestTwoService.MyBinder myBinder = (TestTwoService.MyBinder) binder;
            service = myBinder.getService();
            int num = service.getRandomNumber();
        }

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

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
        Intent intent = new Intent(this, TestTwoService.class);
        intent.putExtra("from", "MainAcivity");
        bindService(intent, conn, BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

MyService.java

public class MyService extends Service{

    //client 可以通过Binder获取Service实例
    public class MyBinder extends Binder {
        public MyService getService() {
            return MyService .this;
        }
    }

    //通过binder实现调用者client与Service之间的通信
    private MyBinder binder = new MyBinder();

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_NOT_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return false;
    }

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

    //getRandomNumber是Service暴露出去供client调用的公共方法
    public int getRandomNumber() {
        return generator.nextInt();
    }
}

如何保证service不被杀死

之前说过:当系统资源不足时, 会回收一些不重要的service,service被系统回收也会停止运行并被销毁,那么如何保证service不被杀死呢

1. onStartCommand方式中,返回START_STICKY

表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该Service依然设置为started状态(即运行状态),但是不再保存onStartCommand方法传入的intent对象,然后Android系统会尝试再次重新创建该Service,并执行onStartCommand回调方法,但是onStartCommand回调方法的Intent参数为null,也就是onStartCommand方法虽然会执行但是获取不到intent信息。如果你的Service可以在任意时刻运行或结束都没什么问题,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY

2. 提高Service的优先级

在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低

*: 但是我在service中设置intent-filter,设置优先级build报错,有兴趣的可以另行查证

3. 提升Service进程的优先级

前台进程foreground_app优先级相对较高,可以将service设置为前台进程
代码实例:
MainActivity.java

package com.iauto.helloword;

import androidx.appcompat.app.AppCompatActivity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 启动service
        Intent mIntent=new Intent(MainActivity.this,MyService.class) ;
        Log.d("activity", "onCreate: to start service");
        startForegroundService(mIntent);
        Log.d("activity", "onCreate: start service end");
        finish();
    }
}

MyService.java

package com.iauto.helloword;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;

import androidx.core.app.NotificationCompat;

public class MyService extends Service {
    private static final String TAG = "MyService";
    private NotificationManager notificationManager;
    private String notificationId = "channel_Id";
    private String notificationName = "channel_Name";

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate: ...");
        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        //创建NotificationChannel
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(notificationId, notificationName, NotificationManager.IMPORTANCE_HIGH);
            // 必须创建notifychannel, 不然会抛异常Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service
            notificationManager.createNotificationChannel(channel);
        }
        startForeground(1, getNotification());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: ...");
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy: ....");
        super.onDestroy();
    }

    private Notification getNotification() {
        Notification.Builder builder = new Notification.Builder(this)
                .setContentTitle("ScenarioEngineLite正在后台运行")
                .setContentText("");

        //设置Notification的ChannelID,否则不能正常显示

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            builder.setChannelId(notificationId);
        }
        Notification notification = builder.build();
        return notification;

    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.iauto.helloword">
    <!-- 必须设置以下权限,否则会抛异常RemoteException-->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MainActivity"
        android:persistent="true">
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
<!--            <intent-filter android:priority = "1000"/>-->
        </service>

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
4.在onDestroy方法里重启Service

当service走到onDestroy()时,发送一个自定义广播,当收到广播时,重新启动service。

5.系统广播监听Service状态
6.将APK安装到/system/app,变身为系统级应用

版权声明:程序员胖胖胖虎阿 发表于 2022年9月19日 下午4:08。
转载请注明:Android如何启动service | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...