android开发中,作为4大组件的service在开发中经常会使用到。activity用于在前台展示,service用于在后台下载,很多时候,我们的activity和service之间需要进行相应的交互,activity需要调用service里面的方法实现某些功能,service需要调用activity的方法,实现界面更新等的交互。
一般在Activity中启动后台Service,通过Intent来启动,Intent中我们可以传递数据给Service,而当我们Service执行某些操作之后想要更新UI线程,我们应该怎么做呢?我们通过一个下载的小例子来理解它们通信的方式
效果
【通过Binder对象】
1.Activity通过Intent向服务发送消息,通过调用bindService(Intent service, ServiceConnection conn,int flags)并绑定,此时我们可以得到一个Service的一个对象实例,我们就可以调用其公开的方法。通过IBinder拿到Service的引用调用其公开的方法。
2.核心总结下来就是service中有个类部类继承Binder,然后提供一个公有方法,返回当前service的实例。 activity通过bindService来开启一个service,通过onServiceConnected方法,获取IBinder实例,然后再通过IBinder实例来获取service实例,这样,我们得到了service的实例,那么我们的activity就可以随心所欲的使用它里面的各种方法来操作它了。如果要主动通知Activity,我们可以利用回调方法。
3.放出代码~
布局就不放了,就是一个小demo,有一个button,一个progressbar,一个textview
首先要写一个service的类 ,我们命名为MyService
public class MyService extends Service {
//进度条最大值
public static final int MAX_PROGRESS = 100;
// 进度条的当前进度值
public int currentProgress=0;
/**
* 更新进度的回调接口
*/
private OnProgressListener onProgressListener;
//开启下载任务
public void startDownloadTask(final String url){
new Thread(new Runnable() {
@Override
public void run() {
try {
HttpURLConnection connection ;
InputStream input;
OutputStream output;
URL downloadUrl = new URL(url);
connection = (HttpURLConnection) downloadUrl.openConnection();
connection.connect();
input=connection.getInputStream();
output=new FileOutputStream("/sdcard/new.apk");
int fileLength = connection.getContentLength();
byte data[] = new byte[2048];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
currentProgress=(int) (total * 100 / fileLength);//进度发生变化通知调用方
if(onProgressListener != null){
onProgressListener.onProgress(currentProgress);
}
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
//提供一个对外的获得的progress的值
public int getProgress(){
return currentProgress;
}
/**
* 注册回调接口的方法,供外部调用
*/
public void setOnProgressListener(OnProgressListener onProgressListener) {
this.onProgressListener = onProgressListener;
}
public MyService() {
}
/**
* 返回一个Binder对象
*/
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
//1.service中有个类部类继承Binder,然后提供一个公有方法,返回当前service的实例。
public class MyBinder extends Binder{
public MyService getService(){
return MyService.this;
}
}
}
对于这个类提供的几点说明:
1.新建一个内部类MyBinder继承Binder,提供一个公有的方法返回当前Service的实例
2.在onbind()方法中返回我们1中的Binder对象,activity也是通过绑定服务bindservice,并在onServiceConnected中获取IBinder实例,然后再通过IBinder实例来获取service实例。
3.剩下的方法我们直接在activity中调用即可
OnProgressListener接口代码,通过这个接口回调当前progress值
public interface OnProgressListener {
void onProgress(int progress);
}
Activity代码
public class MainActivity extends AppCompatActivity {
private MyService myService;
private ProgressBar progressBar;
private Button button;
private TextView textview;
private String url="xxx";
private ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//返回一个MyService对象
myService=((MyService.MyBinder)service).getService();
myService.setOnProgressListener(new OnProgressListener() {
@Override
public void onProgress(int progress) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//主线程更新UI
progressBar.setProgress(progress);
textview.setText(progress+"%");
}
});
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button= (Button) findViewById(R.id.button);
progressBar= (ProgressBar) findViewById(R.id.progressBar);
textview= (TextView) findViewById(R.id.textView);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
myService.startDownloadTask(url);
}
});
Intent intent = new Intent(this,MyService.class);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}
对于这个类提供的几点说明:
1.在oncreate中通过bindservice绑定服务,在 onServiceConnected中获取service实例,并通过回调返回的progress更新UI。
【通过广播】
当我们的进度发生变化的时候我们发送一条广播,然后在Activity的注册广播接收器,接收到广播之后更新ProgressBar。
放代码:
不同于上一个service,我们新建一个service通过广播的方式向activity传递
public class MyService2 extends Service {
public MyService2() {
}
//进度条最大值
public static final int MAX_PROGRESS = 100;
// 进度条的当前进度值
public int currentProgress=0;
private Intent intent = new Intent("com.test.service.RECEIVER");
//开启下载任务
public void startDownloadTask(final String url){
new Thread(new Runnable() {
@Override
public void run() {
try {
HttpURLConnection connection ;
InputStream input;
OutputStream output;
URL downloadUrl = new URL(url);
connection = (HttpURLConnection) downloadUrl.openConnection();
connection.connect();
input=connection.getInputStream();
output=new FileOutputStream("/sdcard/new.apk");
int fileLength = connection.getContentLength();
byte data[] = new byte[2048];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
currentProgress=(int) (total * 100 / fileLength);//进度发生变化通知调用方
//发送Action为com.test.service.RECEIVER的广播
intent.putExtra("progress", currentProgress);
sendBroadcast(intent);
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//通过startservice启动服务,调用的是service的onStartCommand方法
//此时service通过发送广播实现,activity注册广播接收器接受进度
startDownloadTask((String) intent.getSerializableExtra("url"));
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
//通过bindservice绑定服务,调用的是onbind方法,此时是通过IBinder传递参数
//这个service主要演示的通过广播传递参数 这个方法直接返回空即可
return null;
}
}
Activity代码
public class Main2Activity extends AppCompatActivity {
private ProgressBar progressBar;
private Button button;
private TextView textview;
private String url="http://app-distribute.oss-cn-qingdao.aliyuncs.com/default/shengji.apk";
private Intent intent;
private MyReceiver myReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button= (Button) findViewById(R.id.button);
progressBar= (ProgressBar) findViewById(R.id.progressBar);
textview= (TextView) findViewById(R.id.textView);
//动态注册广播接收器
myReceiver = new MyReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.test.service.RECEIVER");
registerReceiver(myReceiver, intentFilter);
intent=new Intent(this,MyService2.class);
intent.putExtra("url",url);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//启动服务
startService(intent);
}
});
}
/**
* 广播接收器
* @author len
*
*/
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//拿到进度,更新UI
int progress = intent.getIntExtra("progress", 0);
progressBar.setProgress(progress);
textview.setText(progress+"%");
}
}
@Override
protected void onDestroy() {
//停止服务
stopService(intent);
//注销广播
unregisterReceiver(myReceiver);
super.onDestroy();
}
}
相比较,如果某个service要向多个activity发送,用广播的方式更好一点
【Android IntentService和ResultReceiver的异步处理实现service与activity的交互】
其他在学习过程中应该了解的小知识点
startService与bindService的区别
静态/动态注册广播的区别
亲,给个赞鼓励一下吧~