Android四大组件之Service

简介:    

Service(服务)是Android中实现程序后台运行的解决方案,非常适合去执行那些不需要和用户交互而且还要长期运行的任务。服务不依赖于任何用户界面,即使程序被切换到后台,或者打开了另外一个应用程序,服务任然能够正常运行。

写在前面:

1.服务并不是运行在一个独立的进程中,而是依赖于创建服务时多在的应用进程。当某个应用进程被杀掉时,所有依赖于该进程的服务也都会停止运行。

2.服务并不会自动开启线程,所有的代码都是默认运行在主线程中。我们需要在服务的内部手动创建子线程,在子线程中执行具体的任务。否则可能会出现主线程被阻塞的情况。

服务的生命周期:

启动一个 Service 有两种方法:调用 startService() 或 bindService。下图左侧是启动式服务,右侧是绑定式服务

启动式服务:

简介:此方式仅仅是启动该服务,不与服务交互,无法获取服务的运行状态等。

import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onCreate() {
        Log.i("======", "onCreate running.");
        super.onCreate();
    }
    
    @Override
    public void onDestroy() {
        Log.i("======", "onDestroy running.");
        super.onDestroy();
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("======", "onStartCommand running.");
        return super.onStartCommand(intent, flags, startId);
    }
    
}

需要在 AndroidManifest.xml 文件中进行注册:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.servicetest"
    android:versionCode="1"
    android:versionName="1.0" >
......
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
......
        <service android:name=".MyService" >
        </service>

    </application>
</manifest>

在antivity中定义两个按钮,分别触发开启/停止服务操作:

Intent startIntent = new Intent(this, MyService.class);
startIntent.putExtra("op", 1); //可以向service中传数据
startService(startIntent); // 启动服务


Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent); // 停止服务

服务未创建时,onCreat()**onStartCommand() **两个方法都会执行。

服务已创建,启动只执行onStartCommand() 。

如果没有停止按钮,需要服务自己停止时,只需要在服务类的任何一个位置调用 **stopSelf() **方法就行。

在同一个应用程序任何位置调用 startService() 方法都能启动 Service。系统回调 Service 类的 onCreate() 方法以及 onStart() 方法。这种方式启动的 Service 会一直运行在后台,直到调用 Context.stopService 或者 selfStop() 方法。如果一个 Service 已经被启动,其它代码再试图调用 startService() 方法启动 Service ,将不执行 onCreate(),而是重新执行一次 onStartCommand() 方法。

在MyService的各方法打印日志可以查看代码执行顺序。

 

绑定式服务(活动和服务进行通信):

在启动式服务中,我们只是启动和停止了服务,启动之后,活动和服务基本没什么关系了,服务中具体执行了什么逻辑,完成的怎么样,活动都无法知晓,不能控制。此时就需要绑定服务来解决问题。

在 MyService 中:

1. 创建内部类 MyBinder 继承自 Binder,里面是活动需要调用的内容。

2. 在 MyService 中创建 MyBinder 的实例。

3. 在 onBind() 方法里返回这个实例 mBinder。

public class MyService extends Service {
    private DownloadBinder mBinder = new DownloadBinder();
    class DownloadBinder extends Binder {
        public void startDownload() {
            Log.d("MyService", "startDownload executed");
        }
        public int getProgress() {
            Log.d("MyService", "getProgress executed");
            return 0;
        }
    }
    //该方法是 Service 类中定义的抽象方法,必须实现
    //用于获取该 Service 对象,如果返回  null,则意味着该 Service 不支持  bind
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
.....
}

当 Activity 使用 bindService 方法,绑定 Service 时,并不能直接获取到 Service 对象,需要借助 ServiceConnection 对象。onServiceConnected 方法会在绑定成功时执行。bindService() 方法执行后 Service 会回调 onBind() 方法,可以从这里返回一个实现了 IBinder 接口的类,在应用程序中就可以通过这个类和 Service 通信,得到 Service 运行的状态或其他操作。如果 Service 没有运行,使用这个方法启动 Service 会调用 onCreate() 方法,但不会调用 onStartCommand() 方法。

在 MainActivity 中:

1. 创建 ServiceConnection 的匿名类实例,重写 onServiceConnected() 方法和 onServiceDisconnected() 方法。

  • 这两个方法分别会在活动与服务成功绑定以及解除绑定的时候调用。

2. 在 onServiceConnected() 方法中,通过向下转型得到 DownloadBinder 的实例。

  • 有了这个实例,可以在活动中根据具体的场景来调用 DownloadBinder 中的任何 public 方法。

绑定活动和服务:

1. 创建 Intent。

2. 用 bindService() 方法绑定活动和服务。

  • 第一个参数:Intent。

  • 第二个参数:ServiceConnection 的实例。

  • 第三个参数:这里传入** BIND_AUTO_CREATE** 表示在活动和服务进行绑定后自动创建服务,即执行 onCreate() 方法

3. 用 unbindService() 方法解除绑定。参数:ServiceConnection 的实例。

public class MainActivity extends Activity implements OnClickListener {
    private Button startService;
    private Button stopService;
    private Button bindService;
    private Button unbindService;

    private MyService.DownloadBinder downloadBinder;

    private ServiceConnection connection = new ServiceConnection() {

        @Override
            public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadBinder = (MyService.DownloadBinder) service;
            downloadBinder.startDownload();
            downloadBinder.getProgress();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ......
        bindService = (Button) findViewById(R.id.bind_service);
        unbindService = (Button) findViewById(R.id.unbind_service);
        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            ......
            case R.id.bind_service:
                Intent bindIntent = new Intent(this, MyService.class);
                bindService(bindIntent, connection, BIND_AUTO_CREATE); // 绑定服务
                break;
            case R.id.unbind_service:
                unbindService(connection); // 解绑服务
                break;
            default:
                break;
        }
    }
}

 

  • 调用 **startService() 方法,相应的服务就会启动起来,并回调 onStartCommand() 方法。如果这个服务之前还没有创建过onCreate() 方法会先于 onStartCommand() **方法执行。
  • 服务启动了之后会一直保持运行状态,直到 stopService()或 **stopSelf() **方法被调用。
  • 还可以调用** bindService() 来获取一个服务的持久连接,这时就会回调服务中的 onBind() 方法。如果这个服务之前还没有创建过onCreate()** 方法会**先于 onBind() **方法执行。
  • 当调用了** startService() **方法后,又去调用 stopService() 方法,这时服务中的onDestroy() 方法就会执行,表示服务已经销毁了。
  • 当调用了 **bindService() **方法后,又去调用 unbindService() 方法,onDestroy() 方法也会执行
  • 需要注意的是,当对一个服务既调用了 startService() 方法又调用了 bindService() 方法一个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。所以,这种情况下要同时调用 stopService() 和 unbindService() 方法onDestroy() 方法才会执行

Service 的两种启动方法有可能出现交叉调用的情况。如果出现这种情况,需要按照启动的顺序依次调用与启动对应的结束方式。例如:先调用 startService() ,然后再调用 bindService(),启动并绑定 Service 可以达到既保持和 Service 的通信,又使得 Service 不会随着 Activity 的退出而退出。当不需要绑定时,先调用 unbindService() 方法,此时 Service 会执行 onUnbind(),但不会把这个 Service 销毁。只有再调用 stopService() 时,Service 才会销毁。

服务的更多技巧

根据所使用的区域不同,Service 分为 Local Service 和 Remote Service

(1)Local Service(本地服务)用于应用程序内部,当不需要与 Service 交互时,可以采用 startService() 方式启动;需要和Service 交互时,采用 bindService() 方式启动。

(2)Remote Service(远程服务)可以让其他应用程序访问该服务(Android 各应用程序都是互相独立的,数据独享),实际上就是可以进程间通信(RPC),这时需要使用 Android 提供的接口描述语言(AIDL)来定义远程服务的接口。emmm....用到再说吧

使用前台服务

  • 前台服务会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。如天气应用经常要用到。
  • 希望服务可以一直保持运行状态,而不会由于系统内存不足的原因导致被回收,就可以考虑使用前台服务。
Notification.Builder builder = new Notification.Builder(MainActivity.this);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle("title");
builder.setContentText("text");
builder.setWhen(System.currentTimeMillis()); // 显示时间

Intent intent=new Intent(MainActivity.this,Main2Activity.class);
PendingIntent pi=PendingIntent.getActivity(MainActivity.this,0,intent,PendingIntent.FLAG_CANCEL_CURRENT);
builder.setContentIntent(pi);

Notification notification = builder.build();

startForeground(1, notification);

使用很简单,就是把创建 Notification 方法里的 manager.notify(1, notification); 换成startForeground(1, notification);

使用 IntentService

  • 服务中的代码都是默认运行在主线程当中的,如果直接在服务里去处理一些耗时的逻辑,就很容易出现 ANR(Application Not Responding)的情况。
  • 所以对于一些耗时操作,需要在服务中开启子线程执行。
  • 因为服务启动后会一直处于开启状态,有时也会希望服务在执行完毕后自动停止。可以这样写。
public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                //TODO 处理具体的逻辑
                stopSelf();
            }
        }).start();

        return super.onStartCommand(intent, flags, startId);
    }
}
  • 为了可以简单地创建一个异步的、会自动停止的服务,Android 专门提供了一个 IntentService类:
public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("MyIntentService"); // 调用父类的有参构造函数
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // 打印当前线程的id
        Log.d("MyIntentService", "Thread id is " + Thread.currentThread().getId());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyIntentService", "onDestroy executed");
    }
}
  1. 首先要提供一个无参的构造函数,并且必须在其内部调用父类的有参构造函数。
  2. 在子类中去实现** onHandleIntent()** 这个抽象方法,这个方法已经是在子线程中运行的了。
  3. 根据 IntentService 的特性,这个服务在运行结束后应该是会自动停止的,即执行** onDestroy()** 方法。




参考:

https://www.jianshu.com/p/f801be783c3b

https://www.cnblogs.com/renzimu/articles/4515019.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值