详解Service以及Service进程间通信

1、Android服务

服务(Service)是Android系统中4大组件之一。服务主要用于两个目的:后台运行和跨进程访问。通过启动一个服务,可以在不显示界面的前提下在后台运行指定的任务,这样可以不影响用户做其他事情。通过AIDL服务可以实现不同进程之间的通信,这也是服务的重要用途之一

Service并没有实际界面,而是一直在Android系统的后台运行。一般使用Service为应用程序提供一些服务,或不需要界面的功能,例如,从网上下载文件、控制Video播放器等。本节主要介绍Service的启动和结束过程(Service的生命周期)以及启动Service的各种方法

1.1 Service的生命周期

 Service和Activity一样,也有从启动到销毁的过程,但Service的过程比Activity简单的多:  创建->开始->销毁

public void onCreate();                     //  创建服务  
public void onStartCommand(Intent intent, int startId);            //  开始服务  
public void onDestroy();                        //  销毁服务 

一个服务只会创建一次,销毁一次,但可以开始多次,因此,onCreate和onDestroy方法只会被调用一次,而onStartCommand方法会被调用多次

//  MyService是一个服务类,该类必须从android.app.Service类继承  
public class MyService extends Service  
{  
    @Override  
    public IBinder onBind(Intent intent)  
    {  
        return null;  
    }  
    //  当服务第1次创建时调用该方法  
    @Override  
    public void onCreate()  
    {  
        Log.d("MyService", "onCreate");  
        super.onCreate();  
    }  
    //  当服务销毁时调用该方法  
    @Override  
    public void onDestroy()  
    {  
        Log.d("MyService", "onDestroy");  
        super.onDestroy();  
    }  
    //  当开始服务时调用该方法  
    @Override  
    public void onStartCommand(Intent intent, int startId)  
    {  
        Log.d("MyService", "onStartCommand");  
        super.onStart(intent, startId);  
    }  
} 

在MyService中实现了Service类中3个生命周期方法,并在这些方法中输出了相应的日志信息,以便更容易地观察事件方法的调用情况。

写Android的应用组件时要注意,不管是编写什么组件(例如,Activity、Service等),都需要在AndroidManifest.xml文件中进行配置。MyService类也不例子。配置这个服务类很简单,只需要在AndroidManifest.xml文件的<application>标签中添加如下代码即可

<service android:enabled="true" android:name=".MyService" />

其中android:enabled属性的值为true,表示MyService服务处于激活状态。虽然目前MyService是激活的,但系统仍然不会启动MyService,要想启动这个服务。必须显式地调用startService方法。如果想停止服务,需要显式地调用stopService方法,代码如下

Intent serviceIntent = new Intent(this, MyService.class);
public void onClick(View view)  
{  
    switch (view.getId())  
    {  
        case R.id.btnStartService:  
            startService(serviceIntent);        
//  单击【Start Service】按钮启动服务  
            break;  
        case R.id.btnStopService:  
            stopService(serviceIntent);     
//  单击【Stop Service】按钮停止服务  
            break;  
    }  
} 

启动后会看到:

onCreate  
onStartCommand

停止后会看到:

onDestroy

1.2 绑定Activity和Service

public class MyService extends Service  
{  
    private MyBinder myBinder = new MyBinder();  
    //  成功绑定后调用该方法  
    @Override  
    public IBinder onBind(Intent intent)  
    {  
        Log.d("MyService", "onBind");  
        return myBinder;  
    }  
    //  重新绑定时调用该方法  
    @Override  
    public void onRebind(Intent intent)  
    {  
        Log.d("MyService", "onRebind");  
        super.onRebind(intent);  
    }  
    //  解除绑定时调用该方法  
    @Override  
    public boolean onUnbind(Intent intent)  
    {  
        Log.d("MyService", "onUnbind");  
        return super.onUnbind(intent);  
    }  
    @Override  
    public void onCreate()  
    {  
        Log.d("MyService", "onCreate");  
        super.onCreate();  
    }  
    @Override  
    public void onDestroy()  
    {  
        Log.d("MyService", "onDestroy");  
        super.onDestroy();  
    }  
    @Override  
    public void onStartCommand(Intent intent, int startId)  
    {  
        Log.d("MyService", "onStart");  
        super.onStart(intent, startId);  
    }  
    public class MyBinder extends Binder  
    {  
        MyService getService()  
        {  
            return MyService.this;  
        }  
    }  
} 

现在定义一个MyService变量,和一个ServiceConnection变量:

private MyService myService;  
private ServiceConnection serviceConnection = new ServiceConnection() {  
    //  连接服务失败后,该方法被调用  
    @Override  
    public void onServiceDisconnected(ComponentName name)  
    {  
        myService = null;  
        Toast.makeText(Main.this, "Service Failed.", Toast.LENGTH_LONG).show();  
    }  
    //  成功连接服务后,该方法被调用。在该方法中可以获得MyService对象  
    @Override  
    public void onServiceConnected(ComponentName name, IBinder service)  
    {  
        //  获得MyService对象  
        myService = ((MyService.MyBinder) service).getService();  
        Toast.makeText(Main.this, "Service Connected.", Toast.LENGTH_LONG).show();  
    }  
}; 

最后使用bindService来绑定Activity和Service

bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
unbindService(serviceConnection);

1.3 在BroadcastReceiver中启动Service

public class StartupReceiver extends BroadcastReceiver {  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        //  启动一个Service  
        Intent serviceIntent = new Intent(context, MyService.class);          
        context.startService(serviceIntent);          
        Intent activityIntent = new Intent(context, MessageActivity.class);  
        //  要想在Service中启动Activity,必须设置如下标志  
        activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
        context.startActivity(activityIntent);  
    }  
} 

2、什么事AIDL服务

2.1 跨进程

Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。Activity和Broadcast都可以跨进程通信,除此之外,还可以使用Content Provider进行跨进程通信。现在我们已经了解了4个Android应用程序组件中的3个(Activity、Broadcast和Content Provider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。这就是本节要介绍的AIDL服务

2.2 什么是AIDL服务

前面的部分介绍了开发人员如何定制自己的服务,但这些服务并不能被其他的应用程序访问。为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务

2.3 AIDL的语法十分简单,与Java语言基本保持一致, AIDL支持的数据类型

  1. AIDL文件以 .aidl 为后缀名
  2. AIDL支持的数据类型分为如下几种:
    • 八种基本数据类型:byte、char、short、int、long、float、double、boolean
    • String,CharSequence
    • 实现了Parcelable接口的数据类型
    • List 类型。List承载的数据必须是AIDL支持的类型
    • Map类型。Map承载的数据必须是AIDL支持的类型

2.3建立AIDL服务的步骤

(1)在java文件目录上点击右键,新建AIDL -> AIDL File, 创建好以后系统会自动创建一个aidl目录,所有aidl文件都在这个目录中

我们新建了IMyService.aidl,里面有一个接口IMyService,重新编译代码,会自动生成一个IMyService.java文件,里面东西只关心IMyService.Stub这个类,这里面是我们封装的方法

(2)在Java目录中创建Service,并创建aidl文件里生成的IMyService.Stub实例,然后onBind返回(注意:这一步必须先重新编译代码,不然找不到接口)

(3)如果要加方法,就在IMyService.aidl文件中加,加完以后需要重新编译,这时候stub实例里的回调方法就会报错,提示你实现接口

(4)在AndroidManifest.xml文件中配置AIDL服务,android:exported=true,这个属性用于指示该服务是否能够被其他应用程序组件调用或跟它交互。如果设置为true,则能够被调用或交互,否则不能

<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true"/>

(5)启动Service

Intent it = new Intent(this, MyService.class);
startService(it);

在服务端工程中必须启动Service,否则客户端连不上

(6)编写客户端代码获取数据(首先要把aidl目录完全复制到客户端代码中,不需要任何变动)

当服务端添加了方法,必须把aidl目录全部重新复制过来

(7)客户端绑定服务

public class MainActivity extends AppCompatActivity {
    IMyService myService;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ServiceConnection serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                myService = IMyService.Stub.asInterface(service);
                try {
                    Toast.makeText(MainActivity.this, myService.getValue(), Toast.LENGTH_LONG).show();
                }
                catch (Exception e) {

                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
        };

        Intent it = new Intent();
        it.setComponent(new ComponentName("com.bank.myaapplication", "com.bank.myaapplication.MyService"));
        bindService(it, serviceConnection, Context.BIND_AUTO_CREATE);
    }
}

启动成功后就可以通过myService变量来调用服务端提供的方法。

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
进程间通信中,信号是一种非常重要的机制。它可以让一个进程向另一个进程发送一个异步通知,让接收进程执行特定的操作。 信号的基本概念是:当一个进程需要通知另一个进程发生了某个事件时,它可以向另一个进程发送一个信号。接收进程在接收到信号后,可以选择忽略该信号或者执行相应的处理程序。 信号可以由如下几种方式发送: 1. kill命令:可以使用kill命令向指定进程发送信号。 2. 信号函数:可以使用信号函数向指定进程发送信号。 3. 软件异常:当一个进程执行某些非法操作时,操作系统会向该进程发送一个信号。 常见的信号有: 1. SIGINT:中断进程。通常由Ctrl+C键触发。 2. SIGTERM:请求进程终止。通常由kill命令发送。 3. SIGKILL:强制进程终止。通常由kill命令发送。 4. SIGSTOP:暂停进程。通常由kill命令发送。 5. SIGCONT:恢复进程。通常由kill命令发送。 6. SIGUSR1和SIGUSR2:用户自定义信号。 在Linux系统中,可以使用kill命令向指定进程发送信号。例如: ``` kill -SIGINT pid ``` 这个命令会向指定pid的进程发送SIGINT信号,让该进程接收到信号后执行相应的处理程序。 在编写程序时,可以使用信号函数向指定进程发送信号。例如,下面的代码向指定进程发送SIGUSR1信号: ``` #include <signal.h> #include <unistd.h> int main() { int pid = 12345; // 指定进程的pid kill(pid, SIGUSR1); // 向指定进程发送SIGUSR1信号 return 0; } ``` 当接收进程接收到信号后,可以执行相应的处理程序。例如,下面的代码定义了一个信号处理程序,当接收到SIGUSR1信号时会执行该处理程序: ``` #include <signal.h> void handle_signal(int signum) { // 处理SIGUSR1信号 } int main() { signal(SIGUSR1, handle_signal); // 注册信号处理程序 while(1) {} // 循环等待信号 return 0; } ``` 上面的代码中,signal函数用于注册信号处理程序,当接收到SIGUSR1信号时会执行handle_signal函数。循环部分是为了等待信号的到来。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值