美文网首页
Service 组件的启动过程

Service 组件的启动过程

作者: JunL_Dev | 来源:发表于2020-01-13 11:44 被阅读0次
    Start

    前言

    与 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 组件在进程中的启动

    1. Counter 组件向 ActivityManagerService 发送一个绑定 CounterService 组件的进程间通信请求。

    2. ActivityManagerService 发现用来运行 CounterService 组件的应用程序进程即为 Counter 组件所运行在的应用程序进程,因此,它就直接通知该应用程序进程将 CounterService 组件启动起来。

    3. CounterService 组件启动起来之后,ActivityManagerService 就请求它返回一个 Binder 本地对象,以便 Counter 组件可以通过这个 Binder 本地对象来和 CounterService 组件建立联系。

    4. ActivityManagerService 将前面从 CounterService 组件中获得的一个 Binder 本地对象发送给 Counter 组件。

    5. Counter 组件获得了 ActivityManagerService 给他发送的 Binder 组件对象之后,就可以通过它来获得 CounterService 组件的一个访问接口。Counter 组件以后就可以通过这个访问接口来使用 CounterService 组件所提供的服务,这就相当于将 CounterService 组件绑定在 Counter 组件内部了。

    3. Service 组件在新进程中的启动

    如果给 CounterService 组件的 android:process 属性设置为"work",那么 CounterService 将会在一个 "work" 的应用程序进程中启动。启动过如下:

    1. Counter 组件向 ActivityManagerService 发送一个启动 Service 组件的进程间通信请求。

    2. ActivityManagerService 发现用来运行 Service 组件的应用程序进程不存在,因此,它就会首先将 Service 组件的信息保存下来,接着再创建一个新的应用程序进程。

    3. 新的应用程序进程启动完成之后,就会向 ActivityManagerService 发送一个启动完成的精彩间通信请求,以便 ActivityManagerService 可以继续执行启动 Service 组件操作。

    4. ActivityManagerService 将第 2 步保存下来的 Service 组件信息发送给第 2 步创建的应用程序进程,以便它可以将 Service 组件启动起来。

    4. 总结

    Service 组件可以呗 Activity 组件启动,也可以被其他的 Service 组件启动。同时,它既可以在启动它的 Activity 组件或者 Service 组件所在的应用程序中启动,也可以在一个新的应用程序进程中启动。当一个 Service 组件被 Activity 组件或者另外一个 Service 组件启动时,我们可以将它们绑定起来,以便启动者可以方便得到它的访问接口。

    申明:开始的图片来源网络,侵删

    参考文献:《Android系统源代码情景分析》

    相关文章

      网友评论

          本文标题:Service 组件的启动过程

          本文链接:https://www.haomeiwen.com/subject/bycfactx.html