android service

Service 是一個可以在后臺執行長時間運行操作而不使用用戶界面的應用組件。服務可由其他應用組件啟動,而且即使用戶切換到其他應用,服務仍將在后臺繼續運行。 此外,組件可以綁定到服務并與之進行交互,甚至是執行進程間通信 (IPC)。 例如,服務可以處理網絡事務、播放音樂,執行文件 I/O 或與內容提供程序交互,而所有這一切均可在后臺進行。

服務在其托管進程的主線程中運行,它既不創建自己的線程,也不在單獨的進程中運行(除非另行指定)。

應使用服務還是線程?

服務是一種即使用戶未與應用交互,但它仍可以在后臺運行的組件。 因此,應僅在必要時才創建服務。如果您確實要使用服務,則默認情況下,它仍會在應用的主線程中運行,因此,如果服務執行的是密集型或阻止性操作(例如 MP3 播放或聯網),則仍應在服務內創建新線程。

如需在主線程外部執行工作,不過只是在用戶正在與應用交互時才有此需要,則應創建新線程而非服務。 例如,如果只是想在 Activity 運行的同時播放一些音樂,則可在 onCreate() 中創建線程,在 onStart() 中啟動線程,然后在 onStop() 中停止線程。還可以考慮使用 AsyncTask 或 HandlerThread,而非傳統的 Thread 類。

Service的種類

  • 按運行類型分:
類別 區別 應用
前臺服務 會在通知欄顯示onGoing的 Notification 當服務被終止的時候,通知一欄的 Notification 也會消失,這樣對于用戶有一定的通知作用。常見的如音樂播放服務。
后臺服務 默認的服務即為后臺服務,即不會在通知一欄顯示 onGoing的 Notification。 當服務被終止的時候,用戶是看不到效果的。某些不需要運行或終止提示的服務,如天氣更新,日期同步,郵件同步等。
  • 按使用方式分:
類別 區別
startService啟動的服務 主要用于啟動一個服務執行后臺任務,不進行通信。停止服務使用stopService。
bindService啟動的服務 方法啟動的服務要進行通信。停止服務使用unbindService。
同時使用startService、bindService 啟動的服務 停止服務應同時使用stopService與unbindService。

使用service

  • 使用startService啟動服務
    1.定義一個類繼承service
public class MyService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
            //TODO do something useful
            return Service.START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
          //TODO for communication return IBinder implementation
      return null;
    }
}

onstartCommad方法返回一個int類型用來定義服務在被android系統終止之后的重啟方式。最常用的三種常量返回值



2.在Manifest.xml文件中配置該Service

<service
        android:name="MyService"
        android:icon="@drawable/icon"
        android:label="@string/service_name"
        >
</service>

3.使用Context的startService(Intent)方法啟動該Service

Intent intent = new Intent(this, MyService.class);
startService(intent);

4.不再使用時,調用stopService(Intent)方法停止該服務
.
使用這種方式創建啟動服務的生命周期如下圖:


注意:
1.如果服務已經開啟,不會重復的執行onCreate(),而是會調用onStartCommand()。
2.服務停止的時候調用 onDestory()。服務只會被停止一次。

  • 使用bindService方式啟動服務
    如果您的服務僅供本地應用使用,不需要跨進程工作,則可以實現自有 Binder 類,讓您的客戶端通過該類直接訪問服務中的公共方法。

注:此方法只有在客戶端和服務位于同一應用和進程內這一最常見的情況下方才有效。 例如,對于需要將 Activity 綁定到在后臺播放音樂的自有服務的音樂應用,此方法非常有效。

具體的創建服務的方法:

① 在服務中,創建一個可滿足下列任一要求的 Binder 實例:
 - 包含客戶端可調用的公共方法
 - 返回當前 Service實例,其中包含客戶端可調用的公共方法
 - 返回由服務承載的其他類的實例,其中包含客戶端可調用的公共方法

② 從 onBind()回調方法返回此 Binder實例。

③ 在客戶端中,從 [onServiceConnected()](https://developer.android.com/reference/android/content/ServiceConnection.html#onServiceConnected(android.content.ComponentName, android.os.IBinder))回調方法接收 Binder,并使用提供的方法調用綁定服務。

例如,以下這個服務可讓客戶端通過 Binder 實現訪問服務中的方法:

public class LocalService extends Service {
    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();

    //Binder中包含返回該實例的方法
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService 
            //so clients can call public methods
            return LocalService.this;
        }
    }
    // onBind()回調方法返回此 Binder實例。
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    /** method for clients */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

LocalBinder 為客戶端提供 getService() 方法,以檢索 LocalService 的當前實例。這樣,客戶端便可調用服務中的公共方法。 例如,客戶端可調用服務中的 getRandomNumber()。

點擊按鈕時,以下這個 Activity 會綁定到 LocalService 并調用 getRandomNumber():

public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;

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

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService,建議在onstart方法中進行綁定
        Intent intent = new Intent(this, LocalService.class);
        //利用bindService(Intent, ServiceConnection, int)方法啟動該Service
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service,建議在onstop方法中進行解綁
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    /** Called when a button is clicked (the button in the layout file attaches to
      * this method with the android:onClick attribute) */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call were something that might hang, 
            //then this request should occur in a separate thread 
            //to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder 
            //and get LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}

特點:bind的方式開啟服務,綁定服務,調用者掛了,服務也會跟著掛掉。綁定者可以調用服務里面的方法。

使用這種方式創建啟動服務的生命周期如下圖:


Service 元素的常見屬性

屬性 描述
android:name 服務類名
android:label 服務的名字,如果此項不設置,那么默認顯示的服務名則為類名
android:icon 服務的圖標
android:permission 申明此服務的權限,這意味著只有提供了該權限的應用才能控制或連接此服務
android:process 表示該服務是否運行在另外一個進程,如果設置了此項,那么將會在包名后面加上這段字符串表示另一進程的名字
android:enabled 如果此項設置為 true,那么 Service 將會默認被系統啟動,不設置默認此項為 false
android:exported 表示該服務是否能夠被其他應用程序所控制或連接,不設置默認此項為 false

IntentService

IntentService是繼承于Service并處理異步請求的一個類,在IntentService內有一個工作線程來處理耗時操作,啟動IntentService的方式和啟動傳統Service一樣,同時,當任務執行完后,IntentService會自動停止,而不需要我們去手動控制。
另外,可以啟動IntentService多次,而每一個耗時操作會以工作隊列的方式在IntentService的onHandleIntent回調方法中執行,并且,每次只會執行一個工作線程,執行完第一個再執行第二個,以此類推。
那么,用IntentService有什么好處呢?首先,我們省去了在Service中手動開線程的麻煩,第二,當操作完成時,我們不用手動停止Service

下面來寫一個Demo來模擬耗時操作在IntentService中的運行過程


自定義intentService

public class MyIntentService extends IntentService {

    public static final String REUSLT = "reuslt";

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        String msg = intent.getStringExtra(MainActivity.COM_KEVINWANGY_TESTINTENTSERVICE_MSG);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        msg = "get result \"" + msg + "_result\" at "
                + android.text.format.DateFormat.format("dd/MM/yy h:mm:ss aa", System.currentTimeMillis());
        Log.i("onHandleIntent", msg);

        Intent resultIntent = new Intent();
        resultIntent.setAction(MainActivity.ResultReceiver.RESULT_ACTION);
        resultIntent.putExtra(REUSLT, msg);
        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this); //Service本身就是Context
        localBroadcastManager.sendBroadcast(resultIntent);
    }
}

定義MainActiviy , 其中包含一個BroadCastReceiver

public class MainActivity extends AppCompatActivity {

    public static final String COM_KEVINWANGY_TESTINTENTSERVICE_MSG = "com.kevinwangy.testintentservice.msg";
    private Button mButton;
    private TextView mStart_text;
    private TextView mFinish_text;
    private EditText mEditText;
    private String mStart_msg;
    private ResultReceiver mResultReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final StringBuilder stringBuilder = new StringBuilder();

        mStart_text = (TextView)findViewById(R.id.start_text);
        mFinish_text = (TextView)findViewById(R.id.finish_text);

        mEditText = (EditText)findViewById(R.id.input_edit);

        mButton = (Button)findViewById(R.id.input_btn);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mStart_msg = mEditText.getText().toString();
                stringBuilder.append("send message \"" + mStart_msg + "\" at "
                        + android.text.format.DateFormat.format("dd/MM/yy h:mm:ss aa", System.currentTimeMillis()) + "\n");
                mStart_text.setText(stringBuilder.toString());

                Intent intent = new Intent(MainActivity.this, MyIntentService.class);
                intent.putExtra(COM_KEVINWANGY_TESTINTENTSERVICE_MSG, mStart_msg);
                startService(intent);
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    public class ResultReceiver extends BroadcastReceiver {
    }
}

ResultReceiver 的定義如下

    public class ResultReceiver extends BroadcastReceiver {
        public static final String RESULT_ACTION = "com.kevinwang.testintentservice.resultReceiver";

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null && TextUtils.equals(intent.getAction(), RESULT_ACTION)) {
                String result = intent.getStringExtra(MyIntentService.REUSLT);
                Log.i("onReceive", result);
                Log.i("onReceive", "context is " + context.toString());
                mFinish_text.setText(result);
            }
        }
    }

在onstart中注冊BroadCastReceiver

    protected void onStart() {
        super.onStart();
        IntentFilter filter = new IntentFilter(ResultReceiver.RESULT_ACTION);
        mResultReceiver = new ResultReceiver();
        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
        localBroadcastManager.registerReceiver(mResultReceiver, filter);
    }

在onstop中解除注冊

    protected void onStop() {
        super.onStop();
        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
        localBroadcastManager.unregisterReceiver(mResultReceiver);
    }

在androidManifest中聲明

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

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

關于service、intentService、thread和AsyncTask的對比

參考文檔及博客服務綁定服務Service那點事兒

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容