实验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”
2)实现一个“MyLocalService”服务 在项目上右键,选择”New”->”Other”,然后选择Android文件夹下的“Android Object”,单击”Next”,选择”Service”
完成以上步骤后会创建一个名为:“MyLocalService.java”的文件,这样一个基本的服务就创建成功了。接下去可以再服务里面添加代码了。
3)在LocalServiceDemo中调用“MyLocalService”的Compare方法,并将结果显示在界面上 在 上单击右键,选择”New”->”File”,输入“IMyLocalService.aidl”。在新建立的文件中输入一行“package com.example.localservicedemo;”,然后就可以写相关的接口了(接口名为” MyLocalService”, Compare方法请自行设计)。写好接口并保存后,系统会自动生成”IMyLocalService.java”文件。
4)后续步骤: 添加一个新的Activity(”New”->“Other”),并在其中添加用户界面,绑定事件后调用“MyLocalService”。
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;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; private MyBinder binder = new MyBinder(); public class MyBinder extends Binder // ① { public float getCount () { return max; } } float data1; float data2; @Override public IBinder onBind (Intent intent) { System.out.println("Service is Binded" ); 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); } @Override public void onCreate () { super .onCreate(); System.out.println("Service is Created" ); 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(); } @Override public boolean onUnbind (Intent intent) { System.out.println("Service is Unbinded" ); return true ; } @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;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; BindService.MyBinder binder; private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected (ComponentName name , IBinder service) { System.out.println("--Service Connected--" ); binder = (BindService.MyBinder) 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); 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); 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());} startService(intent); bindService(intent, conn, Service.BIND_AUTO_CREATE); } }); unbind.setOnClickListener(new OnClickListener() { @Override public void onClick (View source) { unbindService(conn); } }); getServiceStatus.setOnClickListener(new OnClickListener() { @Override public void onClick (View source) { 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" /> <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 >
程序一(实现进程内服务调用)功能简介 当只输入一个数时显示继续输入
EditText只允许输入数字
输入两个数字
点击绑定Service
日志输出
结果显示
最后解除绑定
程序二(实现跨进程调用)源代码(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;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); }
程序二(实现跨进程调用)功能简介 与程序一样有文本框检测功能
结果展示
日志输出
总结 这次实验学到了很多,本地服务和远程服务让我对安卓的服务端有了一定的了解,特别是远程服务,遇到了一个又一个坑,不过果然只要时间长并没有不能写出来的代码,写了5小时完成实验,虽枯燥但值得
本文使用 CC BY-NC-SA 3.0 中国大陆 协议许可 具体请参见 知识共享协议 本文链接: https://zyhang8.github.io/2019/10/31/android-exp5/