0
点赞
收藏
分享

微信扫一扫

Android APP完整基础教程(06)四大组件之Service

hwwjian 2022-01-20 阅读 97

1 一张图解读Service

这里用一张图解读 生命周期 & 基本概念 & 关键回调方法解读

2 Service类关系解读

关于Service类的父类、子类的继承关系图如下所示:

3 配置Service

Service和Activity都是android系统组件,都需要在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Service。简单的service组件内容如下:

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

常见属性一般有name、exported(是否允许被其他应用调用),permission(启动service需要的权限)、process(该service所在进程,默认是在APP进程中,但可以指定其他进程),无需指定label属性(因为没有界面)。如果未设置intent-filter这一选项时则不能直接响应Intent,只能通过指定Component的Intent来启动。

4 Service操作

4.1 Service启动/停止

启动和停止服务的关键API如下所示:

//定义Intent,携带service
Intent intent = new Intent(MainActivity.this,com.ags.XXXService.class);

//启动服务
startService(intent);

//停止服务
stopService(intent);

关于service:

  • 连续多次启动service后,onCreate回调函数执行一次,onStartCommand回调函数执行多次。
  • 使用启动/停止服务的API并不能与原来的调用者之间有数据交换/通信的能力,想要有通信能力,需要绑定service。

注意:从android 5.0开始Google要求必须使用显式Intent启动service组件。

4.2 绑定Service并通信

如果service想和启动者之间进行通信和交换数据,则应该使用bindService()和unbindService()方法启动/关闭service。这里使用一个案例来解读下bindService和unbindService方法。这里设置了两个按键,一个绑定服务,绑定成功后调用service组件的方法addNum;另一个解绑服务。

首先,写一个自定义的service继承系统服务,名为BinderService,代码实现如下:

public class BinderService extends Service {
    private static String TAG = "BinderService";

    public class AgsBinder extends Binder {
        public BinderService getService(){
            return BinderService.this;
        }
    }

    //通过binder实现 调用者和service之间的通信
    private AgsBinder binder = new AgsBinder();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnBind");
        return super.onUnbind(intent);
    }

    @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);
    }

    /**参数解读:
     *service:通过intent指定要启动的service
     * conn:监听访问者和service的连接情况,有连接成功/失败的回调处理
     * flags:是否自动创建service
     */
    @Override
    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
        Log.d(TAG, "bindService");
        return super.bindService(service, conn, flags);
    }

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

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

    public int addNum(int a,int b){
        Log.d(TAG, "addNum: "+(a+b));
        return a+b;
    }
}

其次,MainActivity代码的实现如下所示:

public class MainActivity extends AppCompatActivity {
    private static String TAG = "MainActivity";
    private Button btn_bind;
    private Button btn_unbind;
    private BinderService binderservice = null;
    private boolean isBound = false;

    private ServiceConnection connection = new ServiceConnection() {
        @Override //连接成功回调
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected");
            isBound = true;
            BinderService.AgsBinder binder = (BinderService.AgsBinder)service;
            binderservice = binder.getService();
            Log.d(TAG, "Activity onServiceConnected");
            //连接成功后就可以调用binderservice的各种方法了。
            int x = binderservice.addNum(3,4);
            Log.d(TAG, "Activity call binderservice.addNum:"+x);
        }

        @Override //发生意外回调,unbind并不会导致回调
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected");
            isBound = false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_bind = findViewById(R.id.btn_bind);
        btn_unbind = findViewById(R.id.btn_unbind);

        btn_bind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, com.ags.myapplication.BinderService.class);
                //intent.putExtra("from", "MainActivity");
                Log.d(TAG, "onClick:bindService");
                //启动并绑定service
                bindService(intent,connection,BIND_AUTO_CREATE);
            }
        });
        btn_unbind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(isBound){
                    Log.d(TAG, "onClick:unbindService");
                    //解绑service
                    unbindService(connection);
                    isBound = false;
                }
            }
        });
    }
}

注意:启用端和Service解绑时,onServiceDisconnected不会被调用。onServiceDisconnected被调用的情况是发生在启用端和Service连接意外丢失时,这个时候确定 启用端和Service一定是断开连接的。

5 IntentService

service存在的问题:与所在应用位于同一个进程中,不能在service中直接处理耗时任务。要想解决这个问题就需要使用IntentService了,IntentService有如下优势:

  • 会创建单独的worker线程来处理所有Intent请求 以及 处理onHandleIntent方法实现的代码。
  • 请求处理完成后自动停止,无需使用stopSelf方法来停止该service。
  • 只需实现onHandleIntent方法就可以启动service在后台执行耗时工作。

这里使用一个案例来解读下耗时任务的处理,耗时任务intentService代码实现如下:

public class SecondService extends IntentService {
    private static String TAG = "IntentService";

    public SecondService() {
        super("SecondService");
    }

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

    //该方法内可以执行耗时函数而不阻塞UI主线程
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.d(TAG, "onHandleIntent: Start Task");
        synchronized (this){
            try {
                wait(20*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        Log.d(TAG, "onHandleIntent: End Task");
    }
}

MainActivity代码如下所示:

public class MainActivity extends AppCompatActivity {
    private static String TAG = "MainActivity";
    private Button btn_start;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_start = findViewById(R.id.btn_start);

        btn_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //和正常service一样启动。
                Intent intent = new Intent(MainActivity.this, com.ags.myapplication.SecondService.class);
                startService(intent);
            }
        });
    }
}

由于IntentService使用的是单独的worker线程,因此不会阻塞UI主线程。

6 跨进程AIDL Service

6.1 aidl简介

关键词解读:

  • IPC(Inter-Process Communication):即跨进程通信,用于进程间的数据交换。
  • AIDL(android interface definition language):Android接口定义语言用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。

AIDL的目的:用来定义远程接口,即定义两个进程之间的通信接口。

为啥要弄AIDL?实现跨进程需要编写很多复杂的代码,于是android提供了AIDL,通过编写简单的AIDL文件,编译器根据AIDL的规则生成那些复杂的代码,化繁为简是根本。

6.2 接口规则与编译

aidl接口文件( .aidl为后缀)支持的数据类型如下:

  • 基本数据类型 byte、char、short、int、long、float、double、boolean(默认tag为in)
  • 字符串 String,CharSequence(默认tag为in)
  • 实现Parcelable接口的数据类型(需要import)
  • List 类型(内容必须是AIDL支持的类型,或是其它AIDL声明的接口,需要import)
  • Map类型(内容必须是AIDL支持的类型,或是其它AIDL声明的接口,需要import

关于定向tag:AIDL中的定向 tag 表示了在跨进程通信中数据的流向:

  • in 表示数据只能由客户端流向服务端。可理解为 客户端的输入,服务端输出。
  • out 表示数据只能由服务端流向客户端。可理解为 服务端输入,客户端输出。
  • inout 表示数据可在服务端与客户端之间双向流通。客户端/服务端均可输入/输出。

接下来直接新建一个aidl文件即可,内容如下:

// IMyAidlInterface.aidl
package com.ags.myservice;

interface IMyAidlInterface {
    void testMethod1(String str);
    void testMethod2(int id);
}

执行make project后 编译生成IMyAidlInterface.java文件。

6.3 aidl的使用

这里从两个方面:client(获取并使用服务)和service(实现并提供服务)来看如何使用:

  • service端关键步骤:建立并定义aidl文件->实现接口方法和生命周期方法->在AndroidMainfest.xml文件中注册服务 & 声明为远程服务。
  • client端关键步骤:拷贝服务端AIDL文件到客户端目录下->通过Intent对指定的Service进行绑定->使用Stub.asInterface接口获取服务端Binder,调用服务提供的接口方法。

案例实现如下:

@1 服务端Service:

这里建立一个aidl文件,直接copy上面的,如下所示:

// IMyAidlInterface.aidl
package com.ags.myservice;

interface IMyAidlInterface {
    void testMethod1(String str);
    void testMethod2(int id);
}

编译后,生成IMyAidlInterface.java。内容较多,这里仅展示框架部分,如下所示:

package com.ags.myservice;
public interface IMyAidlInterface extends android.os.IInterface
{
  public static class Default implements com.ags.myservice.IMyAidlInterface
  {
    //...
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  public static abstract class Stub extends android.os.Binder implements com.ags.myservice.IMyAidlInterface
  {
    private static final java.lang.String DESCRIPTOR = "com.ags.myservice.IMyAidlInterface";
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    public static com.ags.myservice.IMyAidlInterface asInterface(android.os.IBinder obj)
    {
      //...
    }
    @Override public android.os.IBinder asBinder()
    {
      return this;
    }
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
      //...
    }
    private static class Proxy implements com.ags.myservice.IMyAidlInterface
    {
      private android.os.IBinder mRemote;
      Proxy(android.os.IBinder remote)
      {
        mRemote = remote;
      }
      @Override public android.os.IBinder asBinder()
      {
        return mRemote;
      }
      public java.lang.String getInterfaceDescriptor()
      {
        return DESCRIPTOR;
      }
	  //...
      public static com.ags.myservice.IMyAidlInterface sDefaultImpl;
    }
    static final int TRANSACTION_testMethod1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_testMethod2 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    //...
    public static com.ags.myservice.IMyAidlInterface getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public void testMethod1(java.lang.String str) throws android.os.RemoteException;
  public void testMethod2(int id) throws android.os.RemoteException;
}

有了 IMyAidlInterface这类,我们创建一个Service,实现aidl中的方法,如下所示:

public class TestAIDLService extends Service {
    private static String TAG = "TestAIDLService";

    //实例化AIDL的Stub类(Binder子类),实现aidl中的方法
    IMyAidlInterface.Stub mBinder = new  IMyAidlInterface.Stub(){
        @Override
        public void testMethod1(String str) throws RemoteException {
            Log.d(TAG, "service Impl:testMethod1:exec");
        }
        @Override
        public void testMethod2(int id) throws RemoteException {
            Log.d(TAG, "service Impl:testMethod1:exec");
        }
    };

    //life cycle functions
    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate: exec");
        super.onCreate();
    }

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

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind: exec");
        //返回继承自Binder的Stub类型的Binder
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnbind: exec");
        return super.onUnbind(intent);
    }

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

在AndroidMainfest.xml中注册服务 & 声明为远程服务,配置为:

<service
    android:name=".TestAIDLService"
    android:process=":remote"  
    android:exported="true">
    <intent-filter>
        <action android:name="com.ags.myservice.IMyAidlInterface"/>
    </intent-filter>
</service>

注意:

  • 这里remote表示将本地服务设置成远程服务。
  • 此处Intent的action必须写成“服务器端包名.aidl文件名”。

@2 客户端MainActivity:

将服务端的AIDL文件所在的包复制到客户端目录下($Project/app/src/main),并编译。然后编写代码,如下所示:

public class MainActivity extends AppCompatActivity {
    private static String TAG = "MainActivity";
    private Button btn_start;
    private IMyAidlInterface aidlService;

    private ServiceConnection connection = new ServiceConnection(){
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            aidlService = IMyAidlInterface.Stub.asInterface(service);
            try {
                aidlService.testMethod1("testString");
                Log.d(TAG, "onServiceConnected");
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_start = findViewById(R.id.btn_start);
        btn_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //这里参数与服务器端的action一致,为"服务器包名.aidl接口文件名"
                Intent intent = new Intent("com.ags.myservice.IMyAidlInterface");
                //注意:Android5.0后只通过显式Intent绑定远程Service
                intent.setPackage("com.ags.myservice");//指定包名
                bindService(intent, connection, Context.BIND_AUTO_CREATE);
            }
        });
    }
}

整个过程中横跨两个应用,调用者是客户端,被调用者是服务端。至此AIDL 通信案例完成。

 关于AIDL更多内容可查看:Android 开发者之Android 接口定义语言 (AIDL)

7 系统Service

这里主要解读getSystemService方法,如果让安卓应用能够发短信、来电处理、接收陀螺仪数据等。。。那么获取系统服务就必不可少,同时 getSystemService是Android很重要的一个API,它是Activity的一个方法,根据传入的NAME来取得对应的Object,进而获得系统服务。使用如下所示:

public class MainActivity extends AppCompatActivity {
    private static String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TelecomManager telecomManager = (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
    }
}

获取了系统服务,接下来就可以根据每个不同的服务来获取需要的方法了。更多关于getSystemService的内容可以查看文档:Android activity关键方法getSystemService详解

8 官方文档索引

关于service 更多内容查看文档:Android组件之Service组件

举报

相关推荐

0 条评论