Android开发之远程服务[Remote:]通信

在Android项目的实际开发中,或多或少的都要用到Service,比如监测APP升级、即时消息通知以及一些需要持久运行或耗时的操作,作为Android四大组件之一,Service的用法是必要掌握的,Service分为本地服务和远程服务,又可分为前台服务和后台服务,在service中,本地服务(LocalService)为普通服务,与客户端捆绑在一起,主进程停止后服务也会终止,无法独立存活,不过它节省资源,通信方便,通常可用于监测APP升级、音乐播放等;而远程服务(RemoteService)是一个独立的进程,可以独立存活,亦可以被其他不同包名的APP所共同调用,当然啦,由于是独立进程,相对来说更耗费资源,通讯也相对麻烦,既然是一个独立的进程,那么进程之间通信肯定就会用到AIDL。

今天探讨一下关于远程服务的实现及与客户端之间的通讯。

生命周期

先看一张图
Android开发之远程服务[Remote:]通信_第1张图片

这张图把生命周期分为两种情况,即开启服务startService()和绑定服务bindService(),startService()开启服务之后需要通过stopService进行停止,bindService()绑定服务之后则需要通过onUnbind()进行解绑停止,其实即便是通过开启服务的方式启动远程服务,也是可以随时被允许绑定,即startService()之后依然可以执行bindService(),那么交叉式生命周期见下图
Android开发之远程服务[Remote:]通信_第2张图片

实例讲解

远程服务如果想跟客户端通信,由于不在同一进程,就必须要用到Android接口定义语言,即AIDL(Android Interface Definition Language),关于AIDL的内容,网上很多详细讲解,我就不班门弄斧了。
首先创建一个.aidl文件,AndroidStudio可以直接创建,eclipse的话,创建一个File,后缀加上“.aidl”就可以了
Android开发之远程服务[Remote:]通信_第3张图片
这个文件里根据自身需要实现用于通讯的接口

package com.demo.service;

interface IRemoteService{

   void setName(String name); 

   String getName(); 

}

然后创建一个服务类RemoteService,继承于Service,在这其中创建一个嵌套类ServiceImpl ,用于通信过程中的逻辑操作,与aidl中的接口对应,继承于Sub

class ServiceImpl extends Stub {
        private String _name;

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

        @Override
        public String getName() throws RemoteException {
            return _name;
        }
    }

接着将ServiceImpl做一个简单的单例模式

// 将ServiceImpl做一个简单的单例模式
        private ServiceImpl getInstance() {
            if (serviceImpl == null) {
                serviceImpl = new ServiceImpl();
            }
            return serviceImpl;
        }

这样的话,就可以在生命周期 onBind()方法中将这个单例模式返回

@Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return getInstance();
    }

RemoteService服务类中还需要重写onCreate()、onStartCommand()、onStart()、onUnbind()、onDestroy()方法,下面直接上完整代码

package com.demo.service;

import com.demo.service.IRemoteService.Stub;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class RemoteService extends Service {

    private ServiceImpl serviceImpl;
    private static final String TAG = "ForegroundService";
    private static final int NOTIFICATION_ID = 1; // 如果id设置为0,会导致不能设置为前台service

    class ServiceImpl extends Stub {
        private String _name;

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

        @Override
        public String getName() throws RemoteException {
            return _name;
        }
    }

    // 将ServiceImpl做一个简单的单例模式
        private ServiceImpl getInstance() {
            if (serviceImpl == null) {
                serviceImpl = new ServiceImpl();
            }
            return serviceImpl;
        }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return getInstance();
    }
    @SuppressLint("NewApi")
    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
        Notification.Builder builder = new Notification.Builder(this);
        builder.setSmallIcon(R.drawable.ic_launcher);
        builder.setTicker("服务启动");
        builder.setContentTitle(getString(R.string.app_name));
        builder.setContentText("运行中");
        Notification notification = builder.build();
        notification.largeIcon = BitmapFactory.decodeResource(
                this.getResources(), R.drawable.ic_launcher);
        startForeground(NOTIFICATION_ID, notification);

    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        // TODO Auto-generated method stub
        super.onStart(intent, startId);
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        Log.d(TAG, "onDestroy");
        stopForeground(true);
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // TODO Auto-generated method stub
        return super.onUnbind(intent);
    }
}

代码很简单,如果不需要实现前台服务,onCreate()方法中的代码完全可以省略掉(该onCreate()只在首次服务运行时执行一次,重复启动或绑定服务不再执行),当然,前台服务会更小几率的被系统自动回收机制kill掉,onCreate()中的那堆代码是创建一个Notification对象,将服务的运行状态显示在系统下拉列表项中
Android开发之远程服务[Remote:]通信_第4张图片

基本的远程服务端基本可以了,现在看客户端
Android开发之远程服务[Remote:]通信_第5张图片
有开启服务按钮 调用startService(),绑定服务按钮 调用bindService(),停止服务按钮 调用stopService()
startService()是单纯的开启服务,无法通过aidl进行进程间的通信,只有bindService()之后才可以
要想实现通信,需要在客户端实例化一个ServiceConnection对象,然后在它的回调方法onServiceConnected中得到IRemoteService(aidl接口),通过它,就可以在绑定服务之后调用aidl中声明的方法

private ServiceConnection sc = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            irservice = IRemoteService.Stub.asInterface(service);

        }
    };

绑定服务

// 绑定服务
            Intent intentBind = new Intent(MainActivity.this,
                    RemoteService.class);

            bindService(intentBind, sc, Service.BIND_AUTO_CREATE);

完整代码

package com.demo.service;

import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {
    EditText editName;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editName = (EditText) findViewById(R.id.editName);
        findViewById(R.id.btnStart).setOnClickListener(this);
        findViewById(R.id.btnBind).setOnClickListener(this);
        findViewById(R.id.btnStop).setOnClickListener(this);
        findViewById(R.id.btnSet).setOnClickListener(this);
        findViewById(R.id.btnGet).setOnClickListener(this);
    }

    IRemoteService irservice = null;
    private ServiceConnection sc = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            irservice = IRemoteService.Stub.asInterface(service);

        }
    };

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch (v.getId()) {
        case R.id.btnStart:
            // 开启服务
            Intent intentStart = new Intent(MainActivity.this,
                    RemoteService.class);
            startService(intentStart);
            break;
        case R.id.btnBind:
            // 绑定服务
            Intent intentBind = new Intent(MainActivity.this,
                    RemoteService.class);

            bindService(intentBind, sc, Service.BIND_AUTO_CREATE);
            break;
        case R.id.btnSet:
            //设置姓名
            try {
                irservice.setName(editName.getText().toString());
            } catch (RemoteException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            break;
        case R.id.btnGet:
            //获取姓名
            try {
                Toast.makeText(MainActivity.this, irservice.getName(), 0)
                        .show();
            } catch (RemoteException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            break;
        case R.id.btnStop:
            // 停止服务
            Intent intentStop = new Intent(MainActivity.this,
                    RemoteService.class);
            stopService(intentStop);
            break;
        default:
            break;
        }
    }
}

如果仅开启服务,无法建立通信,如果仅绑定服务,那么在客户端销毁时远程服务也随之销毁,开启并绑定服务,则可以保持进程通信和持久存活。
逻辑很简单,就不上源码了,有需要的话留邮箱

忘了一事儿,还需要在AndroidManifest.xml中把远程服务注册一下

 <service
            android:name="com.demo.service.RemoteService"
            android:enabled="true"
            android:exported="false"
            android:process=":remote" >
            <intent-filter>
                <action android:name="com.demo.service.REMOTE_SREVICE" />
            intent-filter>
        service>

简单说一下,android:process=":remote"是将服务设置为远程服务,android:exported="false"是是否允许其他application调用此服务

你可能感兴趣的:(Android小笔记)