Android-Service小結(jié)

雖然是Android四大組件之一,但從開(kāi)發(fā)Android以來(lái)基本上沒(méi)怎么用過(guò),真是慚愧,今天有時(shí)間來(lái)倒騰倒騰Service,作為一個(gè)假Android,還是總結(jié)下好 ??,省得后面需要了又到處去找。
一說(shuō)起Service,印象里只有在需要處理耗時(shí)的后臺(tái)任務(wù)時(shí)才會(huì)想起它,實(shí)際開(kāi)發(fā)中應(yīng)該也是這種情況居多吧。好了,廢話不多說(shuō)。下面開(kāi)始整理:
按照大的分類來(lái)說(shuō)Service可以分為兩種:Start ServiceBound Service,他們都要繼承自Service基類。

1. Start Service

1.先上代碼,首先,自定義Service:
public class MyService extends Service {

    private static final String TAG = "MyService";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.w(TAG, "in onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Log.w(TAG, "in onStartCommand");
        Log.w(TAG, "MyService: " + this);
        String name = intent.getStringExtra("name");
        Log.w(TAG, "name: " + name);
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.w(TAG, "in onDestroy");
    }
}

自定義Service只需要實(shí)現(xiàn)基類中的onBind(...)方法就可以了,這個(gè)方法的返回值只對(duì)Bound Service有用,這里直接返回nll就可以了。
后面的onCreate()onStartCommand(...),onDestroy(...)都是生命周期方法,一個(gè)service start的時(shí)候會(huì)先調(diào)用onCreate,再調(diào)用onStartCommand。
注意:同一個(gè)Service,只能用startService(...)啟動(dòng)一次,后面再啟動(dòng)也不會(huì)執(zhí)行onCreate()方法了,直接執(zhí)行onStartCommand(...),同理,一個(gè)service不管被啟動(dòng)多少次,只需要調(diào)用一次stopService()(或者service自己調(diào)用stopSelf())即可終止service。

然后看下onStartCommand(...)這個(gè)方法

onStartCommand(Intent intent, int flags, int startId)

intent:包含外部Client啟動(dòng)service時(shí)傳遞的一些參數(shù)。
flags: 默認(rèn)是0,對(duì)應(yīng)的常量是START_STICKY_COMPATIBILITY
startId: 當(dāng)多次start該Service的時(shí)候,startId會(huì)遞增。

返回值:
START_NOT_STICKY: 當(dāng)service因系統(tǒng)內(nèi)存不足被kill后,即時(shí)未來(lái)系統(tǒng)內(nèi)存足夠了也不會(huì)主動(dòng)去重新創(chuàng)建該service,除非程序中再次調(diào)用startService()啟動(dòng)該service.
START_STICKY: 和START_NOT_STICKY不同,被kill后,未來(lái)系統(tǒng)內(nèi)存足夠后會(huì)自動(dòng)重建該service,重建后會(huì)調(diào)用onStartCommand(),但其中的intent參數(shù)會(huì)是null,pendingintent除外。
START_REDELIVER_INTENT: 和START_STICKY唯一的不同是,重建后onstartCommand()方法中的intent為最后一次startService()中的intent.
最常用的就這三種,其他的類型需要的時(shí)候再去翻一下即可。

最后需要注意的是,如果用戶強(qiáng)制kill了service,onDestroy()是不會(huì)執(zhí)行的。

2. AndroidManifest.xml中注冊(cè)

必須在AndroidManifest.xml中進(jìn)行注冊(cè),如果不注冊(cè)的話也不會(huì)崩潰異常,所以一定要注意:

<service
            android:name=".MyService"
            android:enabled="true"
            android:exported="false"
            android:icon="@drawable/ic_launcher_background"
            android:isolatedProcess="true"
            android:label="@string/app_name"
            android:permission="@string/app_name"
            android:process=":Myservice"/>

如果service要運(yùn)行在單獨(dú)的進(jìn)程中,需要在process中指明此進(jìn)程名稱
當(dāng)此service需要對(duì)其他App開(kāi)放時(shí),exported設(shè)為true即可。

3. 啟動(dòng)service
Intent serviceIntent = new Intent(MainActivity.this, MyService.class);
startService(serviceIntent);

調(diào)用startService(...)即可啟動(dòng)service。這里,當(dāng)Client可通過(guò)intent傳遞參數(shù)給service,當(dāng)service需要傳遞參數(shù)給Client的時(shí)候,可以使用廣播,Client只要注冊(cè)該廣播就可以了。

以上就是Start Service的簡(jiǎn)單用法了。

2. Bound Service

Bound ServiceStart Service不同的是它的生命周期依賴于Client,當(dāng)Client銷(xiāo)毀的時(shí)候,它也就不存在了。另外,Bound Service可以通過(guò)Binder對(duì)象來(lái)和Client進(jìn)行通信。

Bound Service的使用過(guò)程是這樣的:
1. 自定義Service繼承自Service,并實(shí)現(xiàn)onBind()方法,返回具體的Binder對(duì)象
2.Client通過(guò)ServiceConnection接口自定義一個(gè)ServiceConnection,實(shí)現(xiàn)onServerConnected(),并獲取Service端Binder實(shí)例,通過(guò)binder實(shí)例進(jìn)行service端其他公共方法的調(diào)用。
3.Client端通過(guò)bindService(...)方法將Service綁定到該Client上
4.Client在恰當(dāng)?shù)臅r(shí)候(比如在onDestory的時(shí)候),通過(guò)unBindService()解綁service.

另外,在Bound Service的具體使用過(guò)程中,根據(jù)onBind中返回的Binder對(duì)象的定義方式不同,又具體分為三種方式,每種方式又不同的特點(diǎn),適應(yīng)不同的場(chǎng)景。

2.1 繼承Binder類

這是Bound Service最常用也是最簡(jiǎn)單的一種

局限:
Client和Service必須同屬于一個(gè)進(jìn)程,不能實(shí)現(xiàn)進(jìn)程間通信(IPC)。否則會(huì)出現(xiàn)類似于 "android.os.BinderProxy cannot be cast to xxxx." 的錯(cuò)誤

自定義Service:

public class BoundService1 extends Service {

    private static final String TAG = "BoundService1";

    /////// 自定義binder
    private MyBinder mBinder = new MyBinder();
    public class MyBinder extends Binder {
        BoundService1 getService() {
            return BoundService1.this;
        }
    }
    /////// 自定義binder

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.w(TAG, "in onBind");
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.w(TAG, "in onUnbind" );
        return super.onUnbind(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.w(TAG, "in onCreate");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.w(TAG, "in onDestroy");
    }
}

AndroidManifest.xml中注冊(cè):

<service android:name=".BoundService1"/>

Client中使用:

public class BActivity extends AppCompatActivity {

    private static final String TAG = "BActivity";

    private Button bindServiceBtn;
    private Button unBindServiceBtn;
    private Button startIntentService;

    private Intent serviceIntent;

    private ServiceConnection sc = new MyServiceConnection();
    private MyBinder mBinder;
    private BoundService1 mBindService;
    private boolean mBound;

    //////////////自定義ServiceConnection
    private class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            Log.w(TAG, "in MyServiceConnection onServiceConnected");
            mBinder = (MyBinder) binder;
            mBindService = mBinder.getService();

            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.w(TAG, "in MyServiceConnection onServiceDisconnected");
            mBound = false;
        }
    }
    //////////////自定義ServiceConnection

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

        bindServiceBtn = findViewById(R.id.bind_service_btn);
        unBindServiceBtn = findViewById(R.id.unBind_service_btn);
        startIntentService = findViewById(R.id.start_intent_service_btn);

        bindServiceBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(BActivity.this, BoundService1.class);
                bindService(intent, sc, Context.BIND_AUTO_CREATE);
            }
        });

        unBindServiceBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                excuteUnBindService();
            }
        });

        startIntentService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(BActivity.this, MyIntentService.class);
                startService(intent);
            }
        });
    }

    private void excuteUnBindService() {
        if (mBound) {
            unbindService(sc);
            mBound = false;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.w(TAG, "in onDestroy");
        excuteUnBindService();
    }
}

同樣這里即使多次點(diǎn)擊綁定,也是會(huì)綁定一次,BoundService1中的生命周期方法也只會(huì)執(zhí)行一次綁定。

2.2 使用Messenger

通過(guò)Messenger方式返回Binder對(duì)象可以不用考慮Client-Service是否屬于同一個(gè)進(jìn)程的問(wèn)題,并且,可以實(shí)現(xiàn)Client-Service之間的雙向通信。

局限:
不支持嚴(yán)格意義上的多線程并發(fā)處理,實(shí)際上是以隊(duì)列去處理

自定義Service:

public class BoundService2 extends Service {

    private static final String TAG = "BoundService2";
    public static final int MSG_FROM_CLIENT_TO_SERVICE = 1;
    public static final int MSG_FROM_SERVICE_TO_CLIENT = 2;

    private Messenger mClientMessenger;
    private Messenger mServiceMessenger = new Messenger(new ServiceHandler());

    class ServiceHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            Log.w(TAG, "thread name: " + Thread.currentThread().getName());
            switch (msg.what) {
                case MSG_FROM_CLIENT_TO_SERVICE:
                    Log.w(TAG, "receive msg from client");
                    mClientMessenger = msg.replyTo;

                    // service發(fā)送消息給client
                    Message toClientMsg = Message.obtain(null, MSG_FROM_SERVICE_TO_CLIENT);
                    try {
                        Log.w(TAG, "service begin send msg to client");
                        mClientMessenger.send(toClientMsg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;

                default:
                    super.handleMessage(msg);
            }
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.w(TAG, "in onBind");
        return mServiceMessenger.getBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.w(TAG, "in onUnbind");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        Log.w(TAG, "in onDestroy");
        super.onDestroy();
    }
}

AndroidManifest.xml中注冊(cè):

<service android:name=".BoundService2"/>

Client中的使用:

public class CActivity extends AppCompatActivity {

    private static final String TAG = "CActivity";

    private Button bindServiceBtn;
    private Button unBindServiceBtn;
    private Button sendMsgToService;

    private ServiceConnection sc = new MyServiceConnection();
    private boolean mBound;
    private Messenger mServiceMessenger;
    private Handler mClientHandler = new MyClientHandler();
    private Messenger mClientMessenger = new Messenger(mClientHandler);

    /////////// Message handler && ServiceConnection
    private class MyClientHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == BoundService2.MSG_FROM_SERVICE_TO_CLIENT) {
                Log.w(TAG, "receive msg from service.");
            }
        }
    }

    private class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            Log.w(TAG, "in MyServiceConnection onServiceConnected.");

            mServiceMessenger = new Messenger(binder);
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.w(TAG, "in MyServiceConnection onServiceDisconnected.");
            mBound = false;
        }
    }
    /////////// Message handler && ServiceConnection

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

        bindServiceBtn = findViewById(R.id.bind_service_btn);
        unBindServiceBtn = findViewById(R.id.unBind_service_btn);
        sendMsgToService = findViewById(R.id.send_msg_to_service);

        bindServiceBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(CActivity.this, BoundService2.class);
                bindService(intent, sc, Context.BIND_AUTO_CREATE);
            }
        });

        unBindServiceBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                excuteUnBindService();
            }
        });

        sendMsgToService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sayHello();
            }
        });
    }

    public void sayHello() {
        if (!mBound) {
            return;
        }

        Message msg = Message.obtain(null, BoundService2.MSG_FROM_CLIENT_TO_SERVICE,0,0);
        msg.replyTo = mClientMessenger;

        try {
            mServiceMessenger.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private void excuteUnBindService() {
        if (mBound) {
            unbindService(sc);
            mBound = false;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.w(TAG, "in onDestroy");
        excuteUnBindService();
    }
}

這里在ServiceConnection中的onServiceConnected(...)方法中根據(jù)service端傳來(lái)的binder參數(shù)實(shí)例化一個(gè)Messenger對(duì)象,就可以使用它發(fā)送消息給Service了,
另外,在給service發(fā)送消息的時(shí)候,在消息中夾帶一個(gè)Client端的Messenger實(shí)例:

Message msg = Message.obtain(null, BoundService2.MSG_FROM_CLIENT_TO_SERVICE,0,0);
msg.replyTo = mClientMessenger;

在service端獲取到該Messenger實(shí)例,就可以在service端發(fā)送消息回Client端了。

3. AIDL (Android Interface Definition Language)

一般情況下,Messenger這種方式都是可以滿足需求的,當(dāng)然,通過(guò)自定義AIDL方式相對(duì)更加靈活。這種方式需要自己在項(xiàng)目中自定義xxx.aidl文件,然后系統(tǒng)會(huì)自動(dòng)在gen目錄下生成相應(yīng)的接口類文件,接下來(lái)整個(gè)的流程與Messenger方式差別不大。

首先在服務(wù)端的src目錄下按照下圖中的樣子創(chuàng)建


目錄

在aidl.aidl下新建一個(gè)MyAIDLService.aidl文件,文件內(nèi)容如下:

// MyAIDLService.aidl
package aidl.aidl;

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

interface MyAIDLService {

    // 這個(gè)AIDL service 對(duì)外的接口只有一個(gè),就是獲取字符串
    String getString();
}

注意這里的package后面的名稱,與后面客戶端的名稱要一致,不然會(huì)出錯(cuò)。這個(gè)文件中我們只定義了一個(gè)對(duì)外的接口String getString()。這時(shí)候Build一下,編譯器會(huì)自動(dòng)在Build->generated->source->aidl->debug->aidl->aidl下生成對(duì)應(yīng)的接口文件

接下來(lái),在服務(wù)端創(chuàng)建一個(gè)MyService:

public class MyService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }

    class MyBinder extends MyAIDLService.Stub {

        @Override
        public String getString() throws RemoteException {
            return "我是從服務(wù)器返回的";
        }
    }
}

這里只需要注意,onBind(...)中返回一個(gè)MyAIDLService.Stub的實(shí)例。

然后到AndroidManifest.xml中注冊(cè)一下就可以了:

        <service android:name=".MyService">
            <intent-filter>
                <action android:name="com.server.aidl.service.MyService"/>
            </intent-filter>
        </service>

下面,來(lái)實(shí)現(xiàn)客戶端的調(diào)用:
首先,將服務(wù)端的MyAIDLService.aidl文件拷貝過(guò)來(lái),并要保持包名和服務(wù)端一致,如下圖所示:


目錄

接下來(lái),直接調(diào)用就可以了:

public class MainActivity extends AppCompatActivity {

    private Button bindServiceBtn, unBindServiceBtn;
    private TextView tvData;
    private MyAIDLService mMyAIDLService;

    private ServiceConnection sc = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            // 這里獲取Service的方式有點(diǎn)兒區(qū)別
            mMyAIDLService = MyAIDLService.Stub.asInterface(binder);
            try {
                String str = mMyAIDLService.getString();
                tvData.setText(str);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mMyAIDLService = null;
        }
    };

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

        bindServiceBtn = findViewById(R.id.bind_service);
        unBindServiceBtn = findViewById(R.id.unBind_service);
        tvData = findViewById(R.id.tv_from_server);

        bindServiceBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setAction("com.server.aidl.service.MyService");
                // 從Android 5.0開(kāi)始,隱式Intent綁定服務(wù)的方式已經(jīng)不能使用了,這里需要設(shè)置Service所在服務(wù)端的包名
                intent.setPackage("com.server.aidl.service.serviceaidlserver");
                bindService(intent, sc, BIND_AUTO_CREATE);
            }
        });

        unBindServiceBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(sc);
            }
        });
    }
}

這樣,AIDL的方式也介紹完了。

最后, Bound Service還有很重要的一點(diǎn)和Start Service不同,就是解綁的時(shí)候一定要確認(rèn)已經(jīng)綁定的才能解綁,否則會(huì)崩潰,而Start Service不需要。

另外,四大組件中BroadcastReceiver不能作為Bound Service的Client,因?yàn)樗纳芷诤芏?,而B(niǎo)ound Service的生命周期和Client的生命周期是綁定的。因此,在Android中,不允許BroadcastReceiver去bindService(...),當(dāng)有此類需求時(shí),可以用start service去代替。

需要補(bǔ)充的一點(diǎn)是:所有的Service都是運(yùn)行在其所在進(jìn)程的主線程上,如果Service和Client同屬一個(gè)進(jìn)程,則它就會(huì)運(yùn)行在主線程,也就是UI線程上,此時(shí)要注意不要在Service中執(zhí)行耗時(shí)任務(wù),如果要執(zhí)行耗時(shí)任務(wù),就要在Service中新建線程,避免阻塞UI導(dǎo)致ANR;Service一旦創(chuàng)建,需要停止時(shí)都需要顯示調(diào)用相應(yīng)的方法(started service調(diào)用stopService()或Service自身調(diào)用stopSelf(),bound service需要unBindService()),否則started service將一直處于運(yùn)行狀態(tài),對(duì)于bound service,則只有等到Client生命周期結(jié)束后才會(huì)銷(xiāo)毀。




補(bǔ)充:

介紹一下IntentService

IntentService是系統(tǒng)提供給我們的一個(gè)繼承自Service的特殊類,它的特殊性在于:

  1. 默認(rèn)實(shí)現(xiàn)了onBind()方法,直接返回null
  2. 定義了抽象方法onHandleIntent(), 用戶需要實(shí)現(xiàn)此方法,該方法用來(lái)處理耗時(shí)任務(wù),并已經(jīng)在新的線程中,用戶無(wú)需再新建線程。
  3. 當(dāng)耗時(shí)任務(wù)結(jié)束(onHandleIntent()方法執(zhí)行完畢),此IntentService自動(dòng)結(jié)束,無(wú)需人為調(diào)用方法使其結(jié)束
  4. IntentService處于任務(wù)時(shí),也是按照隊(duì)列的方式一個(gè)一個(gè)去處理,而非真正意義上的多線程并發(fā)方式.

簡(jiǎn)單定義:

public class MyIntentService extends IntentService {

    private static final String TAG = "MyIntentService";

    public MyIntentService() {
        super(TAG);
    }

    public MyIntentService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.w(TAG, "in onHandleIntent");
        Log.w(TAG, "thread name: " + Thread.currentThread().getName());
    }
}

注冊(cè):

<service android:name=".MyIntentService"/>

使用:

Intent intent = new Intent(BActivity.this, MyIntentService.class);
startService(intent);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容