Android后台服务

Author Avatar
Euan 10月 31, 2019
  • 在其它设备中阅读本文章

实验5 Android后台服务

实验目的

1.了解Android系统下Service的基本含义

2.掌握Android系统下Service的使用方法

实验内容

       
实验要求使用Service,提供比较两个整数大小的功能,提供int
Compare(int, int)函数,输入两个整数,输出较大的整数。具体要求如下:(1)实现进程内的服务;(2)使用AIDL语言,实现相同功能的跨进程服务;(3)设计用户界面,在界面上允许用户输入两个整数,并分别通过调用进程内和跨进程服务,将较大的数字显示在界面上。

实验原理

       
Service是Android系统提供的四种组件之一,它的地位和Activity是并列的,只不过没有Activity的使用频率高。顾名思义Service就是运行在后台的一种服务程序,一般很少和用户交互,因此没有可视化界面。

       
创建一个Service类比较简单,只要定义一个类继承Service,覆盖该类中相应的方法就可以了。Service中定义了一系列和自身生命周期相关的方法,这些方法有:

       
● onBind(Intent intent):是必须实现的一个方法,返回一个绑定的接口给Service;

       
● onCreate():当Service第一次被创建时,由系统调用;

       
● onStart(Intent intent,int startId):当通过startService()方法启动Service时,该方法被调用;

       
● onDestroy():当 Service不再使用时,系统调用该方法。

       
一旦定义好一个Service,就可以在其他组件中启动该Service来使用它了。启动一个Service使用Context.startService(Intent intent)方法,你会发现这和启动一个Activity非常相似,也是传递一个Intent对象。当我们调用startService()方法时,被调用的Service会调用它的onCreate()方法(如果该Service还未创建),接着调用onStart()方法。一旦Service启动后将一直运行直到调用了Context.stopService()或者stopSelf()。

       
我们可以调用startService()方法来启动一个Service,也可以通过bindService()方法来绑定一个Service。和调用startService()方法一样,Service会调用onCreate()方法来创建Service(如果它还未创建),但是它不会调用onStart()方法而是调用onBind()返回客户端一个IBinder接口。绑定Service一般使用在远程Service调用。

       
绑定Service需要三个参数:bindService(Intent intent,ServiceConnection conn,Service.BIND_AUTO_CREATE);其中第二个参数是ServiceConnection,我们创建该对象实现其onServiceConnected()和onServiceDisconnected()来判断连接成功或断开连接;第三个参数是如何创建Service,一般指定绑定时自动创建。

实验步骤:

要求:

       
1)可以参考教材上的例子,但一定不要直接拷贝;

       
2)需要在AndroidManifest.xml文件中声明服务;

       
3)将实验代码和运行效果截图放在实验报告中提交。

1.实现进程内服务调用

1)建立一个项目“LocalServiceDemo”

V8r81S.png
V8rlff.png

2)实现一个“MyLocalService”服务

在项目上右键,选择”New”->”Other”,然后选择Android文件夹下的“Android Object”,单击”Next”,选择”Service”

V8rMkt.png
V8rQtP.png
V8ruTI.png
V8r3p8.png

完成以上步骤后会创建一个名为:“MyLocalService.java”的文件,这样一个基本的服务就创建成功了。接下去可以再服务里面添加代码了。

3)在LocalServiceDemo中调用“MyLocalService”的Compare方法,并将结果显示在界面上

V8rG6g.png上单击右键,选择”New”->”File”,输入“IMyLocalService.aidl”。在新建立的文件中输入一行“package com.example.localservicedemo;”,然后就可以写相关的接口了(接口名为” MyLocalService”, Compare方法请自行设计)。写好接口并保存后,系统会自动生成”IMyLocalService.java”文件。

V8rJXQ.png
V8rtmj.png

4)后续步骤:

添加一个新的Activity(”New”->“Other”),并在其中添加用户界面,绑定事件后调用“MyLocalService”。

V8rN0s.png

2.实现跨进程调用(选做)
可以模仿教材例子修改。具体过程略。

程序一(实现进程内服务调用)源代码

BindService.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
package org.crazyit.service;
/**
* Package: org.crazyit.service
* Created by zyh
* on 2019/6/2
*/
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class BindService extends Service
{
private float max;
private boolean quit;
// 定义onBinder方法所返回的对象
private MyBinder binder = new MyBinder();
// 通过继承Binder来实现IBinder类
public class MyBinder extends Binder // ①
{
public float getCount()
{
// 获取Service的运行状态:count
return max;
}
}
// 必须实现的方法,绑定该Service时回调该方法
float data1;
float data2;
@Override
public IBinder onBind(Intent intent)
{
System.out.println("Service is Binded");
// 返回IBinder对象
return binder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
data1 = Float.parseFloat(intent.getStringExtra("data1"));
data2 = Float.parseFloat(intent.getStringExtra("data2"));
max=data1>data2?data1:data2;
return super.onStartCommand(intent, flags, startId);
}
// Service被创建时回调该方法
@Override
public void onCreate()
{
super.onCreate();
System.out.println("Service is Created");
// 启动一条线程,动态地修改count状态值
new Thread()
{
@Override
public void run()
{
while (!quit)
{
System.out.println("Service中获取到的数据:" + max);
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
//发送广播
Intent intent_max=new Intent();
intent_max.putExtra("max", max);
}
}
}.start();
}
// Service被断开连接时回调该方法
@Override
public boolean onUnbind(Intent intent)
{
System.out.println("Service is Unbinded");
return true;
}
// Service被关闭之前回调该方法
@Override
public void onDestroy()
{
super.onDestroy();
this.quit = true;
System.out.println("Service is Destroyed");
}
}

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
105
106
package org.crazyit.service;
/**
* Package: org.crazyit.service
* Created by zyh
* on 2019/6/2
*/
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.widget.EditText;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity
{
Button bind, unbind, getServiceStatus;
private EditText edit_num1, edit_num2;
// 保持所启动的Service的IBinder对象
BindService.MyBinder binder;
// 定义一个ServiceConnection对象
private ServiceConnection conn = new ServiceConnection()
{
// 当该Activity与Service连接成功时回调该方法
@Override
public void onServiceConnected(ComponentName name
, IBinder service)
{
System.out.println("--Service Connected--");
// 获取Service的onBind方法所返回的MyBinder对象
binder = (BindService.MyBinder) service; // ①
}
// 当该Activity与Service断开连接时回调该方法
@Override
public void onServiceDisconnected(ComponentName name)
{
System.out.println("--Service Disconnected--");
}
};
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 获取程序界面中的start、stop、getServiceStatus按钮
bind = (Button) findViewById(R.id.bind);
unbind = (Button) findViewById(R.id.unbind);
edit_num1 = (EditText) findViewById(R.id.number_one);
edit_num2 = (EditText) findViewById(R.id.number_two);
getServiceStatus = (Button) findViewById(R.id.getServiceStatus);
// 创建启动Service的Intent
final Intent intent = new Intent(this, BindService.class);
bind.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View source)
{
if (edit_num1.getText().toString().trim().equals("")) {
Toast.makeText(MainActivity.this, getString(R.string.number1_empty),
Toast.LENGTH_SHORT).show();
intent.putExtra("data1","0");
intent.putExtra("data2","0");
} else if (edit_num2.getText().toString().trim().equals("")) {
Toast.makeText(MainActivity.this, getString(R.string.number2_empty),
Toast.LENGTH_SHORT).show();
intent.putExtra("data1","0");
intent.putExtra("data2","0");
}
else{
intent.putExtra("data1",edit_num1.getText().toString());
intent.putExtra("data2",edit_num2.getText().toString());}
// System.out.println("Service中获取到的数据1:" + edit_num1.getText().toString());
// System.out.println("Service中获取到的数据1:" + edit_num2.getText().toString());
// 绑定指定Service
startService(intent);
bindService(intent, conn, Service.BIND_AUTO_CREATE);

}
});
unbind.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View source)
{
// 解除绑定Service
unbindService(conn);
}
});
getServiceStatus.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View source)
{
// 获取、并显示Service的count值
Toast.makeText(MainActivity.this,
"Service的max值为:" + binder.getCount(),
Toast.LENGTH_SHORT).show(); // ②
}
});
}
}

main.xml

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
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:stretchColumns="1">
<TableRow>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/number_one"
android:textSize="16sp"/>
<EditText
android:id="@+id/number_two"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/input_number_one"
android:inputType="number"
android:selectAllOnFocus="true"
/>
</TableRow>
<TableRow>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/number_two"
android:textSize="16sp"/>
<!-- android:inputType="number"表明只能数字 -->
<EditText
android:id="@+id/number_one"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/input_number_two"
android:inputType="number"
android:selectAllOnFocus="true"/>
</TableRow>
<Button
android:id="@+id/bind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bind"
android:textSize="14sp" />

<Button
android:id="@+id/getServiceStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/getServiceStatus"
android:textSize="14sp" />

<Button
android:id="@+id/unbind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/unbind"
android:textSize="14sp" />
</TableLayout>

程序一(实现进程内服务调用)功能简介

当只输入一个数时显示继续输入

V8yeII.png
V8yMz8.png

EditText只允许输入数字

V8yuJP.png

输入两个数字

点击绑定Service

V8yuJP.png

日志输出

V8yZdA.png

结果显示

V8yKRf.png

最后解除绑定

程序二(实现跨进程调用)源代码(aidlclient)

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
105
106
107
108
109
package com.bignerdranch.android.aidlclient;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.bignerdranch.android.aidl.IMyAidlInterface;
/**
* Package: org.crazyit.service
* Created by zyh
* on 2019/6/2
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private EditText edit_num1;
private EditText edit_num2;
private EditText edit_max;

private Button btnMax;
private IMyAidlInterface iMyAidlInterface;

private ServiceConnection conn = new ServiceConnection() {
//绑定服务时
@Override
public void onServiceConnected(ComponentName name, IBinder service) {

iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);


}

//断开服务时
@Override
public void onServiceDisconnected(ComponentName name) {

//回收资源
iMyAidlInterface = null;
}
};


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

initView();

//当程序启动时就绑定服务
bindService();
}

private void initView() {
edit_num1 = (EditText) findViewById(R.id.number_one);
edit_num2 = (EditText) findViewById(R.id.number_two);
edit_max = (EditText) findViewById(R.id.number_max);

btnMax = (Button) findViewById(R.id.btn_max);

btnMax.setOnClickListener(this);
}

@Override
public void onClick(View v) {
if (edit_num1.getText().toString().trim().equals("")) {
Toast.makeText(MainActivity.this, getString(R.string.number1_empty),
Toast.LENGTH_SHORT).show();
} else if (edit_num2.getText().toString().trim().equals("")) {
Toast.makeText(MainActivity.this, getString(R.string.number2_empty),
Toast.LENGTH_SHORT).show();
}
else {
int num1 = Integer.parseInt(edit_num1.getText().toString());
int num2 = Integer.parseInt(edit_num2.getText().toString());
int minnum = num1<num2?num1:num2;
try {
//调用远程服务
int res = iMyAidlInterface.Compare(num1, num2) - minnum;
edit_max.setText(res + "");
} catch (RemoteException e) {
e.printStackTrace();
edit_max.setText("错误了");
}
}
}

private void bindService() {
//获取服务端
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.bignerdranch.android.aidl", "com.bignerdranch.android.aidl.IRemoteService"));

bindService(intent, conn, Context.BIND_AUTO_CREATE);
}

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

IMyAidlInterface.aidl

1
2
3
4
5
6
7
8
9
// IMyAidlInterface.aidl
package com.bignerdranch.android.aidl;

// Declare any non-default types here with import statements

interface IMyAidlInterface {

int Compare(int num1, int num2);
}

程序二(实现跨进程调用)功能简介

与程序一样有文本框检测功能

VGPBef.png

VGPDw8.png

结果展示
VGPrTS.png

日志输出
VGPwOP.png

总结

这次实验学到了很多,本地服务和远程服务让我对安卓的服务端有了一定的了解,特别是远程服务,遇到了一个又一个坑,不过果然只要时间长并没有不能写出来的代码,写了5小时完成实验,虽枯燥但值得

本文使用 CC BY-NC-SA 3.0 中国大陆 协议许可
具体请参见 知识共享协议

本文链接:https://zyhang8.github.io/2019/10/31/android-exp5/