前言
与 Activity 组件的启动方式类似,Service 组件的启动方式也分为显式和隐式两种。对于隐式启动来说,我们只需要知道他的组件名称。而对于显式来说,我们需要知道它的类名称。
1. 准备
ICounterCallback.java
public interface ICounterCallback {
void count(int val);
}
这个定义了一个计数器回调接口 ICounterCallback,它只有一个成员函数 count,用来将 CounterService 组件的当前计数实时地更新到 Counter 组件的用户界面上。
ICounterService.java
public interface ICounterService {
public void startCounter(int initVal, ICounterCallback callback);
public void stopCounter();
}
这个定义了一个计数器接口 ICounterService,它有两个成员函数 startCounter 和 stopCounter,其中,前者用来启动计数器,后者用来停止计数器。在启动计数器时,可以指定计数器的初始值,以及更新用户界面的回调接口。
CounterService.java
package com.android.junl_note.Android.service;
import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
public class CounterService extends Service implements ICounterService {
private final static String LOG_TAG = CounterService.class.getSimpleName();
private boolean stop = false;
private ICounterCallback counterCallback = null;
private final IBinder binder = new CounterBinder();
public class CounterBinder extends Binder {
public CounterService getService() {
return CounterService.this;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onCreate() {
super.onCreate();
Log.i(LOG_TAG, "Counter Service Created");
}
@Override
public void startCounter(final int initVal, ICounterCallback callback) {
counterCallback = callback;
AsyncTask<Integer, Integer, Integer> task = new AsyncTask<Integer, Integer, Integer>() {
@Override
protected Integer doInBackground(Integer... integers) {
Integer initCounter = integers[0];
stop = false;
while (!stop) {
publishProgress(initCounter);
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
initCounter++;
}
return initCounter;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
int val = values[0];
counterCallback.count(val);
}
@Override
protected void onPostExecute(Integer integer) {
counterCallback.count(initVal);
}
};
task.execute(initVal);
}
@Override
public void stopCounter() {
stop = true;
}
}
CounterService 是应用程序 Counter 的一个 Service 组件,因此,它必须要从 Service 类继续下来。CounterService 组件同时实现了一个计数器接口 ICounterService,用来提供计数器服务。在 CounterService 组件启动时,它的成员函数 onCreate 就会被调用;而在 CounterService 组件被绑定时,它的成员函数 onBind 就会被调用。
CounterService 组件有一个成员变量 binder,它指向一个类型为 CounterBinder 的 Binder 本地对象。当 CounterService 组件被绑定时,即它的成员函数 onBind 被调用时,它就会将内部的 Binder 本地对象 binder 返回给绑定者,以便绑定者可以通过这个 Binder 本地对象来获得它的一个计数器访问接口。
⚠️ 这里之所以要返回一个 Binder 本地对象给绑定者,是因为绑定者与 CounterService 组件可能是运行在两个不同的应用程序进程中的。不过,在这个应用实例中,启动 CounterService 组件的 Counter 组件与 CounterService 组件是运行在同一个应用程序进程中的。
Counter.java
package com.android.junl_note.Android.service;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
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.TextView;
import com.android.junl_note.R;
public class Counter extends Activity implements View.OnClickListener, ICounterCallback {
private final static String LOG_TAG = Counter.class.getSimpleName();
private Button startButton = null;
private Button stopButton = null;
private TextView counterText = null;
private ICounterService counterService = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_counter);
startButton = findViewById(R.id.button_start);
stopButton = findViewById(R.id.button_stop);
counterText = findViewById(R.id.textview_counter);
startButton.setOnClickListener(this);
stopButton.setOnClickListener(this);
startButton.setEnabled(true);
stopButton.setEnabled(false);
Intent bindIntent = new Intent(Counter.this, CounterService.class);
bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
Log.i(LOG_TAG, "Counter Activity Create");
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
@Override
public void onClick(View v) {
if (v.equals(startButton)) {
if (counterService != null) {
counterService.startCounter(0, this);
startButton.setEnabled(false);
stopButton.setEnabled(true);
}
} else if (v.equals(stopButton)) {
if (counterService != null) {
counterService.stopCounter();
startButton.setEnabled(true);
stopButton.setEnabled(false);
}
}
}
@Override
public void count(int val) {
String text = String.valueOf(val);
counterText.setText(text);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
counterService = ((CounterService.CounterBinder) service).getService();
Log.i(LOG_TAG, "Counter Service Connected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
counterService = null;
Log.i(LOG_TAG, "Counter Service Disconnected");
}
};
}
Counter 是继承 Activity 组件的,实现了计数器回调接口 ICounterCallback,以便可以实时的地更新用户界面上的计数器。
activity_counter.xml
<?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:gravity="center"
android:orientation="vertical"
tools:context=".Android.service.Counter">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginBottom="10dp"
android:background="@color/main_bg"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="4dp"
android:gravity="center"
android:text="Counter :"
android:textColor="@color/main_white"
android:textSize="16sp" />
<TextView
android:id="@+id/textview_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:gravity="center"
android:text="0"
android:textColor="@color/main_white"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/button_start"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:background="@color/main_bg"
android:gravity="center"
android:text="Start Countter"
android:textColor="@color/main_white"
android:textSize="16sp" />
<Button
android:id="@+id/button_stop"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginLeft="20dp"
android:layout_weight="1"
android:background="@color/main_bg"
android:gravity="center"
android:text="Stop Countter"
android:textColor="@color/main_white"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:process=":main">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Android.service.Counter" />
<service
android:name=".Android.service.CounterService"
android:enabled="true">
</service>
</application>
这是应用程序 Counter 的配置文件。由于在程序中使用到了 Activity 组件 Counter 和 Service 组件 CounterService。
2. Service 组件在进程中的启动
-
Counter 组件向 ActivityManagerService 发送一个绑定 CounterService 组件的进程间通信请求。
-
ActivityManagerService 发现用来运行 CounterService 组件的应用程序进程即为 Counter 组件所运行在的应用程序进程,因此,它就直接通知该应用程序进程将 CounterService 组件启动起来。
-
CounterService 组件启动起来之后,ActivityManagerService 就请求它返回一个 Binder 本地对象,以便 Counter 组件可以通过这个 Binder 本地对象来和 CounterService 组件建立联系。
-
ActivityManagerService 将前面从 CounterService 组件中获得的一个 Binder 本地对象发送给 Counter 组件。
-
Counter 组件获得了 ActivityManagerService 给他发送的 Binder 组件对象之后,就可以通过它来获得 CounterService 组件的一个访问接口。Counter 组件以后就可以通过这个访问接口来使用 CounterService 组件所提供的服务,这就相当于将 CounterService 组件绑定在 Counter 组件内部了。
3. Service 组件在新进程中的启动
如果给 CounterService 组件的 android:process 属性设置为"work",那么 CounterService 将会在一个 "work" 的应用程序进程中启动。启动过如下:
-
Counter 组件向 ActivityManagerService 发送一个启动 Service 组件的进程间通信请求。
-
ActivityManagerService 发现用来运行 Service 组件的应用程序进程不存在,因此,它就会首先将 Service 组件的信息保存下来,接着再创建一个新的应用程序进程。
-
新的应用程序进程启动完成之后,就会向 ActivityManagerService 发送一个启动完成的精彩间通信请求,以便 ActivityManagerService 可以继续执行启动 Service 组件操作。
-
ActivityManagerService 将第 2 步保存下来的 Service 组件信息发送给第 2 步创建的应用程序进程,以便它可以将 Service 组件启动起来。
4. 总结
Service 组件可以呗 Activity 组件启动,也可以被其他的 Service 组件启动。同时,它既可以在启动它的 Activity 组件或者 Service 组件所在的应用程序中启动,也可以在一个新的应用程序进程中启动。当一个 Service 组件被 Activity 组件或者另外一个 Service 组件启动时,我们可以将它们绑定起来,以便启动者可以方便得到它的访问接口。
申明:开始的图片来源网络,侵删
参考文献:《Android系统源代码情景分析》
网友评论