定义布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp"
tools:context=".MainActivity">
<!-- 启动服务-->
<Button
android:id="@+id/btn_start"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="StartService"
android:textAllCaps="false"
android:textSize="30sp"
android:enabled="true"/>
<!-- 结束服务-->
<Button
android:id="@+id/btn_stop"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="StopService"
android:textAllCaps="false"
android:textSize="30sp"
android:enabled="false"/>
<!-- 绑定服务到Activity-->
<Button
android:id="@+id/btn_bind"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="BindService"
android:textAllCaps="false"
android:textSize="30sp" />
<!--通过BindService实现了Activity与Service的通信,然后访问Service的对应方法-->
<Button
android:id="@+id/btn_operate"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="OperateService"
android:textAllCaps="false"
android:textSize="30sp" />
<!--和Activity解除绑定-->
<Button
android:id="@+id/btn_unbind"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="UnBindService"
android:textAllCaps="false"
android:textSize="30sp" />
<!-- 执行耗时操作-->
<Button
android:id="@+id/btn_intent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="IntentService"
android:textAllCaps="false"
android:textSize="30sp" />
<Button
android:id="@+id/btn_foreground"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="ForegroundService"
android:textAllCaps="false"
android:textSize="30sp" />
</LinearLayout>

新建Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.example.servicetest;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {
private static final String TAG = MyService.class.getSimpleName();
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: ThreadId"+Thread.currentThread().getId());
super.onCreate();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: StartId"+startId);
Log.d(TAG, "onStartCommand: ThreadId"+Thread.currentThread().getId());
return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
Log.d(TAG, "onDestroy: ThreadId"+Thread.currentThread().getId());
super.onDestroy();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.example.servicetest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();

private Button mBtnStart, mBtnStop;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate: ThreadId"+Thread.currentThread().getId());

initView();
initClick();
}

private void initClick() {
mBtnStart.setOnClickListener((View view)->{
mBtnStart.setEnabled(false);
mBtnStop.setEnabled(true);
Intent intent = new Intent(this, MyService.class);;
startService(intent);
});
mBtnStop.setOnClickListener((View view)->{
mBtnStop.setEnabled(false);
mBtnStart.setEnabled(true);
Intent intent = new Intent(this, MyService.class);;
stopService(intent);
});
}

private void initView() {
mBtnStart = findViewById(R.id.btn_start);
mBtnStop = findViewById(R.id.btn_stop);
}
}

通过如下按钮操作

  • 点击三次StartService
  • 点击一次Stop
  • 点击一次Stop
  • 点击一次StartService

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
D/MainActivity: onCreate: ThreadId2

D/MyService: onCreate: ThreadId2
D/MyService: onStartCommand: StartId1
D/MyService: onStartCommand: ThreadId2
D/MyService: onStartCommand: StartId2
D/MyService: onStartCommand: ThreadId2
D/MyService: onStartCommand: StartId3
D/MyService: onStartCommand: ThreadId2

D/MyService: onDestroy: ThreadId2

D/MyService: onCreate: ThreadId2
D/MyService: onStartCommand: StartId1
D/MyService: onStartCommand: ThreadId2

分析

ThreadId相同,故在同一程序中的本地Service与Activity在同一UI线程中执行

第一次启动Service时,会调用onCreate()方法,然后调用onStartCommand()方法

允许多次启动Service,startId将会累加

调用stopService后将回调onDestroy()方法

再次启动又从startId=1开始

绑定Service

MyService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.example.servicetest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {
private static final String TAG = MyService.class.getSimpleName();
public class MyBinder extends Binder {
public MyService getService (){
return MyService.this;
}
}
private MyBinder myBinder = new MyBinder();
public MyService() {
}

@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: bind");
return myBinder;
}

@Override
public void onCreate() {
Log.d(TAG, "onCreate: ThreadId"+Thread.currentThread().getId());
super.onCreate();
}

public String doSomeOperation(String str) {
Log.i(TAG,"doSomeOperation: bind");
return str+"value";
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: StartId"+startId);
Log.d(TAG, "onStartCommand: ThreadId"+Thread.currentThread().getId());
return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
Log.d(TAG, "onDestroy: ThreadId"+Thread.currentThread().getId());
super.onDestroy();
}
}

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.example.servicetest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();

private Button mBtnStart, mBtnStop;
private Button mBtnBind, mBtnOperate, mBtnUnBind;


private Intent intent;

private MyService.MyBinder myBinder;
private MyService myService;

private ServiceConnection conn;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent(this, MyService.class);
Log.d(TAG, "onCreate: ThreadId"+Thread.currentThread().getId());
conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.d(TAG, "ServiceConnected");
myBinder = (MyService.MyBinder) binder;
myService = myBinder.getService();
}

@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "ServiceDisconnected");
myBinder = null;
myService = null;
}
};
initView();
initClick();
}
private void initClick() {
mBtnStart.setOnClickListener((View view)->{
startService(intent);
});
mBtnStop.setOnClickListener((View view)->{
stopService(intent);
});

mBtnBind.setOnClickListener((View view)->{
bindService(intent, conn, BIND_AUTO_CREATE);
});
mBtnOperate.setOnClickListener((View view)->{
if(myService != null) {
String ret = myService.doSomeOperation("abc");
Toast.makeText(this, ret, Toast.LENGTH_SHORT).show();
}
});
mBtnUnBind.setOnClickListener((View view)->{
if(myBinder != null){
unbindService(conn);
}
});
}

private void initView() {
mBtnStart = findViewById(R.id.btn_start);
mBtnStop = findViewById(R.id.btn_stop);

mBtnBind = findViewById(R.id.btn_bind);
mBtnOperate = findViewById(R.id.btn_operate);
mBtnUnBind = findViewById(R.id.btn_unbind);
}
}

IntentService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.example.servicetest;

import android.app.IntentService;
import android.content.Intent;
import android.util.Log;

import androidx.annotation.Nullable;

public class MyIntentService extends IntentService {
private static final String TAG = MyIntentService.class.getSimpleName();
public MyIntentService() {
super(TAG);
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService(String name) {
super(name);
}

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

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

@Override
public void onStart(@Nullable Intent intent, int startId) {
super.onStart(intent, startId);
Log.d(TAG, "onStart");
}

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

@Override
protected void onHandleIntent(@Nullable Intent intent) {
int count = intent.getExtras().getInt("count");
Log.d(TAG, "onHandleIntent: count="+count);

while (count > 0) {
count--;
Log.d(TAG, "onHandleIntent: count="+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

1
2
3
4
5
6
7
mBtnIntent.setOnClickListener((View view)->{
Intent intent1 = new Intent(this, MyIntentService.class);
intent1.putExtra("count",20);
startService(intent1);
intent1.putExtra("count",5);
startService(intent1);
});

AndroidManifest.xml

application中加入

1
2
<service
android:name=".MyIntentService"></service>

前台Service

Android系统对运行的进程按优先级划分了四种进程类型

  1. 前台进程 (Foreground Process)
  2. 可见进程 (Visible Process)
  3. 服务进程 (Service Process)
  4. 后台进程 (Background Process)

当系统内存不足时,系统优先杀死后台进程,为防止后台的Service被杀,故可申请为前台进程
MainActivity.java中的点击事件

1
2
3
4
5
6
mBtnForeground.setOnClickListener((View view)-> {
Intent intent1 = new Intent(this, MyForegroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startService(intent1);
}
});

MyForegroundService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.example.servicetest;

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

import androidx.core.app.NotificationCompat;

public class MyForegroundService extends Service {
public MyForegroundService() {
}

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

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

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

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

public void showNotification() {
String channelId = "MyChannel01";
// Notification点击后跳转到MainActivity
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId, "MyChannel", NotificationManager.IMPORTANCE_LOW);
notificationManager.createNotificationChannel(channel);
}

Notification notification = new NotificationCompat.Builder(this,channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("前台服务")
.setContentText("This is a foreground")
.setContentIntent(pendingIntent)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.build();
startForeground(1, notification);
}
}

最终的AndroidManifest.xml文件

声明前台Service权限,声明几个service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.servicetest">
<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.ServiceTest">

<service
android:name=".MyForegroundService"
android:enabled="true"
android:exported="true"></service>
<service android:name=".MyIntentService" />
<service
android:name=".MyService"
android:enabled="true"
android:exported="true" />

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

最终的MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package com.example.servicetest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();

private Button mBtnStart, mBtnStop;
private Button mBtnBind, mBtnOperate, mBtnUnBind;

private Button mBtnIntent;
private Button mBtnForeground;
private Intent intent;

private MyService.MyBinder myBinder;
private MyService myService;

private ServiceConnection conn;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent(this, MyService.class);
Log.d(TAG, "onCreate: ThreadId"+Thread.currentThread().getId());
conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.d(TAG, "ServiceConnected");
myBinder = (MyService.MyBinder) binder;
myService = myBinder.getService();
}

@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "ServiceDisconnected");
myBinder = null;
myService = null;
}
};
initView();
initClick();
}
private void initClick() {
mBtnStart.setOnClickListener((View view)->{
startService(intent);
});
mBtnStop.setOnClickListener((View view)->{
stopService(intent);
});

mBtnBind.setOnClickListener((View view)->{
bindService(intent, conn, BIND_AUTO_CREATE);
});
mBtnOperate.setOnClickListener((View view)->{
if(myService != null) {
String ret = myService.doSomeOperation("abc");
Toast.makeText(this, ret, Toast.LENGTH_SHORT).show();
}
});
mBtnUnBind.setOnClickListener((View view)->{
if(myBinder != null){
unbindService(conn);
}
});
mBtnIntent.setOnClickListener((View view)->{
Intent intent1 = new Intent(this, MyIntentService.class);
intent1.putExtra("count",20);
startService(intent1);
intent1.putExtra("count",5);
startService(intent1);
});
mBtnForeground.setOnClickListener((View view)-> {
Intent intent1 = new Intent(this, MyForegroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startService(intent1);
}
});
}

private void initView() {
mBtnStart = findViewById(R.id.btn_start);
mBtnStop = findViewById(R.id.btn_stop);

mBtnBind = findViewById(R.id.btn_bind);
mBtnOperate = findViewById(R.id.btn_operate);
mBtnUnBind = findViewById(R.id.btn_unbind);

mBtnIntent = findViewById(R.id.btn_intent);

mBtnForeground = findViewById(R.id.btn_foreground);
}
}