今天复习了服务的基础知识,下面尝试使用广播来启动一个Service进行耗时工作的处理。
首先明白,当启动服务的时候有两种方法,一种是使用StartService,另一种是使用bindService,使用bindService启动的话执行的方法onCreate--->onbind,而且这种方法启动的话,只要该service对象存在,onbind只执行一次,以后再点击bindService按钮启动服务,onbind方法就不会再执行了。而使用StartService这种方法启动的话,执行的方法是onCreate----->onStartCommand,但是这种方法启动的话每次点击startService按钮,onStartCommand方法都会得到执行。
Service启动之后和Activity运行在一个进程中(但是是两个不同的线程)。使用startService方法启动的Service,Service的销毁必须调用stopService或者stopSelf方法才能销毁,而且这种方法启动的Service当Activtiy死了之后,不调用以上两种方法杀死Service的话,service就一直活着。使用bindService启动的Service,当绑定Service的Activity死之前灭有调用unbindService的话,那么这个Service的存在就会造成内存泄露,属于半死不活状态,因为没有他此时虽然活着,但是已经没有活着的意义了,他活着也没用了,因为它活着的作用就是为了和绑定它的Activity进行通信,既然Activity已经死了,与他通信的对象也就没有了。
首先给出一个例子,使用在服务里面定义一个计算方法,让Activity去调用这个方法,并返回计算结果:
定义一个MyService继承自Service:
public class MyService extends Service {
private int china_score;
private int math_score;
private int english_score;
private Calculate calculate=new Calculate();
@Override
public void onCreate() {
Log.e("MyService","yly------1");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("MyService","yly------2");
china_score=intent.getIntExtra("china_score",0);
math_score=intent.getIntExtra("math_score",0);
english_score=intent.getIntExtra("english_score",0);
return super.onStartCommand(intent, flags, startId);
}
class Calculate extends Binder{
public double calculate(double...score){
Log.e("MyService","yly------4");
int count=score.length;
if (count==0){
return 0;
}
double sum=0;
for (double s:score){
sum+=s;
}
double averages=sum/count;
return averages;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("MyService","yly------3");
return calculate;
}
@Override
public void unbindService(ServiceConnection conn) {
Log.e("MyService","yly------4");
super.unbindService(conn);
}
@Override
public void onDestroy() {
Log.e("MyService","yly------5");
super.onDestroy();
}
}
在MainActivity里面这样写:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText china_editText;
private EditText math_editText;
private EditText english_editText;
private EditText average_score;
private Button start_service;
private Button calculate_button;
private MyService myService;
private MyService.Calculate calculate;
private ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("MainActivity","yly------1");
calculate= (MyService.Calculate) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
china_editText=findViewById(R.id.china_score);
math_editText=findViewById(R.id.math_score);
english_editText=findViewById(R.id.english_score);
average_score=findViewById(R.id.average_score);
start_service=findViewById(R.id.start_service);
calculate_button=findViewById(R.id.calculate);
start_service.setOnClickListener(this);
calculate_button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start_service:
Intent intent=new Intent("myservice");
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
break;
case R.id.calculate:
double china_score=Double.parseDouble(china_editText.getText().toString());
double math_score=Double.parseDouble(math_editText.getText().toString());
double english_score=Double.parseDouble(english_editText.getText().toString());
double result=calculate.calculate(china_score,math_score,english_score);
average_score.setText(result+"");
break;
default:
break;
}
}
@Override
protected void onDestroy() {
Log.e("MainActivity","yly------2");
super.onDestroy();
}
}
主布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:orientation="vertical"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center">
<TextView
android:id="@+id/textView_china"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="语文:"
android:textSize="20sp"
android:textColor="#000"/>
<EditText
android:id="@+id/china_score"
android:layout_width="0dp"
android:layout_weight="4"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center">
<TextView
android:id="@+id/textView_math"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="数学:"
android:textSize="20sp"
android:textColor="#000"/>
<EditText
android:id="@+id/math_score"
android:layout_width="0dp"
android:layout_weight="4"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center">
<TextView
android:id="@+id/textView_english"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="英语:"
android:textSize="20sp"
android:textColor="#000"/>
<EditText
android:id="@+id/english_score"
android:layout_width="0dp"
android:layout_weight="4"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center">
<TextView
android:id="@+id/textView_average"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="平均成绩:"
android:textSize="20sp"
android:textColor="#000"/>
<EditText
android:id="@+id/average_score"
android:layout_width="0dp"
android:layout_weight="4"
android:layout_height="wrap_content" />
</LinearLayout>
<Button
android:id="@+id/start_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="启动"
android:layout_gravity="center_horizontal"/>
<Button
android:id="@+id/calculate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="计算"
android:layout_gravity="center_horizontal"/>
</LinearLayout>
注意一点:如果在MainActivity中,把计算按钮里面的内容也写道启动按钮里面,如:
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start_service:
Intent intent=new Intent("myservice");
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
double china_score=Double.parseDouble(china_editText.getText().toString());
double math_score=Double.parseDouble(math_editText.getText().toString());
double english_score=Double.parseDouble(english_editText.getText().toString());
double result=calculate.calculate(china_score,math_score,english_score);
average_score.setText(result+"");
break;
这这样做的话会报错,因为在点击的时候calculate这个对象还没有造出来,这个过程是异步的。当onBind成功的时候,才会调用ServiceConnect这个方法,中间是会有一定的时间间隔的。所以不能同时执行。
如果我们不希望点击按钮才能启动服务的话,可以把bindService这个按钮实现的功能写在MainActivity的onCreate方法里面。为了代码的严谨性,我们可以在计算按钮里面添加一行判断语句,来判断calculate对象是否返回成功。还有就是需要在MainActivity里的onDestroy方法里面,把服务解绑。具体做法如下:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText china_editText;
private EditText math_editText;
private EditText english_editText;
private EditText average_score;
private Button calculate_button;
private MyService myService;
private MyService.Calculate calculate;
private ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("MainActivity","yly------1");
calculate= (MyService.Calculate) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
china_editText=findViewById(R.id.china_score);
math_editText=findViewById(R.id.math_score);
english_editText=findViewById(R.id.english_score);
average_score=findViewById(R.id.average_score);
calculate_button=findViewById(R.id.calculate);
calculate_button.setOnClickListener(this);
//绑定服务
Intent intent=new Intent("myservice");
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.calculate:
double china_score=Double.parseDouble(china_editText.getText().toString());
double math_score=Double.parseDouble(math_editText.getText().toString());
double english_score=Double.parseDouble(english_editText.getText().toString());
double result=calculate.calculate(china_score,math_score,english_score);
average_score.setText(result+"");
break;
default:
break;
}
}
@Override
protected void onDestroy() {
Log.e("MainActivity","yly------2");
super.onDestroy();
unbindService(serviceConnection);
}
}
以上就是Service与Activity的通信,以上的工作使用startSevice能完成吗?答案是能完成,但是很困难,在Service里面计算的结果需要使用广播来帮助回传给Activity。下面的是使用startService来启动Service来执行计算,但是只能执行计算功能,并不能把计算结果回传给Activity。
在MainActivity里面点击按钮启动Service并把值通过Intent传过去:
case R.id.test_button:
Intent intent_test=new Intent("myservice");
double china_score=Double.parseDouble(china_editText.getText().toString());
double math_score=Double.parseDouble(math_editText.getText().toString());
double english_score=Double.parseDouble(english_editText.getText().toString());
intent_test.putExtra("china_score",china_score);
intent_test.putExtra("math_score",math_score);
intent_test.putExtra("english_score",english_score);
startService(intent_test);
break;
在MyService里面的onStartCommand方法里面执行相应的处理逻辑:
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("MyService","yly------2");
china_score=intent.getDoubleExtra("china_score",0);
math_score=intent.getDoubleExtra("math_score",0);
english_score=intent.getDoubleExtra("english_score",0);
double result=calculate(china_score,math_score,english_score);
Log.e("MyService","三门课程的平均分数是:"+result);
return super.onStartCommand(intent, flags, startId);
}
以上只能打印出三门课的平均成绩,并不能把计算结果返回给Activity进行显示。
下面讲解使用广播来启动Service并解决这一困境:
首先主布局的写法:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText china_editText;
private EditText math_editText;
private EditText english_editText;
private EditText average_score;
private Button test;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
china_editText=findViewById(R.id.china_score);
math_editText=findViewById(R.id.math_score);
english_editText=findViewById(R.id.english_score);
average_score=findViewById(R.id.average_score);
test=findViewById(R.id.test_button);
BroadcastReceiver broadcastReceiver=new BrocastReceiver();
//注册相应的广播才能尽享接收
IntentFilter intentFilter=new IntentFilter("send.brocast");
registerReceiver(broadcastReceiver,intentFilter);
test.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.calculate:
break;
case R.id.test_button:
Intent intent_test=new Intent("myservice");
double china_score=Double.parseDouble(china_editText.getText().toString());
double math_score=Double.parseDouble(math_editText.getText().toString());
double english_score=Double.parseDouble(english_editText.getText().toString());
intent_test.putExtra("china_score",china_score);
intent_test.putExtra("math_score",math_score);
intent_test.putExtra("english_score",english_score);
startService(intent_test);
break;
default:
break;
}
}
@Override
protected void onDestroy() {
Log.e("MainActivity","yly------2");
super.onDestroy();
//unbindService(serviceConnection);
}
class BrocastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
double averages=intent.getDoubleExtra("cal_result",0);
average_score.setText(averages+" ");
}
}
}
MyService的写法:
public class MyService extends Service {
private double china_score;
private double math_score;
private double english_score;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
china_score=intent.getDoubleExtra("china_score",0);
math_score=intent.getDoubleExtra("math_score",0);
english_score=intent.getDoubleExtra("english_score",0);
double result=calculate(china_score,math_score,english_score);
Log.e("MyService","三门课程的平均分数是:"+result);
//在这里我发送一个广播到Activity,当然Activity必须要注册广播才行,然后让intent携带计算结果
Intent intent_brocast=new Intent("send.brocast");
intent_brocast.putExtra("cal_result",result);
sendBroadcast(intent_brocast);
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
}
public double calculate(double...score){
int count=score.length;
if (count==0){
return 0;
}
double sum=0;
for (double s:score){
sum+=s;
}
double averages=sum/count;
return averages;
}
}
这样就能回传计算结果了,并显示在界面。
以上问题已经被解决了,但是现在我想在界面启动的时候就会启动Servcie,接下来点击按钮,发送一条广播让Service计算,然后把结果回传。要想解决这个问题,只需要在Service里面注册一个广播,用于接受来自Activity的广播就行了,如下:
MainActivity代码:
public class MainActivity extends AppCompatActivity {
private EditText china_editText;
private EditText math_editText;
private EditText english_editText;
private EditText average_score;
private Button test;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
china_editText=findViewById(R.id.china_score);
math_editText=findViewById(R.id.math_score);
english_editText=findViewById(R.id.english_score);
average_score=findViewById(R.id.average_score);
test=findViewById(R.id.test_button);
//界面产生就启动Service
Intent intentService=new Intent("myservice");
startService(intentService);
BroadcastReceiver broadcastReceiver=new BrocastReceiver();
//注册相应的广播才能尽享接收
IntentFilter intentFilter=new IntentFilter("send.brocast");
registerReceiver(broadcastReceiver,intentFilter);
//点击按钮发送广播给服务让服务执行计算
test.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent_sendBroad=new Intent("send.brocast.service");
double china_score=Double.parseDouble(china_editText.getText().toString());
double math_score=Double.parseDouble(math_editText.getText().toString());
double english_score=Double.parseDouble(english_editText.getText().toString());
intent_sendBroad.putExtra("china_score",china_score);
intent_sendBroad.putExtra("math_score",math_score);
intent_sendBroad.putExtra("english_score",english_score);
sendBroadcast(intent_sendBroad);
}
});
}
@Override
protected void onDestroy() {
Log.e("MainActivity","yly------2");
super.onDestroy();
//unbindService(serviceConnection);
}
class BrocastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
double averages=intent.getDoubleExtra("cal_result",0);
average_score.setText(averages+" ");
}
}
}
MyService里面的代码:
public class MyService extends Service {
private double china_score;
private double math_score;
private double english_score;
private ServiceReceiver serviceReceiver;
@Override
public void onCreate() {
super.onCreate();
serviceReceiver=new ServiceReceiver();
IntentFilter intentFilter=new IntentFilter("send.brocast.service");
registerReceiver(serviceReceiver,intentFilter);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
}
public double calculate(double...score){
int count=score.length;
if (count==0){
return 0;
}
double sum=0;
for (double s:score){
sum+=s;
}
double averages=sum/count;
return averages;
}
class ServiceReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
china_score=intent.getDoubleExtra("china_score",0);
math_score=intent.getDoubleExtra("math_score",0);
english_score=intent.getDoubleExtra("english_score",0);
double result=calculate(china_score,math_score,english_score);
//在这里我发送一个广播到Activity,当然Activity必须要注册广播才行,然后让intent携带计算结果
Intent intent_brocast=new Intent("send.brocast");
intent_brocast.putExtra("cal_result",result);
sendBroadcast(intent_brocast);
Log.e("MyService","三门课程的平均分数是:"+result);
}
}
}
以上就是Service和Activity之间通过广播来通信。有需要完整代码的或者觉得有问题的欢迎留言。