转载请注明出处:http://blog.csdn.net/krislight/article
Service可以看作一个后台服务,但并非是开启另外的线程,Service还是在主线程中运行.所以需避免耗时操作。
如果Service還未啟動,調用startService方法會call Service的onCreate()方法,如果已經啟動會call Service的
onStartCommand()方法,在onStartCommand()方法最後會返回一個int值代表Service重啟后的行為,該int值含義有
以下幾種:
Table 1. Restart options
Option |
Description |
Service.START_STICKY |
Service is restarted if it gets terminated. Intent data passed to the onStartCommand method is null. Used for services which manages their own state and do not depend on the Intent data. |
Service.START_NOT_STICKY |
Service is not restarted. Used for services which are periodically triggered anyway. The service is only restarted if the runtime has pending startService() calls since the service termination. |
Service.START_REDELIVER_INTENT |
Similar to Service.START_STICKY but the original Intent is re-delivered to the onStartCommand method. |
通過Intent.getFlags()
方法可以判斷
Service
是否重啟過
,
返回START_FLAG_REDELIVERY
和
START_FLAG_RETRY
代表
Service
已經重啟過了。
通過
stopService()
或
stopSelf()
來停止
Service
如果一個
Activity
想和
Service
之間交互通信
,
使用
bindService()
來啟動
Service
可以指定
android:process屬性將service運行到另一個單獨的進程,如果屬性前面有分號如
android:process =”:remote”代表該Service針對自身的應用程序私有,如果沒有分號如
android:process =”remote”代表該Service是共享進程,可以被其他應用程序使用。
很少指定Service運行在單獨運行在自己的進程,除非你希望將Service與其他應用程序共享。
IntentService使用队列的方式将请求的Intent加入队列,然后开启一个worker thread(线程)来处理队列中的Intent,
对于异步的startService请求,IntentService会处理完成一个之后再处理第二个,每一个请求都会在一个单独的
worker thread中处理,不会阻塞应用程序的主线程.
使用IntentService下載文件實例:
①在Acitivity中啟動Service
public class DownloadService extends IntentService { private int result = Activity.RESULT_CANCELED; public static final String URL = "urlpath"; public static final String FILENAME = "filename"; public static final String FILEPATH = "filepath"; public static final String RESULT = "result"; public static final String NOTIFICATION = "com.example.android.service.receiver"; public DownloadService() { super("DownloadService"); } // will be called asynchronously by Android @Override protected void onHandleIntent(Intent intent) { String urlPath = intent.getStringExtra(URL); String fileName = intent.getStringExtra(FILENAME); File output = new File(Environment.getExternalStorageDirectory(), fileName); if (output.exists()) { output.delete(); } InputStream stream = null; FileOutputStream fos = null; try { URL url = new URL(urlPath); stream = url.openConnection().getInputStream(); InputStreamReader reader = new InputStreamReader(stream); fos = new FileOutputStream(output.getPath()); int next = -1; while ((next = reader.read()) != -1) { fos.write(next); } // successfully finished result = Activity.RESULT_OK; } catch (Exception e) { e.printStackTrace(); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } publishResults(output.getAbsolutePath(), result); } private void publishResults(String outputPath, int result) { Intent intent = new Intent(NOTIFICATION); intent.putExtra(FILEPATH, outputPath); intent.putExtra(RESULT, result); sendBroadcast(intent); } }在XML配置文件中申請權限:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
註冊Service
<service android:name="com.example.android.service.DownloadService" ></service>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick" android:text="Download" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Status: " /> <TextView android:id="@+id/status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Not started" /> </LinearLayout> </LinearLayout>
主頁面:
public class MainActivity extends Activity { private TextView textView; private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Bundle bundle = intent.getExtras(); if (bundle != null) { String string = bundle.getString(DownloadService.FILEPATH); int resultCode = bundle.getInt(DownloadService.RESULT); if (resultCode == RESULT_OK) { Toast.makeText(MainActivity.this, "Download complete. Download URI: " + string, Toast.LENGTH_LONG).show(); textView.setText("Download done"); } else { Toast.makeText(MainActivity.this, "Download failed", Toast.LENGTH_LONG).show(); textView.setText("Download failed"); } } } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.status); } @Override protected void onResume() { super.onResume(); registerReceiver(receiver, new IntentFilter(DownloadService.NOTIFICATION)); } @Override protected void onPause() { super.onPause(); unregisterReceiver(receiver); } public void onClick(View view) { Intent intent = new Intent(this, DownloadService.class); // add infos for the service which file to download and where to store intent.putExtra(DownloadService.FILENAME, "index.html"); intent.putExtra(DownloadService.URL, "http://www.baidu.com/index.html"); startService(intent); textView.setText("Service started"); } }
這裡Activity與Service交互是通過Activity註冊一個BroadCastReceiver來監聽廣播,啟動Service后,一旦Service完成任務發送廣播,BroadCastReceiver監聽到廣播進行相應的處理。
再介紹一個例子讓Activity綁定到運作中的Service,這個Service在開機后自動運行,并模擬抓取數據
先建一個Service:
public class LocalWordService extends Service { private final IBinder mBinder = new MyBinder(); private ArrayList<String> list = new ArrayList<String>(); @Override public int onStartCommand(Intent intent, int flags, int startId) { Random random = new Random(); if (random.nextBoolean()) { list.add("Linux"); } if (random.nextBoolean()) { list.add("Android"); } if (random.nextBoolean()) { list.add("iPhone"); } if (random.nextBoolean()) { list.add("Windows7"); } if (list.size() >= 20) { list.remove(0); } return Service.START_NOT_STICKY; } @Override public IBinder onBind(Intent arg0) { return mBinder; } public class MyBinder extends Binder { LocalWordService getService() { return LocalWordService.this; } } public List<String> getWordList() { return list; } }建立一個監聽開機啟動的廣播接收器,該接收器每隔30秒發送啟動Service的廣播:
public class MyScheduleReceiver extends BroadcastReceiver { // restart service every 30 seconds private static final long REPEAT_TIME = 1000 * 30; @Override public void onReceive(Context context, Intent intent) { AlarmManager service = (AlarmManager) context .getSystemService(Context.ALARM_SERVICE); Intent i = new Intent(context, MyStartServiceReceiver.class); PendingIntent pending = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT); Calendar cal = Calendar.getInstance(); // start 30 seconds after boot completed cal.add(Calendar.SECOND, 30); // fetch every 30 seconds // InexactRepeating allows Android to optimize the energy consumption service.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), REPEAT_TIME, pending); // service.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), // REPEAT_TIME, pending); } }
啟動Service的接收器:
public class MyStartServiceReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Intent service = new Intent(context, LocalWordService.class); context.startService(service); } }
佈局文件mainLayout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick" android:text="Update" > </Button> <ListView android:id="@id/android:list" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView> </LinearLayout>
主頁面:
public class MainActivity extends ListActivity { private LocalWordService s; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); wordList = new ArrayList<String>(); adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, wordList); setListAdapter(adapter); } @Override protected void onResume() { super.onResume(); Intent intent= new Intent(this, LocalWordService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onPause() { super.onPause(); unbindService(mConnection); } private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder binder) { LocalWordService.MyBinder b = (LocalWordService.MyBinder) binder; s = b.getService(); Toast.makeText(MainActivity.this, "Connected", Toast.LENGTH_SHORT) .show(); } public void onServiceDisconnected(ComponentName className) { s = null; } }; private ArrayAdapter<String> adapter; private List<String> wordList; public void onClick(View view) { if (s != null) { Toast.makeText(this, "Number of elements" + s.getWordList().size(), Toast.LENGTH_SHORT).show(); wordList.clear(); wordList.addAll(s.getWordList()); adapter.notifyDataSetChanged(); } } }