《老罗Android第二季》使用Services(1)及进程优先级

一、 Service简介

Service是android 系统中的四大组件之一(Activity、Service、BroadcastReceiver、ContentProvider),它跟Activity的级别差不多,但不能自己运行只能后台运行,并且可以和其他组件进行交互。service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务总是藏在后台的。

1. Services 生命周期
 Services生命周期根据产生方法不 一样,有两种生命周期:

有了 Service 类我们如何启动他呢,有两种方法:
      • Context.startService() 
      • Context.bindService()

 1.  在同一个应用任何地方调用 startService() 方法就能启动 Service 了,然后系统会回调 Service 类的 onCreate() 以及 onStart() 方法。启动的 Service 会一直运行在后台,直到 Context.stopService() 或者 selfStop() 方法被调用。另外如果一个 Service 已经被启动,其他代码再试图调用 startService() 方法,是不会执行 onCreate() 的,但会重新执行一次 onStart() 。

 2. 另外一种 bindService() 方法的意思是,把这个 Service 和调用 Service 的客户类绑起来,如果调用这个客户类被销毁,Service 也会被销毁。用这个方法的一个好处是,bindService() 方法执行后 Service 会回调上边提到的 onBind() 方发,你可以从这里返回一个实现了 IBind 接口的类,在客户端操作这个类就能和这个服务通信了,比如得到 Service 运行的状态或其他操作。如果 Service 还没有运行,使用这个方法启动 Service 就会 onCreate() 方法而不会调用 onStart()。

public class MyService extends Service {
	private final String TAG = "MyService";
	public void onCreate() {
		super.onCreate();
		Log.i(TAG, "--->>onCreate");
	}
	//onStartCommand方法中链接网络获取数据,关闭service服务
	public int onStartCommand(Intent intent, int flags, int startId) {
	Log.i(TAG, "--->>onStartCommand-->>"+intent.getStringExtra("name"));
	return super.onStartCommand(intent, flags, startId);
	}
	public IBinder onBind(Intent arg0) {
		return null;
	}
    //资源的释放
	public void onDestroy() {
		Log.i(TAG, "--->>onDestroy");
		super.onDestroy();
	}
}
MainActivity.java:
start.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
	Intent intent = new Intent(MainActivity.this, MyService.class);
	intent.putExtra("name", "jack");
	startService(intent);
	}
});
stop.setOnClickListener(new View.OnClickListener() {
	public void onClick(View v) {
	Intent intent = new Intent(MainActivity.this, MyService.class);
	stopService(intent);
	}
});
点出开启服务,执行了:onCreate(),onStartCommand(),再点击时 就只执行onStartCommand(),不执行onCreate()。
点出开启服务,执行了:onDestroy(). 若执行了onDestroy(),再点击时就不再执行了。
    startService() 方法可以多次执行,若已生成了service,就不再执行onCreate,只执行onStartCommand。不管执行了多少次startService,只需要执行一次onDestroy,就把服务关闭了。若service没启动,则onDestroy方法不会被执行。
在Manifast中要添加service的 注册:<service android:name=".DownLoadService" > </service>放于 </application>之上一行。
二、 拓展知识

与 Service 通信并且让它持续运行

如果我们想保持和 Service 的通信,又不想让 Service 随着 Activity 退出而退出呢?你可以先 startService() 然后再 bindService() 。当你不需要绑定的时候就执行 unbindService() 方法,执行这个方法只会触发 Service 的 onUnbind() 而不会把这个 Service 销毁。这样就可以既保持和 Service 的通信,也不会随着 Activity 销毁而销毁了。

提高 Service 优先级

Android 系统对于内存管理有自己的一套方法,为了保障系统有序稳定的运信,系统内部会自动分配,控制程序的内存使用。当系统的内存不足时, android系统将根据进程优先级选择杀死一些不太重要的进程.这样就能保证真正对用户有用的程序仍然再运行。如果你的 Service 碰上了这种情况,多半会先被杀掉。但如果你增加 Service 的优先级就能让他多留一会,我们可以用 setForeground(true) 来设置 Service 的优先级。

      为什么是 foreground ? 默认启动的 Service 是被标记为 background,当前运行的 Activity 一般被标记为 foreground,也就是说你给 Service 设置了 foreground 那么他就和正在运行的 Activity 类似优先级得到了一定的提高。当让这并不能保证你得 Service 永远不被杀掉,只是提高了他的优先级。

  Android中进程的优先级分为5个级别,从高到低分别为:
1.前台进程 Foreground process
2.可视进程 Visible process
3.服务进程 Service process
4.后台进程 Background process
5.空进程 Empty process:
 ( 1) 一个 前台进程 显示的是用户此时需要处理和显示的。下列的条件有任何一个成立,这个进程都被认为是在前台运行的。
        a进程中包含处于前台的正与用户交互的activity;
        b进程中包含与前台activity绑定的service;
        c进程中包含正在执行生命周期的回调函数的service(如onCreate()、onStar()、onDestroy())

        d 它有一个正在运行onReceive()方法的广播接收对象。

进程中包含调用了startForeground()方法的service;

系统中前台进程的数量很少, 前台进程几乎不会被杀死. 只有当内存低到无法保证所有的前台进程同时运行时才会选择杀死某个前台进程.

(2) 可视进程
        a. 进程中包含未处于前台但仍然可见的activity(调用了activity的onPause()方法, 但没有调用onStop()方法). 典型的情况是运行activity时弹出对话框, 此时的activity虽然不是前台activity, 但其仍然可见.

b. 进程中包含与可见activity绑定的service.

可视进程不会被系统杀死, 除非为了保证前台进程的运行而不得已为之.

(3) 一个 服务进程是一个通过调用startService()方法启动的服务,并且不属于前两种情况。尽管服务进程没有直接被用户看到,但他们确实是用户所关心的,比如后台播放音乐或网络下载数据。所以系统保证他们的运行,直到不能保证所有的前台可见程序都正常运行时才会终止他们。
(4) 一个 后台进程就是一个非当前正在运行的activity(activity的onStop()方法已经被调用),他们不会对用户体验造成直接的影响,当没有足够内存来运行前台可见程序时, 系统随时都有可能杀死一个后台进程。通常,后台进程会有很多个在运行,所以他们维护一个LRU最近使用程序列表来保证经常运行的activity能最后一个被终止。如果一个activity正确的实现了生命周期的方法,并且保存它当前状态,杀死这些进程后, 可以在用户重新启动它时恢复之前的运行状态.
(5) 一个 空线程没有运行任何可用应用程序组,保留他们的唯一原因是为了设立一个缓存机制,来加快组件启动的时间。系统经常杀死这些内存来平衡系统的整个系统的资源,进程缓存和基本核心缓存之间的资源。
   Android把进程里优先级最高的activity或服务,作为这个进程的优先级。例如,一个进程拥有一个服务和一个可见的activity,那么这个进程将会被定义为可见进程,而不是服务进程。
  如果当进程B依赖A时,如果A的优先级之前比B低,那么A的优先级就会被提升。一个为其他进程B服务的进程A的优先级,一定不低于B的优先级。
  由于Service 进程的优先级比Background进程的优先级要高,所以,一般广播接受者,会将任务通过启动服务来执行。
   Andoid UI toolkit(来自android.widget和android.view的组件)不是线程安全的。因此一般情况下只在main线程中操作UI,同时为了使界面响应快速,要将费时的操作放到工作线程中。这是android设计的2条规则。

android的消息机制之个人感悟,服务,activity等都是在主线程中运行的,startService()这些类似的函数只是往当前进程的消息队列中插入消息,然后有handler继续分发和处理消息,进而会调用那些回调函数,如onStartCommand().


2. Service服务之下载网络图片
在MainActivity.java启动服务:
button.setOnClickListener(new View.OnClickListener() {
	public void onClick(View arg0) {
	Intent intent = new Intent(MainActivity.this,DownLoadService.class);
	startService(intent);
	}
});
在DownLoadService.java执行下载服务:
public class DownLoadService extends Service {
private final String IMAGE_PATH = "http://www.baidu.com/img/bdlogo.gif";
public void onCreate() {
	super.onCreate();
}
public int onStartCommand(Intent intent, int flags, int startId) {
	// 不能直接使用http协议访问网络
	final Handler handler = new Handler() {
	public void handleMessage(Message msg) {
		super.handleMessage(msg);
		if (msg.what == 1) {
		stopSelf();// 关闭service服务
		Toast.makeText(getApplicationContext(), "文件下载完毕!!", 1).show();
			}
		}
	};
	new Thread(new Runnable() {
	public void run() {
		HttpClient httpClient = new DefaultHttpClient();
		HttpPost httpPost = new HttpPost(IMAGE_PATH);
		HttpResponse response = null;
		// 获得sdcard卡的目录
		File file = Environment.getExternalStorageDirectory();
		FileOutputStream outputStream = null;
		try {
			response = httpClient.execute(httpPost);
			if (response.getStatusLine().getStatusCode() == 200) {
			// 获得了图片的内容
			byte[] result = EntityUtils.toByteArray(response
					.getEntity());
			// 判断sdcard是否挂载,并且可以存储数据
			if (Environment.getExternalStorageState().equals(
					Environment.MEDIA_MOUNTED)) {
				File new_file = new File(file, "bdlogo.gif");
				outputStream = new FileOutputStream(new_file);
				outputStream.write(result, 0, result.length);
				Message message = Message.obtain();
				message.what = 1;
				handler.sendMessage(message);
				// 关闭service
			}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (httpClient != null) {
				httpClient.getConnectionManager().shutdown();
			}
		}
	}
	}).start();
	return super.onStartCommand(intent, flags, startId);
}
public IBinder onBind(Intent arg0) {
	return null;
}
public void onDestroy() {
	super.onDestroy();
}
}

3. Service服务之IntentService
  在Android开发中,我们或许会碰到这么一种业务需求,一项任务分成几个子任务,子任务按顺序先后执行,子任务全部执行完后,这项任务才算成功。那么,利用几个子线程顺序执行是可以达到这个目的的,但是每个线程必须去手动控制,而且得在一个子线程执行完后,再开启另一个子线程。或者,全部放到一个线程中让其顺序执行。这样都可以做到,但是,如果这是一个后台任务,就得放到Service里面,由于Service和Activity是同级的,所以,要执行耗时任务,就得在Service里面开子线程来执行。那么,有没有一种简单的方法来处理这个过程呢,答案就是IntentService。
  简单说,IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。
    所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。

那么,用IntentService有什么好处呢?首先,我们省去了在Service中手动开线程的麻烦,第二,当操作完成时,我们不用手动停止Service,第三,it's so easy to use!

/*** 1、不需要开启线程 2、不需要关闭服务,它自己关闭 3、单线程下载数据 */
public class DownLoadService extends IntentService {
private final String IMAGE_PATH = "http://www.baidu.com/img/bdlogo.gif";
public DownLoadService() {
	super("DownLoadService");
}
public void onCreate() {
	super.onCreate();
}
protected void onHandleIntent(Intent intent) {
	HttpClient httpClient = new DefaultHttpClient();
	HttpPost post = new HttpPost(IMAGE_PATH);
	HttpResponse response = null;
	File file = Environment.getExternalStorageDirectory();
	FileOutputStream outputStream = null;
	try {
	response = httpClient.execute(post);
	if (response.getStatusLine().getStatusCode() == 200) {
	byte[] result = EntityUtils.toByteArray(response.getEntity());
	if (Environment.getExternalStorageState().equals(
		Environment.MEDIA_MOUNTED)) {
		File new_file = new File(file, "abc.gif");
		outputStream = new FileOutputStream(new_file);
		outputStream.write(result, 0, result.length);
		Toast.makeText(getApplicationContext(), "下载文件完毕!!",1).show();
			}
		}
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		try {
		if (outputStream != null) {
		outputStream.close();
			}
		} catch (Exception e2) {
			e2.printStackTrace();
		}
		httpClient.getConnectionManager().shutdown();
	}
}
}
在manifest中要加入两处权限:
 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  IntentService 实际上是Looper,Handler,Service 的集合体,他不仅有服务的功能,还有处理和循环消息的功能.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值