Android开发让广播来启动Service进行耗时工作的处理过程详解

今天复习了服务的基础知识,下面尝试使用广播来启动一个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之间通过广播来通信。有需要完整代码的或者觉得有问题的欢迎留言。

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值