天天看點

Android中Service的意義及用法

1、下面介紹Service和AsyncTask的用法和比較。

Service沒有界面,用于執行一個需要在背景長期運作的任務。AsyncTask用于執行短時間的異步任務,并跟UI線程互動,用于替代Thread和Handler。

1) MainActivity.java

public class MainActivity extends Activity {
	private static final String TAG = MainActivity.class.getSimpleName();
	/**便于多次、多地調用,生命周期伴随Activity*/
	Intent intent;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		Log.i(TAG, "onCreate");
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
//		mAsyncTask.execute("http://www.qinuli.com:83/picture/thumb.jpg", "http://www.qinuli.com:83/picture/hand_in_hand.jpg");
		intent = new Intent(this, MyService.class);
	}
	protected void onDestroy() {
		Log.d(TAG, "onDestroy");
		super.onDestroy();
	};
	/**
	 * 定義一個AsyncTask的子類;執行個體化并在UI線程調用其execute方法
	 * <li>3個參數:Params、Progress、Result
	 * <li>4 steps:onPreExecute->doInBackground->onProgressUpdate->onPostExecute
	 * <li>功能:用于背景執行前台顯示的任務
	 * <li>意義:代替或封裝Thread和Handler,友善背景線程和UI線程互動
	 */
	AsyncTask<String, Integer, CharSequence> mAsyncTask = new AsyncTask<String, Integer, CharSequence>() {
		@Override
		protected void onPreExecute() {
			Log.d(TAG, "onPreExecute");
		};
		@Override
		protected CharSequence doInBackground(String... params) {
			//perform a computation on a background thread
			for(int i=0;i<params.length;i++){
				publishProgress(Integer.valueOf(i));
			}
			return "加油!";
		}
		@Override
		protected void onProgressUpdate(Integer... values) {
			Log.d(TAG, "onProgressUpdate-"+values[0]);
		};
		@Override
		protected void onPostExecute(CharSequence result) {
			Log.d(TAG, "onPostExecute-"+result);
		};
	};
	private ServiceConnection mServiceConnection = new ServiceConnection() {
		@Override
		public void onServiceDisconnected(ComponentName name) {
			Log.d(TAG, "onServiceDisconnected-"+name.getClassName());
		}
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			//由bindService觸發,在onBind之後
			MyService myService = ((MyBinder)service).getService();
			Log.d(TAG, "onServiceConnected-"+name.getClassName()+"-"+myService.name);
		}
	};
	public void onClick(View v){
		switch (v.getId()) {
		case R.id.btn_startService:
			//調用Service的空參構造
			startService(intent);
			break;
		case R.id.btn_stopService:
			stopService(intent);
			break;
		case R.id.btn_bindService:
			bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
			break;
		case R.id.btn_unbindService:
			unbindService(mServiceConnection);
		}
	}
}
           

2) activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <Button 
        android:id="@+id/btn_startService"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="startService"/>
    <Button 
        android:id="@+id/btn_stopService"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="stopService"/>
    <Button 
        android:id="@+id/btn_bindService"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="bindService"/>
    <Button 
        android:id="@+id/btn_unbindService"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="unbindService"/>
</LinearLayout>
           

3) MyService.java,講解startService和bindService的原理

/**
 * <li>Service的意義:perform a longer running operation
 * <li>Service和AsyncTask的差別:Service運作在主線程,不會結束
 */
public class MyService extends Service {
	private static final String TAG = MyService.class.getSimpleName();
	private MyBinder myBinder = new MyBinder();
	public String name = "ShiXin";
	
	public MyService(){
		super();
		Log.d(TAG, "MyService");
	}
	
	/**繼承Binder類,Binder類實作IBinder接口。執行個體化該類供onBind使用*/
	public class MyBinder extends Binder{
		/**傳回Service的執行個體,進而調用其方法和屬性,實作一些資料通信和功能操作*/
		public MyService getService(){
			return MyService.this;
		}
	}
	
	@Override
	public IBinder onBind(Intent intent) {
		Log.d(TAG, "onBind");
		//由bindService觸發
		return myBinder;
	}

	@Override
	public boolean onUnbind(Intent intent) {
		Log.d(TAG, "onUnbind");
		return super.onUnbind(intent);
	}
	
	@Override
	public void onCreate() {
		Log.d(TAG, "onCreate");
		//重新執行個體化新的Service
		super.onCreate();
	}
	
	public void onDestroy() {
		Log.d(TAG, "onDestroy");
		super.onDestroy();
	};
	
	@Override
	public void onStart(Intent intent, int startId) {
		//deprecated 被onStartCommand代替
		super.onStart(intent, startId);
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		//每次startService都會觸發,bindService不會觸發
		//startId:unique integer token,标記每次startService,Service建立後從1開始計數
		//START_STICKY_COMPATIBILITY 0;Service.START_STICKY 1
		stopSelfResult(3);//onStartCommand執行3次
		int state = super.onStartCommand(intent, flags, startId);
		Log.d(TAG, "onStartCommand-"+intent.getComponent().getClassName()+"-"+flags+"-"+startId+"-"+state);
		return state;//1
	}
}
           

4) MyIntentService.java,IntentService用于在背景線程執行異步任務,執行完Service自動destroy

/**
 * handle asynchronous requests
 */
public class MyIntentService extends IntentService {

	private static final String TAG = MyIntentService.class.getSimpleName();
	
	public MyIntentService() {
		//name worker thread
		super("worker thread");
	}
	@Override
	protected void onHandleIntent(Intent intent) {
		//代替onStartCommand;執行完調用stopSelf()
		Log.d(TAG, Thread.currentThread().getName()+"-onHandleIntent");
	}
	@Override
	public void onCreate() {
		Log.d(TAG, "onCreate");
		super.onCreate();
	}
	@Override
	public void onDestroy() {
		Log.d(TAG, "onDestroy");
		super.onDestroy();
	}
}
           

5) AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.qinuli.testproject"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity android:name="com.qinuli.testproject.MainActivity">
            <intent-filter >
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <service android:name="com.qinuli.testproject.MyService"></service>
        <service android:name="com.qinuli.testproject.MyIntentService"></service>
    </application>

</manifest>
           

2、如何讓服務永久運作

本示例介紹了TIME_TICK和BOOT_COMPLETED廣播的用法

public class MainActivity extends Activity {
	private static final String TAG = MainActivity.class.getSimpleName();
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		Log.d(TAG, "onCreate");
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
	@Override
	protected void onDestroy() {
		Log.d(TAG, "onDestroy");
		super.onDestroy();
	}
	public void onClick(View v){
		switch(v.getId()){
		case R.id.btn_killProcess:
			Process.killProcess(Process.myPid());
		}
	}
}
           

MyService.java

public class MyService extends Service {

	private static final String TAG = MyService.class.getSimpleName();
	private MyBinder myBinder = new MyBinder();
	public class MyBinder extends Binder{
		public MyService getService(){
			return MyService.this;
		}
	}
	@Override
	public IBinder onBind(Intent intent) {
		return myBinder;
	}
	@Override
	public void onCreate() {
		Log.d(TAG, "onCreate");
		super.onCreate();
	}
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.d(TAG, "onStartCommand");
		//程序被殺死,服務會重建
		return Service.START_STICKY;
	}
	@Override
	public void onDestroy() {
		Log.d(TAG, "onDestroy");
		super.onDestroy();
	}
}
           

MyApplication.java

public class MyApplication extends Application {
	private static final String TAG = MyApplication.class.getSimpleName();
	MyService myService;
	Intent intent;
	@Override
	public void onCreate() {
		Log.d(TAG, "onCreate");
		super.onCreate();
		
		intent = new Intent(this, MyService.class);
		startService(intent);
		bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
		
		registerReceiver(new TimeTickReceiver(), new IntentFilter(Intent.ACTION_TIME_TICK));
	}
	@Override
	public void onTerminate() {
		Log.d(TAG, "onTerminate");
		super.onTerminate();
		stopService(intent);
		//禁用一個manifest receiver,可禁用清單檔案裡注冊的四大元件
		ComponentName componentName = new ComponentName(this, BootCompletedReceiver.class);
		PackageManager pm = getPackageManager();
		pm.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 
				PackageManager.DONT_KILL_APP);
	}
	private ServiceConnection mServiceConnection = new ServiceConnection() {
		@Override
		public void onServiceDisconnected(ComponentName name) {
			myService = null;
		}
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			Log.d(TAG, "onServiceConnected");
			myService = ((MyBinder)service).getService();
		}
	};
}
           

BootCompleteReceiver.java

public class BootCompletedReceiver extends BroadcastReceiver {
	private static final String TAG = BootCompletedReceiver.class.getSimpleName();

	public BootCompletedReceiver(){
		super();
		Log.d(TAG, "BootCompletedReceiver");
	}
	
	public void onReceive(Context context, Intent intent) {
		String action = intent.getAction();
		Log.d(TAG, action);
		if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
			//清單檔案注冊,開機可收到廣播
			boolean isServiceRunning = false;
			ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
			for(RunningServiceInfo runningServiceInfo:activityManager.getRunningServices(Integer.MAX_VALUE)){
				if(MyService.class.getName().equals(runningServiceInfo.service.getClassName())){
					isServiceRunning = true;
				}
			}
			if(!isServiceRunning){
				ComponentName comp = new ComponentName(context.getPackageName(), MyService.class.getName());
				context.startService(new Intent().setComponent(comp));
			}
		}else if(ConnectivityManager.CONNECTIVITY_ACTION.equals(action)){
			//在清單檔案注冊,退出APP或殺死程序依然能收到廣播,但清掉記憶體就收不到了
			ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
			NetworkInfo networkInfo = cm.getActiveNetworkInfo();
			if(networkInfo!=null)
				Log.d(TAG, "networkstate:"+networkInfo.isConnectedOrConnecting()+"-"+(networkInfo.getType()==ConnectivityManager.TYPE_MOBILE));
		}
	}
}
           

TimeTickReceiver.java

public class TimeTickReceiver extends BroadcastReceiver {
	private static final String TAG = TimeTickReceiver.class.getSimpleName();

	public TimeTickReceiver(){
		super();
		Log.d(TAG, "TimeTickReceiver");
	}
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String action = intent.getAction();
		Log.d(TAG, action);
		if (Intent.ACTION_TIME_TICK.equals(action)) {
			boolean isServiceRunning = false;
			//拿到所有正在運作的服務
			ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
			for(RunningServiceInfo runningServiceInfo:activityManager.getRunningServices(Integer.MAX_VALUE)){
				if(MyService.class.getName().equals(runningServiceInfo.service.getClassName())){
					isServiceRunning = true;
				}
			}
			Log.d(TAG, "isServiceRunning="+isServiceRunning);
			if(!isServiceRunning){
				ComponentName comp = new ComponentName(context.getPackageName(), MyService.class.getName());
				context.startService(new Intent().setComponent(comp));
			}
		}
	}
}
           

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.qinuli.servicetest"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    
    <application
        android:name="com.qinuli.servicetest.MyApplication"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity android:name="com.qinuli.servicetest.MainActivity">
            <intent-filter >
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <service android:name="com.qinuli.servicetest.MyService"
            android:enabled="true"
            android:exported="false"/>
        <receiver
            android:name="com.qinuli.servicetest.BootCompletedReceiver"
            android:permission="android.permission.RECEIVE_BOOT_COMPLETED"
            android:enabled="true" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
            </intent-filter>
        </receiver>
    </application>

</manifest>