美文网首页
Android四大组件之Service

Android四大组件之Service

作者: 故江 | 来源:发表于2019-03-01 21:44 被阅读70次
  1. 如何启动一个Service
    ①创建一个类继承自Service,Service位于android.app.Service包下,实现抽象方法onBinder(),重写onCreat()、onStartCommand()、onDestroy().
public class MyService extends Service {

    public static final String TAG = "MyService";

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG,"onCreat");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG,"onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"onDestroy");
    }
}

②配置清单文件

<service android:name=".myservice.MyService"></service>

③在MainActivity作为程序的主Activity,在里面加入启动Service和停止Service的逻辑

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn1;
    private Button btn2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        btn1 = (Button) findViewById(R.id.btn1);
        btn2 = (Button) findViewById(R.id.btn2);

        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn1:
                Intent intent1 = new Intent(this,MyService.class);
                startService(intent1);
                break;
            case R.id.btn2:
                Intent intent2 = new Intent(this,MyService.class);
                stopService(intent2);
                break;
        }
    }
}
1.PNG 2.PNG
    onBinder()方法是Service中的唯一的一个抽象方法,所以必须在自类里实现,从上图中我们看到onCreat()只会在第一次点击后创建,多次点击不会再走onCreat()方法,onStartCommand()在每次启动服务的时候都会被调用,onStartCommand方法执行时,返回的是一个int型,onDestroy()方法也只会调用一次去销毁服务。

停止服务的两种方法:
一:stopService(intent) 在外部调用
二:在服务内部(onStartCommand方法内部)使用stopSelf()方法

注:
服务对象同时只会有一个默认情况下,一个started的Service与启动他的组件在同一个线程中。上面的实例中,服务就是在主线程中运行的,如果是在服务中完成耗时操作的话,容易造成主线程阻塞。

  1. IntentService
    (1)Service类
    我们在第一段中就已经说了,服务中的代码默认运行在主线程中,如果直接在服务里执行一些耗时操作,容易造成ANR(Application Not Responding)异常,所以就需要用到多线程的知识了。
public class MyService extends Service {

    public static final String TAG = "MyService";

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG,"onCreat");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG,"onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"onDestroy");
    }
}

需要注意的是,如果没有onStartCommand()方法里的stopSelf(),服务一旦启动后,就会一直处于运行状态,必须调用stopService()或者stopSelf()方法才能让服务停止下来

(1)IntentService的用法:

①新建一个MyIntentService类,继承自IntentService,并重写父类的onHandleIntent()方法

public class MyIntentService extends IntentService {

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

    //该方法在会在一个单独的线程中执行,来完成工作任务。任务结束后,该Service自动停止
    @Override
    protected void onHandleIntent(Intent intent) {
        for (int i = 0; i < 3; i++) {
            Log.d("MyIntentService","IntentService线程的id是:"+Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyIntentService","onDestroy");
    }
}

②在清单文件中对服务进行注册服务:

<service android:name=".MyIntentService"></service>

③在MainActivity里面加入启动IntentService的逻辑

Log.d("MainActivity","主线程的id是:"+Thread.currentThread().getId());
Intent intent3 = new Intent(this,MyIntentService.class);
startService(intent3);
1.PNG
  1. Service和Thread的关系:
    Thread我们大家都知道,是用于开启一个子线程,在这里去执行一些耗时操作就不会阻塞主线程的运行。而Service我们最初理解的时候,总会觉得它是用来处理一些后台任务的,一些比较耗时的操作也可以放在这里运行,这就会让人产生混淆了。但是,如果我告诉你Service其实是运行在主线程里的,你还会觉得它和Thread有什么关系吗?
    其实,后台和子线程是两个完全不同的概念:
    Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,Service既然是运行在主线程里,在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。
    既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例;而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

4.使用Bind Service完成Service和Activity之间的通信
一、Bind Service的介绍:
应用程序组件(客户端)通过调用bindService()方法能够绑定服务,然后Android系统会调用服务的onBind()回调方法,则个方法会返回一个跟服务器端交互的Binder对象。
这个绑定是异步的,bindService()方法立即返回,并且不给客户端返回IBinder对象。要接收IBinder对象,客户端必须创建一个ServiceConnection类的实例,并且把这个实例传递给bindService()方法。ServiceConnection对象包含了一个系统调用的传递IBinder对象的回调方法。
注意:只有Activity、Service、Content Provider能够绑定服务;BroadcastReceiver广播接收器不能绑定服务。

①实现Service和Activity之间通信步骤:

public class ThreeService extends Service {

    public static final String TAG = "ThreeService";
    private MyBinder mBinder = new MyBinder();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }

    @Override
    public IBinder onBind(Intent intent) {
        //在这里返回新建的MyBinder类
        return mBinder;
    }

    ////MyBinder类,继承Binder:让里面的方法执行下载任务,并获取下载进度
    public class MyBinder extends Binder {
        public void startDownload() {
            Log.d("TAG", "startDownload() executed");
        }

        public int getProgress() {
            Log.d("TAG", "getProgress() executed");
            return 0;
        }
    }
}

②检查清单文件,是否已经对Service进行注册:

<service android:name=".myservice.ThreeService"></service>

③Activity操作

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn3;
    private Button btn4;
    private ThreeService.MyBinder myBinder;

    //匿名内部类:服务连接对象
    private ServiceConnection mConnection = new ServiceConnection() {

        //和服务绑定成功后,服务会回调该方法
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (ThreeService.MyBinder) service;
            //在Activity中调用Service里面的方法
            myBinder.startDownload();
            myBinder.getProgress();
        }

        //当服务异常终止时会调用。注意,解除绑定服务时不会调用
        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        btn3 = (Button) findViewById(R.id.btn3);
        btn4 = (Button) findViewById(R.id.btn4);

        btn3.setOnClickListener(this);
        btn4.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn3:
                Intent bindIntent = new Intent(this,ThreeService.class);
                //bindService()方法接收三个参数,第一个参数就是刚刚构建出的Intent对象,第二个参数是前面创建出的ServiceConnection的实例,第三个参数是一个标志位,这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后会自动创建Service
                bindService(bindIntent,mConnection,BIND_AUTO_CREATE);
                break;
            case R.id.btn4:
                unbindService(mConnection);
                break;
        }
    }
}

运行结果


1.PNG

可以看到,只点击了Bind Service按钮,但是oncreate()方法得到了执行,而onStartCommand()方法不会执行。

假设现在Service和Activity已经相关联了,点击Unbind Service按钮能够解除绑定,如果继续点击Unbind Service按钮,程序会异常退出,我们需要在代码中加一个判断是否绑定的标记mBound。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn1;
    private Button btn2;
    private Button btn3;
    private Button btn4;
    private ThreeService.MyBinder myBinder;
    //一开始,并没有和Service绑定.这个参数是用来显示绑定状态
    boolean mBound = false;

    //匿名内部类:服务连接对象
    private ServiceConnection mConnection = new ServiceConnection() {

        //和服务绑定成功后,服务会回调该方法
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (ThreeService.MyBinder) service;
            //在Activity中调用Service里面的方法
            myBinder.startDownload();
            myBinder.getProgress();
        }

        //当服务异常终止时会调用。注意,解除绑定服务时不会调用
        @Override
        public void onServiceDisconnected(ComponentName name) {
            //服务异常终止时,状态为未绑定
            mBound = false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        btn1 = (Button) findViewById(R.id.btn1);
        btn2 = (Button) findViewById(R.id.btn2);
        btn3 = (Button) findViewById(R.id.btn3);
        btn4 = (Button) findViewById(R.id.btn4);

        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
        btn3.setOnClickListener(this);
        btn4.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn1:
                Intent intent1 = new Intent(this,MyService.class);
                startService(intent1);
                break;
            case R.id.btn2:
                Intent intent2 = new Intent(this,MyService.class);
                stopService(intent2);
                break;
            case R.id.btn3:
                Intent bindIntent = new Intent(this,ThreeService.class);
                bindService(bindIntent,mConnection,BIND_AUTO_CREATE);
                break;
            case R.id.btn4:
                if (mBound){
                    unbindService(mConnection);
                    mBound = false;
                }
                break;
        }
    }
}

④Service的生命周期:


031222442832849.png

5.使用Bind Service完成IPC进程间通信:
(1)既然是在在同一个APP内模拟进程间通信,其实就是完成进程内通信。
也就是说,要实现:让Activity与一个远程Service建立关联,这就要使用AIDL来进行跨进程通信了(IPC)。

(2)在客户端绑定一个服务的步骤:
①实现ServiceConnection抽象类。实现过程中,必须重写一下两个回调方法:
1)onServiceConnected() 和服务绑定成功后,系统会调用这个方法来发送由服务的onBind()方法返回的IBinder对象
2)onServiceDisconnected() 当服务异常终止时会调用(如服务崩溃或被杀死时)。注意,在客户端解除绑定时不会调用该方法。
②调用bindService()方法来传递ServiceConnection类的实现;
③当系统调用你的onServiceConnected()回调方法时,你就可以开始使用接口中定义的方法来调用服务了
④调用unbindService()方法断开与服务的链接。
注:bindService()和unbindService()方法都是Context类中的方法。

(3)IPC(进程间通讯)具体的步骤如下:
①使用AIDL定义业务接口,通过ADT工具来生成一个java类,此类实现了进程间远程通讯的代理
②编写自己的业务类(继承生成的类中的Stub)来实现业务接口功能
③再通过绑定Service的方式来暴露此业务对象,给其它组件提供功能
④调用者组件通过bindService方法绑定服务,从而获取绑定成功后的远程业务对象或本地业务对象,然后就可以调用相关功能。
⑤注意:一般在使用完绑定服务后,需要解除绑定。

(4)让Activity与一个远程Service建立关联的步骤:
①新建IPerson.aidl文件,代码如下所示:

interface IPerson {
    void setName(String name);
    void setSex(String sex);
    void setAge(int age);
    String getPerson();
}
1.PNG

ADT会在build目录下自动生成一个对应的Java文件
②新建PersonImpl类,继承IPerson.Stub类,重写父类里的方法。代码如下:

public class PersonImpl extends IPerson.Stub {

    private String name;
    private String sex;
    private int age;

    @Override
    public void setName(String name) throws RemoteException {
        this.name = name;
    }

    @Override
    public void setSex(String sex) throws RemoteException {
        this.sex = sex;
    }

    @Override
    public void setAge(int age) throws RemoteException {
        this.age = age;
    }

    @Override
    public String getPerson() throws RemoteException {
        return "name="+name+",sex="+sex+",age="+age;
    }
}

③新建类MyService,代码如下:

public class FourService extends Service {

    public static final String TAG = "FourService";
    PersonImpl mBinder = new PersonImpl();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }

    @Override
    public IBinder onBind(Intent intent) {
        //在这里返回新建的MyBinder类
        return mBinder;
    }
}

④在清单文件中添加权限:

<service android:name=".myservice.FourService"></service>

④MainActivity中的代码,如下所示:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn3;
    private Button btn4;
    private ThreeService.MyBinder myBinder;
    private IPerson mIPerson;
    //一开始,并没有和Service绑定.这个参数是用来显示绑定状态
    boolean mBound = false;

    //匿名内部类:服务连接对象
    private ServiceConnection mConnection = new ServiceConnection() {

        //和服务绑定成功后,服务会回调该方法
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d("MainActivity", "onServiceConnected");
            mIPerson = IPerson.Stub.asInterface(service);
            // 打印出person对象的内存地址
            Log.d("person", "person对象的内存地址是" + mIPerson);
            try {
                mIPerson.setName("喜羊羊");
                mIPerson.setAge(22);
                mIPerson.setSex("男");
                String p = mIPerson.getPerson();
                Log.d("person","person的信息是" + p);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            //true说明是绑定状态
            mBound = true;
        }

        //当服务异常终止时会调用。注意,解除绑定服务时不会调用
        @Override
        public void onServiceDisconnected(ComponentName name) {
            //服务异常终止时,状态为未绑定
            mBound = false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        btn3 = (Button) findViewById(R.id.btn3);
        btn4 = (Button) findViewById(R.id.btn4);

        btn3.setOnClickListener(this);
        btn4.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn3:
                Intent bindIntent = new Intent(this,FourService.class);
                bindService(bindIntent,mConnection,BIND_AUTO_CREATE);
                break;
            case R.id.btn4:
                if (mBound){
                    unbindService(mConnection);
                    mBound = false;
                }
                break;
        }
    }
}

运行结果


1.PNG

实现进程间通信的两个办法:
1)办法一:将本地的Service设置为远程。只需要在清单文件中注册Service的时候将它的android:process属性指定成:remote就可以了

<service android:name=".myservice.FourService" android:process=":remote"></service>

我们已经将Service的android:process属性指定成:remote,此时Service和Activity不在同一个线程内,那么即使在Service的onStartCommand()方法中执行耗时操作而不重新开启子线程,程序也不会阻塞。

后台打印日志如下: 1.PNG
2)办法二:新建另外一个工程,真正实现远程通信。

AIDL支持的类型:八大基本数据类型、String类型、CharSequence、List、Map、自定义,那我们就来详细说下这个自定义数据类型。

相关文章

网友评论

      本文标题:Android四大组件之Service

      本文链接:https://www.haomeiwen.com/subject/bsmjuqtx.html