Service组件应用实例
这是通过Service实现的一个计数器服务功能实例。
直接先上代码:
ICounterCallback.java
1 package com.example.counter; 2 3 /** 4 * 火龙裸 5 * */ 6 public interface ICounterCallback { 7 void count(int val); 8 }
这个文件定义了一个计数器回调接口ICounterCallback,它只有一个成员函数count,用来将CounterService服务组件的当前“计数结果”,实时地更新到对应的CounterActivity组件上,进行用户界面显示。
ICounterService.java
1 package com.example.counter; 2 3 /** 4 * 火龙裸 5 * */ 6 public interface ICounterService { 7 void strtCounter(int initVal, ICounterCallback callback); 8 void stopCounter(); 9 }
这个文件定义了一个计数器接口ICounterService,它有两个成员函数startCounter和stopCounter;其中,前者用来启动计数器,后者用来停止计数器。在启动计数器时,可以指定计数器的初始值,以及更新用户界面的回调接口。
CounterService.java
1 package com.example.counter; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.AsyncTask; 6 import android.os.Binder; 7 import android.os.IBinder; 8 import android.util.Log; 9 10 import androidx.annotation.Nullable; 11 12 /** 13 * 火龙裸 14 * */ 15 public class CounterService extends Service implements ICounterService { 16 private static final String TAG = "CounterService"; 17 18 private boolean stop = false; 19 private ICounterCallback counterCallback = null; 20 21 private final CounterBinder binder = new CounterBinder(); 22 23 public class CounterBinder extends Binder{ 24 public CounterService getService() { 25 return CounterService.this; 26 } 27 } 28 29 @Override 30 public void onCreate() { 31 super.onCreate(); 32 Log.e(TAG, "onCreate: ============="); 33 } 34 35 @Nullable 36 @Override 37 public IBinder onBind(Intent intent) { 38 return binder; 39 } 40 41 42 43 @Override 44 public void strtCounter(int initVal, ICounterCallback callback) { 45 counterCallback = callback; 46 47 AsyncTask<Integer, Integer, Integer> task = new AsyncTask<Integer, Integer, Integer>() { 48 @Override 49 protected Integer doInBackground(Integer... integers) { 50 Integer initCounter = integers[0]; 51 52 stop = false; 53 54 while (!stop) { 55 publishProgress(initCounter); 56 57 try { 58 Thread.sleep(1000); 59 } catch (InterruptedException e) { 60 e.printStackTrace(); 61 } 62 63 initCounter++; 64 } 65 66 return initCounter; 67 } 68 69 @Override 70 protected void onProgressUpdate(Integer... values) { 71 super.onProgressUpdate(values); 72 73 int val = values[0]; 74 counterCallback.count(val); 75 } 76 77 @Override 78 protected void onPostExecute(Integer val) { 79 counterCallback.count(val); 80 } 81 }; 82 83 task.execute(initVal); 84 Log.e(TAG, "strtCounter: 启动AsyncTask"); 85 } 86 87 @Override 88 public void stopCounter() { 89 stop = true; 90 Log.e(TAG, "stopCounter: 停止AsyncTask 任务标志位"); 91 } 92 }
CounterService是一个Service组件,因此,它必须要从Service类继承下来。CounterService组件同时实现了计数器接口ICounterService,用来提供计数器服务。在CounterService组件启动时,它的成员函数onCreate就会被调用;而在CounterService组件被绑定时,它的成员函数onBind就会被调用。
注意:CounterService组件有一个成员变量binder,它指向一个类型为CounterBinder的Binder本地对象。当CounterService组件被绑定时,它就会将内部的Binder本地对象binder返回给绑定着,以便绑定者可以通过这个Binder本地对象来获得它的一个计数器访问接口。
这里之所以要返回一个Binder本地对象给绑定者,是因为绑定着与CounterService组件可能是运行在两个不同的应用程序进程中的。不过本例中,启动CounterService组件的“CounterActivity组件”和“CounterService”组件是运行在同一个应用程序进程中的。
CounterService组件的成员函数startCounter和stopCounter分别用来启动和停止内部的计数器;其中,成员函数startCounter使用一个异步任务task来启动一个计数器,成员函数stopCounter通过将CounterService组件的成员变量stop设置为true来停止这个计数器。
当计数器被停止执行时,异步任务task的成员函数onPostExecute就会被调用,它主要是使用CounterService组件内部的一个ICounterCallback接口将计数器的最终技术结果更新到用户界面上显示出来。
注意:在异步任务task的三个成员函数中,只有成员函数doInBackground是在后台线程中执行的,其余两个成员函数onProgressUpdate和onPostExecute都是在应用程序的主线程中执行的,因此,它们可以被用来更新用户界面。
CounterActivity.java
1 package com.example.counter; 2 3 import androidx.appcompat.app.AppCompatActivity; 4 5 import android.content.ComponentName; 6 import android.content.Intent; 7 import android.content.ServiceConnection; 8 import android.os.Bundle; 9 import android.os.IBinder; 10 import android.util.Log; 11 import android.view.View; 12 import android.widget.Button; 13 import android.widget.TextView; 14 15 /** 16 * 火龙裸 17 * */ 18 public class CounterActivity extends AppCompatActivity implements View.OnClickListener, ICounterCallback { 19 private static final String TAG = "CounterActivity"; 20 21 private Button btn_start; 22 private Button btn_stop; 23 private TextView tv_count; 24 25 private ICounterService counterService = null; 26 27 @Override 28 protected void onCreate(Bundle savedInstanceState) { 29 super.onCreate(savedInstanceState); 30 setContentView(R.layout.activity_counter); 31 btn_start = findViewById(R.id.btn_start); 32 btn_stop = findViewById(R.id.btn_stop); 33 tv_count = findViewById(R.id.tv_count); 34 btn_start.setOnClickListener(this); 35 btn_stop.setOnClickListener(this); 36 37 Intent serviceIntent = new Intent(CounterActivity.this, CounterService.class); 38 bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE); 39 } 40 41 @Override 42 public void onClick(View view) { 43 switch (view.getId()){ 44 case R.id.btn_start: 45 counterService.strtCounter(0, this); 46 break; 47 case R.id.btn_stop: 48 counterService.stopCounter(); 49 break; 50 } 51 } 52 53 /** 54 * 计数器服务的回调 55 * */ 56 @Override 57 public void count(int val) { 58 tv_count.setText("计算值为:" + val); 59 } 60 61 @Override 62 protected void onDestroy() { 63 super.onDestroy(); 64 unbindService(serviceConnection); 65 } 66 67 private ServiceConnection serviceConnection = new ServiceConnection() { 68 @Override 69 public void onServiceConnected(ComponentName componentName, IBinder iBinder) { 70 counterService = ((CounterService.CounterBinder)iBinder).getService(); 71 Log.e(TAG, "onServiceConnected: ======================>"); 72 } 73 74 @Override 75 public void onServiceDisconnected(ComponentName componentName) { 76 counterService = null; 77 Log.e(TAG, "onServiceDisconnected: >>>>>>>>>>>>>>>"); 78 } 79 }; 80 }
CounterActivity是应用程序的根Activity组件,因此,它必须要从类Activity继承下来。CounterActivity同时实现了计数器回调接口ICounterCallback,以便可以实时地回调更新用户界面上的计数值。
CounterActivity组件被启动时,它的成员函数onCreate就会被调用,这时候它就会调用其父类ContextWrapper的成员函数bindService来启动CounterService组件。当CounterService组件启动起来之后,CounterActivity组件内部的ServiceConnection对象serviceConnection的成员函数onServiceConnected就会被调用,这时候CounterActivity组件就会获得CounterService服务组件中的一个Binder本地对象。由于CounterActivity组件和CounterService服务组件是运行在同一个应用程序进程中的,因此,ServiceConnection对象serviceConnection的成员函数onServiceConnected可以安全地将前面获得的一个Binder本地对象转换为一个类型为CounterBinder的对象,接着再调用它的成员函数getService来获得一个计数器接口。有了这个计数器接口之后,CounterActivity组件就相当于将CounterService服务组件绑定起来了。
由于CounterActivity组件在启动时绑定了CounterService组件,因此当它销毁时,即它的成员函数onDestrory被调用时,它就必须要调用父类ContextWrapper的成员函数unbindService来解绑CounterService服务组件。解绑成功后,CounterActivity组件内部的ServiceConnection对象serviceConnection的成员函数onServiceDisconnected就会被调用,用来执行清理工作。
activity_counter.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 tools:context=".CounterActivity"> 8 9 <Button 10 android:id="@+id/btn_start" 11 android:layout_width="wrap_content" 12 android:layout_height="wrap_content" 13 android:text="开始" 14 app:layout_constraintBottom_toBottomOf="parent" 15 app:layout_constraintHorizontal_bias="0.498" 16 app:layout_constraintLeft_toLeftOf="parent" 17 app:layout_constraintRight_toRightOf="parent" 18 app:layout_constraintTop_toTopOf="parent" 19 app:layout_constraintVertical_bias="0.196" /> 20 21 <TextView 22 android:id="@+id/tv_count" 23 android:layout_width="wrap_content" 24 android:layout_height="wrap_content" 25 android:text="计算值为:" 26 android:textSize="20sp" 27 app:layout_constraintBottom_toBottomOf="parent" 28 app:layout_constraintHorizontal_bias="0.498" 29 app:layout_constraintLeft_toLeftOf="parent" 30 app:layout_constraintRight_toRightOf="parent" 31 app:layout_constraintTop_toTopOf="parent" 32 app:layout_constraintVertical_bias="0.311" /> 33 34 <Button 35 android:id="@+id/btn_stop" 36 android:layout_width="wrap_content" 37 android:layout_height="wrap_content" 38 android:text="停止" 39 app:layout_constraintBottom_toBottomOf="parent" 40 app:layout_constraintHorizontal_bias="0.498" 41 app:layout_constraintLeft_toLeftOf="parent" 42 app:layout_constraintRight_toRightOf="parent" 43 app:layout_constraintTop_toTopOf="parent" 44 app:layout_constraintVertical_bias="0.409" /> 45 46 </androidx.constraintlayout.widget.ConstraintLayout>
AndroidManifest.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.example.counter"> 4 5 <application 6 android:allowBackup="true" 7 android:icon="@mipmap/ic_launcher" 8 android:label="@string/app_name" 9 android:roundIcon="@mipmap/ic_launcher_round" 10 android:supportsRtl="true" 11 android:theme="@style/Theme.Counter"> 12 <activity 13 android:name=".CounterActivity" 14 android:exported="true"> 15 <intent-filter> 16 <action android:name="android.intent.action.MAIN" /> 17 18 <category android:name="android.intent.category.LAUNCHER" /> 19 </intent-filter> 20 </activity> 21 <service 22 android:name=".CounterService" 23 android:enabled="true" /> 24 </application> 25 26 </manifest>