说明——
关于四大组件之一的Service,对它的基本用法不熟悉的可以去看看这个博客!
Service完全解析
除非我们特别为某个操作指定特定的线程,否则大部分在前台UI界面上的操作任务都执行在一个叫做UI Thread的特殊线程中。这可能存在某些隐患,因为部分在UI界面上的耗时操作可能会影响界面的响应性能。UI界面的性能问题会容易惹恼用户,甚至可能导致系统ANR错误。为了避免这样的问题,Android Framework提供了几个类,用来帮助你把那些耗时操作移动到后台线程中执行。那些类中最常用的就是IntentService.
IntentService为在单一后台线程中执行任务提供了一种直接的实现方式。它可以处理一个耗时的任务并确保不影响到UI的响应性。另外IntentService的执行还不受UI生命周期的影响,以此来确保AsyncTask能够顺利运行。
但是IntentService有下面几个局限性:
不可以直接和UI做交互。为了把他执行的结果体现在UI上,需要把结果返回给Activity。
工作任务队列是顺序执行的,如果一个任务正在IntentService中执行,此时你再发送一个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕才开始执行。
正在执行的任务无法打断。
虽然有上面那些限制,然而在在大多数情况下,IntentService都是执行简单后台任务操作的理想选择。
Step 1: 为你的app创建一个IntentService组件,需要自定义一个新的类,它继承自IntentService,并重写onHandleIntent()方法,如下所示:
public class RSSPullService extends IntentService {
@Override
protected void onHandleIntent(Intent workIntent) {
// 这里可以得到传进来的intent,就可以获得intent所携带的数据
String dataString = workIntent.getDataString();
...
// 对获得的数据进行操作,比如一些耗时的网络操作
...
}
}
注意:一个普通Service组件的其他回调,例如onCreate()、onStartCommand()会被IntentService自动调用。在IntentService中,要避免重写那些回调。
Step 2:在Manifest文件中定义IntentService
IntentService需要在manifest文件添加相应的条目如下所示:
<application
android:icon="@drawable/icon"
android:label="@string/app_name">
...
<activity android:name=".MainActivity">
activity>
<service
android:name=".RSSPullService"
android:exported="false">
service>
...
<application/>
注意
标签并没有包含任何intent filter。因为发送任务给IntentService的Activity需要使用显式Intent,所以不需要filter。这也意味着只有在同一个app或者其他使用同一个UserID的组件才能够访问到这个Service。
至此,你已经有了一个基本的IntentService类,你可以通过构造Intent对象向它发送操作请求。
为了创建一个任务请求并发送到IntentService。需要先创建一个显式Intent,并将请求数据添加到intent中,然后通过调用 startService() 方法把任务请求数据发送到IntentService。
下面是代码的示例:
/*
* 创建一个Intent来启动RSSPullService,把一个链接存放进intent中
*/
Intent mServiceIntent = new Intent(getActivity(), RSSPullService.class);
mServiceIntent.setData(Uri.parse(dataUrl));
getActivity.startService(mServiceIntent);
注意:
可以在Activity或者Fragment的任何位置发送任务请求。例如,如果你先获取用户输入,您可以从响应按钮单击或类似手势的回调方法里面发送任务请求。
一旦执行了startService(),IntentService在自己本身的onHandleIntent()方法里面开始执行这个任务,任务结束之后,不需要使用stopService()方法来停止这个服务,因为IntentService会自动停止这个Service。
下一步是如何把工作任务的执行结果返回给发送任务的Activity或者Fragment。
下面用一个实例来说明IntentService的一般工作步骤:
(1)在Activity中通过startService启动service,并传递参数。
(2)Service中接收参数,做耗时的处理,处理完毕,发送Broadcat,并把处理结果传递出来
(3)Activity中注册BroadcastReceiver,监听广播,更新UI。
Step 1:创建一个IntentService,接收参数
RSSPullService.java
public class RSSPullService extends IntentService {
//
public static final String BROADCAST_ACTION =
"com.example.android.threadsample.BROADCAST";
//构造方法必须重写
public RSSPullService() {
super("RSSPullService");
}
@Override
protected void onHandleIntent(Intent workIntent) {
//接收参数,做耗时的处理,处理完毕,发送Broadcat
//将数据打印出来
Log.i(TAG,dataString);
//接收到数据,做耗时处理
String result = downloadHtml(dataString);
Log.i("result",result);
}
...
}
Step 2:在MainActivity中通过startService启动IntentService,并传递参数
MainActivity.java
public class MainActivity extends AppCompatActivity {
MyReceiver receiver = new MyReceiver();
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
regist();
bindView();
}
private void bindView() {
mTextView = (TextView)this.findViewById(R.id.textView);
Button button = (Button)this.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
//创建一个显示Intent
Intent serviceIntent = new Intent(MainActivity.this,RSSPullService.class);
//将百度网址传入Intent
serviceIntent.setData(Uri.parse("http://www.baidu.com"));
//启动服务
startService(serviceIntent);
}
});
}
...
}
Step 3:在MainActivity中注册广播,这里我们利用LocalBroadcastManager来注册广播,监听广播,
MainActivity.java
private void regist() {
IntentFilter intentFilter = new IntentFilter(RSSPullService.BROADCAST_ACTION);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
LocalBroadcastManager.getInstance(this).registerReceiver(receiver, intentFilter);
}
//取消注册
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
}
Step 4:在service中处理耗时操作,并发送Broadcat,并把处理结果传递出来
RssPullService.java
public class RSSPullService extends IntentService {
private static final String TAG = "RSSPullService";
public static final String EXTENDED_DATA_STATUS =
"com.example.android.threadsample.STATUS";
private LocalBroadcastManager mLocalBroadcastManager;
...
protected void onHandleIntent(Intent intent) {
...
//将耗时操作的结果放进Intent,调用LocalBroadcastManager.sendBroadcast将intent传递回去
Intent localIntent = new Intent(BROADCAST_ACTION);
localIntent.putExtra(EXTENDED_DATA_STATUS,result);
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
mLocalBroadcastManager.sendBroadcast(localIntent);
}
...
//处理耗时任务的方法
private String downloadHtml(String dataString) {
try {
URL url = new URL(dataString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
InputStream in = conn.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len = 0;
while ((len = in.read(buff)) != -1) {
out.write(buff, 0, len);
}
in.close();
Log.i("html",out.toByteArray().toString());
return new String(out.toByteArray());
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
}
Step 5:在Activity中创建广播,接收广播,更新UI
MainActivity.java
private class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String data = intent.getStringExtra(RSSPullService.EXTENDED_DATA_STATUS);
Log.i("test", data);
mTextView.setText(data);
}
}
最后别忘了加上网络权限:
<uses-permission android:name="android.permission.INTERNET" />
执行结束,效果图如下:
我把主要的几个类的代码放上来:
MainActivity.java :
public class MainActivity extends AppCompatActivity {
MyReceiver receiver = new MyReceiver();
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
regist();
bindView();
}
private void regist() {
IntentFilter intentFilter = new IntentFilter(RSSPullService.BROADCAST_ACTION);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
LocalBroadcastManager.getInstance(this).registerReceiver(receiver, intentFilter);
}
private void bindView() {
mTextView = (TextView)this.findViewById(R.id.textView);
Button button = (Button)this.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent serviceIntent = new Intent(MainActivity.this,RSSPullService.class);
serviceIntent.setData(Uri.parse("http://www.baidu.com"));
startService(serviceIntent);
}
});
}
private class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String data = intent.getStringExtra(RSSPullService.EXTENDED_DATA_STATUS);
Log.i("test", data);
mTextView.setText(data);
}
}
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
}
}
RssPullService.java :
public class RSSPullService extends IntentService {
public static final String BROADCAST_ACTION =
"com.example.android.threadsample.BROADCAST";
private static final String TAG = "RSSPullService";
public static final String EXTENDED_DATA_STATUS =
"com.example.android.threadsample.STATUS";
private LocalBroadcastManager mLocalBroadcastManager;
private ResponseReceiver receiver;
public RSSPullService() {
super("RSSPullService");
}
@Override
protected void onHandleIntent(Intent intent) {
String dataString = intent.getDataString();
Log.i(TAG,dataString);
String result = downloadHtml(dataString);
Log.i("result",result);
Intent localIntent = new Intent(BROADCAST_ACTION);
localIntent.putExtra(EXTENDED_DATA_STATUS,result);
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
mLocalBroadcastManager.sendBroadcast(localIntent);
}
private String downloadHtml(String dataString) {
try {
URL url = new URL(dataString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
InputStream in = conn.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len = 0;
while ((len = in.read(buff)) != -1) {
out.write(buff, 0, len);
}
in.close();
Log.i("html",out.toByteArray().toString());
return new String(out.toByteArray());
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
}
AndroidManifest.xml:
...
".RSSPullService"
android:exported="false">
"com.example.android.threadsample.BROADCAST"/>
"android.permission.INTERNET" />