Android组件Service

一、Service的概述

Service是Android的四大组件之一,是一个能够在后台执行长时操作的应用程序组件。没有Activity一样的用户界面,不能与用户进行交互,不能自己启动,运行在后台。一个Service具有全局唯一性,同一时间在系统内只能存在该Service的一个实例。Service具有更高的进程优先级。

在android应用项目中,若将耗时的操作放在Activity的线程中(如播放音乐),将可能出现以下情况:因接电话等操作,使得应用程序成为后台进程(Activity被完全遮盖)或成为空进程(应用程序中的所有Activity都被销毁)。虽然这时进程中的线程仍然在工作,如在播放音乐,但是后台进程或空进程在系统内存匮乏时,容易被android系统彻底杀掉,这样线程也将停止。为避免这种问题出现,可以将应用程序中的耗时操作放在Service中进行,由于Service的进程优先级为第三级,将大大降低耗时操作被系统杀掉的几率。

注意:1. 一个service被启动后,实际上还是运行在主线程中,即UI线程。若用户要执行比较耗时的操作,要新建一个线程来处理;否则会使UI线程阻塞。2.Service停止后,在Service中运行的线程并没有结束,此时线程在空进程中运行。

二、Service的生命周期


1、startService方式启动Service

(1)服务的启动和停止:

在应用程序中调用Context #startService启动一个服务,停止服务调用Context #stopService,也可以在服务中调用stopSelf方法,服务会立即停止。

(2)生命周期

onCreate()

Service第一次启动时执行,只执行一次。可以进行初始化操作。

onStartCommand(Intent intent,int flags,finalint startId)

每次启动Service都会执行。在此可以实现服务提供的功能,例如,若该服务是在后台下载文件,则在该函数中启动一个新的线程(Thread),在线程中实现下载功能。

onDestory()

当服务没有被远程客户端绑定时,调用stopServiece(),或stopSelf时绘执行该方法。可以在这里进行释放资源操作。


2、bindService方式启动Service

(1)服务的启动和停止

在应用程序中调用Context #bindService绑定一个服务。

a.一个Activity与一个Service只能绑定一次,也就是说如果已经通过bindService进行了绑定,那么就不会再次绑定,即ServiceConnection的回调方法不会执行。

b.一个Activity可以绑定到多个Service,一个Service也可以同时被多个Activity绑定

c.在绑定Service时,如果将Flag设置为 BIND_AUTO_CREATE 时,如果该Service未创建,则先创建(onCreate()),然后绑定(onBind);如果Service已经创建,则直接绑定(onBind)。

解除服务调用Context #unbindService。

如果Service是通过bindService启动的,那么当unbindService时,Service会被销毁(如果Service被多个Activity绑定,只有当所有的客户端都解绑,该Service才会被销毁)。如果bindService时,Service已经启动了,那么unbindService时,Service不会被销毁。

(2)生命周期

onCreate()

如果Service未启动,Service第一次绑定时执行,只执行一次。如果Service已经通过startService启动,则不会执行。

onBind()

当客户端第一次绑定到Service时执行。一个service可以被多次绑定。onBind()返回一个IBinder对象,客户端调用bindService()绑定该服务时被调用。这个绑定是异步的,bindService()方法会立即返回,并且不给客户端返回IBinder对象。要接收IBinder对象,客户端必须创建一个实现ServiceConnection类,并创建ServiceConnection的实例;然后在,bindService()方法中调用ServiceConnection的实例。注意,onServiceDisconnected()回调,在从服务解绑时并不会调用,只有在服务崩溃时才会调用。如果调用了该函数,则应该认为服务没有连接,可能需要重新调用bindService().

onReBind()

如果onUnbind返回false,该方法不会被执行。如果onUnbind返回true,当已经有客户端正在绑定服务时,其他客户端绑定服务时,不会执行该方法,只有当所有客户端都解绑成功后,并且service实例还存在(没有执行onDestroy),此时有客户端再次绑定才会执行。

onUnbind()

当任意一个已经绑定的客户端第一次解绑时(调用unbindService),会调用onUnbind方法。之后当客户端解绑时是否会调用该方法,由第一次解绑时调用onUnbind方法的返回值决定。如果返回false,那么之后所有的客户端解绑都不会调用该方法,如果返回true,则当最后一个客户端从service解绑时执行该方法。

onDestoty()


三、本地服务

1、startService启动本地服务

一个Activity,包含2个按钮和1个进度条,2个按钮分别是开始按钮、结束按钮。点击“开始”按钮:进度条开始加载;“开始”变成“重启”按钮;显示“结束”按钮(默认情况,“结束”按钮是隐藏状态)。

BaseServiceTest包括了两个主要的类:

StartServiceImpl.java  ——  Service的子类。当服务被启动时,它会并新建一个线程,每隔200ms将一个数字+2,并通过广播发送出去。

StartServiceTest.java  ——  调用StartServiceImpl的Activity。


StartServiceImpl.java的内容如下

/**
 * @desc 服务:每隔200ms将一个数字+2并通过广播发送出去
 *
 */
public class StartServiceImpl extends Service {
    private static final String TAG = "StartServiceImpl";
    
    // 发送的广播对应的action
    private static final String COUNT_ACTION = "com.service.startservice.COUNT_ACTION";
    
    // 线程:用来实现每隔200ms发送广播
    private static CountThread mCountThread = null;
    // 数字的索引
    private static int index = 0;
    
    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.onCreate();
    }
    
    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");

        // 终止服务
        if ( mCountThread != null) {
            mCountThread.interrupt();
            mCountThread = null;
        }
        super.onDestroy();
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        
        // 非首次运行服务时,执行下面操作
        // 目的是将index设为0
        if ( mCountThread != null) {
            Log.d(TAG, "mCountThread != null");
            index = 0;
            return START_STICKY;
        }
        
        Log.d(TAG, "start thread");
        // 首次运行时,创建并启动线程
        mCountThread = new CountThread();
        mCountThread.start();
        
        return START_STICKY;
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return null;
    }
    
    private class CountThread extends Thread {
        @Override 
        public void run() {
            index = 0;
            try {
                while (true) {
                    // 将数字+2,
                    index += 2;                    
                    
                    // 将index通过广播发送出去
                    Intent intent = new Intent(COUNT_ACTION);
                    intent.putExtra("count", index);
                    sendBroadcast(intent);
//                    Log.d(TAG, "CountThread index:"+index);
                    
                    // 若数字>=100 则退出
                    if (index >= 100) {
                        if ( mCountThread != null) {
                            mCountThread = null;
                        }
                        return ;
                    }
                    
                    // 修改200ms
                    this.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
StartServiceTest.java的内容如下

public class StartServiceTest extends Activity {
    private static final String TAG = "StartServiceTest";

    private static final String COUNT_ACTION = "com.service.startservice.COUNT_ACTION";
    private CurrentReceiver mReceiver;
    private Button mStart ;
    private Button mStop ;
    private Intent mIntent ;
    private Intent mServiceIntent ; 
    private ProgressBar mProgressBar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.start_service_test);
        mStart = (Button) findViewById(R.id.start);
        mStart.setOnClickListener(new View.OnClickListener() {
            
            @Override
            public void onClick(View arg0) {
                Log.d(TAG, "click start button");
                // 显示“结束”按钮
                mStop.setVisibility(View.VISIBLE);
                // 将“开始”按钮更名为“重启”按钮
                mStart.setText(R.string.text_restart);
                // 启动服务,用来更新进度
                if (mServiceIntent == null) 
                    mServiceIntent = new Intent("com.service.StartService");
                startService(mServiceIntent);
            }
        });
        

        mStop = (Button) findViewById(R.id.stop);
        mStop.setOnClickListener(new View.OnClickListener() {
            
            @Override
            public void onClick(View view) {
                Log.d(TAG, "click stop button");
                if (mServiceIntent != null) {
                    // 结束服务。
                    stopService(mServiceIntent);
                    mServiceIntent = null;
                }
            }
        });
        mStop.setVisibility(View.INVISIBLE);
        
        mProgressBar = (ProgressBar) findViewById(R.id.pbar_def);
        // 隐藏进度条
        mProgressBar.setVisibility(View.INVISIBLE);
        
        // 动态注册监听COUNT_ACTION广播
        mReceiver = new CurrentReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(COUNT_ACTION);
        this.registerReceiver(mReceiver, filter);
    }

    @Override  
    public void onDestroy(){  
        super.onDestroy();  
  
        if(mIntent != null)
            stopService(mIntent);
        
        if(mReceiver != null)
            this.unregisterReceiver(mReceiver);
    }
    
    /**
     * @desc 更新进度条
     * @param index
     */
    private void updateProgressBar(int index) {
        int max = mProgressBar.getMax();

        if (index < max) {
            mProgressBar.setProgress(index);
            mProgressBar.setVisibility(View.VISIBLE);
        } else {
            // 隐藏进度条
            mProgressBar.setVisibility(View.INVISIBLE);
            // 隐藏“结束”按钮
            mStop.setVisibility(View.INVISIBLE);
            // 将“重启”按钮更名为“开始”按钮
            mStart.setText(R.string.text_start);
        }
//        Log.d(TAG, "progress : "+mProgressBar.getProgress()+" , max : "+max);
    }
    
    /**
     * @desc 广播:监听COUNT_ACTION,获取索引值,并根据索引值来更新进度条
     * @author skywang
     *
     */
    private class CurrentReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action  = intent.getAction();
            if (COUNT_ACTION.equals(action)) {
                int index = intent.getIntExtra("count", 0);
                updateProgressBar(index);
            }
        }
    }
}

2.bindService启动本地服务

BinderServiceImpl.java的内容

/**
 * @desc 服务:每隔200ms将一个数字+2并通过广播发送出去
 *
 */
public class BinderServiceImpl extends Service {
    private static final String TAG = "skywang-->BinderServiceImpl";
    
    // 发送的广播对应的action
    private static final String COUNT_ACTION = "com.service.binderservice.COUNT_ACTION";
    
    // 线程:用来实现每隔200ms发送广播
    private static CountThread mCountThread = null;
    // 数字的索引
    private static int index = 0;

    // 创建IBinder对象
    private final IBinder mBinder = new LocalBinder();
    
    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.onCreate();
    }
    
    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");

        // 终止服务
        if ( mCountThread != null) {
            mCountThread.interrupt();
            mCountThread = null;
        }
        super.onDestroy();
    }
    
    public void startCount() {
        Log.d(TAG, "startCount");
        
        // 非首次运行服务时,执行下面操作
        // 目的是将index设为0
        if ( mCountThread != null) {
            Log.d(TAG, "mCountThread != null");
            index = 0;
            return ;
        }
        
        Log.d(TAG, "start thread");
        // 首次运行时,创建并启动线程
        mCountThread = new CountThread();
        mCountThread.start();        
    }
    
    public void endCount() {
        // 终止服务
        if ( mCountThread != null) {
            mCountThread.interrupt();
            mCountThread = null;
        }
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return mBinder;
    }
    
    public class LocalBinder extends Binder {
        public BinderServiceImpl getService() {
            // Return this instance of LocalService so clients can call public methods
            return BinderServiceImpl.this;
        }
    }

    
    private class CountThread extends Thread {
        @Override 
        public void run() {
            index = 0;
            try {
                while (true) {
                    // 将数字+2,
                    index += 2;                    
                    
                    // 将index通过广播发送出去
                    Intent intent = new Intent(COUNT_ACTION);
                    intent.putExtra("count", index);
                    sendBroadcast(intent);
//                    Log.d(TAG, "CountThread index:"+index);
                    
                    // 若数字>=100 则退出
                    if (index >= 100) {
                        if ( mCountThread != null) {
                            mCountThread = null;
                        }
                        return ;
                    }
                    
                    // 修改200ms
                    this.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

BinderServiceTest.java的内容:

public class BinderServiceTest extends Activity {
    private static final String TAG = "BinderServiceTest";

    private static final String COUNT_ACTION = "com.service.binderservice.COUNT_ACTION";
    private CurrentReceiver mReceiver;
    private Button mStart;
    private Button mStop;
    private Intent mIntent;
    private Intent mServiceIntent; 
    private ProgressBar mProgressBar;
    
    private BinderServiceImpl mService;
    private boolean mBound = false;


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


        mStart = (Button) findViewById(R.id.start);
        mStart.setOnClickListener(new View.OnClickListener() {
            
            @Override
            public void onClick(View arg0) {
                Log.d(TAG, "click start button");
                // 显示“结束”按钮
                mStop.setVisibility(View.VISIBLE);
                // 将“开始”按钮更名为“重启”按钮
                mStart.setText(R.string.text_restart);
                // 启动计数
                mService.startCount();
            }
        });
        

        mStop = (Button) findViewById(R.id.stop);
        mStop.setOnClickListener(new View.OnClickListener() {
            
            @Override
            public void onClick(View view) {
                Log.d(TAG, "click stop button");
                // 结束计数
                mService.endCount();
            }
        });
        mStop.setVisibility(View.INVISIBLE);
        
        mProgressBar = (ProgressBar) findViewById(R.id.pbar_def);
        // 隐藏进度条
        mProgressBar.setVisibility(View.INVISIBLE);
        

        // 启动服务,用来更新进度
        mServiceIntent = new Intent("com.service.BinderService");        
        bindService(mServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
        
        // 动态注册监听COUNT_ACTION广播
        mReceiver = new CurrentReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(COUNT_ACTION);
        this.registerReceiver(mReceiver, filter);
    }

    @Override  
    public void onDestroy(){  
        super.onDestroy();  
  
        if(mIntent != null) {
            // 结束服务。
            unbindService(mConnection);
            mServiceIntent = null;
            mBound = false;
        }
        
        if(mReceiver != null)
            this.unregisterReceiver(mReceiver);
    }
    
    private ServiceConnection mConnection = new ServiceConnection() {

        /**
         * 绑定服务成功的回调函数
         */
        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            Log.d(TAG, "onServiceConnected");
            // 获取IBinder服务对象
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        /**
         * 解除绑定的回调函数
         */
        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            Log.d(TAG, "onServiceDisconnected");
            mBound = false;
        }
    };    
    
    /**
     * @desc 更新进度条
     * @param index
     */
    private void updateProgressBar(int index) {
        int max = mProgressBar.getMax();

        if (index < max) {
            mProgressBar.setProgress(index);
            mProgressBar.setVisibility(View.VISIBLE);
        } else {
            // 隐藏进度条
            mProgressBar.setVisibility(View.INVISIBLE);
            // 隐藏“结束”按钮
            mStop.setVisibility(View.INVISIBLE);
            // 将“重启”按钮更名为“开始”按钮
            mStart.setText(R.string.text_start);
        }
//        Log.d(TAG, "progress : "+mProgressBar.getProgress()+" , max : "+max);
    }
    
    /**
     * @desc 广播:监听COUNT_ACTION,获取索引值,并根据索引值来更新进度条
     *
     */
    private class CurrentReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action  = intent.getAction();
            if (COUNT_ACTION.equals(action)) {
                int index = intent.getIntExtra("count", 0);
                updateProgressBar(index);
            }
        }
    }
}

四、AIDL服务(由其他进程通过IPC使用的服务)

AIDL(Android Interface Definition Language)  IPC机制(interprocess communication)是面向对象的,轻量级的。通过AIDL定义的接口可以实现服务器端与客户端的IPC通信。在Android上,一个进程不能简单的像访问本进程内存一样访问其他进程的内存。所以,进程间想要对话,需要将对象拆解为操作系统可以理解的基本数据单元,并且有序的通过进程边界,这样底层操作系统才能跨应用程序边界整合它们。通过代码来实现这个数据传输过程是冗长乏味的,所幸的是android提供了AIDL工具来帮我们完成了此项工作。

1、定义AIDL接口

AIDL接口使用后缀名位.aidl的文件来定义,.aidl文件使用java语法编写,并且将该.aidl文件保存在 src/目录下(无论是服务端还是客户端都得保存同样的一份拷贝,也就是说只要是需要使用到该AIDL接口的应用程序都得在其src目录下拥有一份.aidl文件的拷贝)。

编译时,Android sdk 工具将会为 src/目录下的.aidl文件在 gen/ 目录下产生一个IBinder接口。服务端必须相应的实现该IBinder接口。客户端可以绑定该服务、调用其中的方法实现IPC通信。


创建一个用AIDL实现的服务端,需要以下几个步骤: 
    (1)创建.aidl文件:

        该文件(.aidl)定义了客户端可用的方法和数据的接口

    (2)实现这个接口:

        Android SDK将会根据你的.aidl文件产生AIDL接口。生成的接口包含一个名为Stub的抽象内部类,该类声明了所有.aidl中描述的方法,你必须在代码里继承该Stub类并且实现.aidl中定义的方法。Stub类是Binder的子类,同时也是IplayControllor的实现类。

public static abstract class Stub extends android.os.Binder 
				implements com.tarena.aidl.IPlayControllor
{
	public static com.test.aidl.IPlayControlor asInterface(android.os.IBinder obj)
}

    (3)向客户端公开服务端的接口:

        实现一个Service,并且在onBinder方法中返回第2步中实现的那个Stub类的子类(实现类)。

IplayControllor.aidl

package com.test.aidl;
interface IPlayControllor{
	void play();
	void pause();
	void next();
	void previous();
	void seekTo(int seekTo);
}

2、服务端实现AIDL接口

在服务端里的服务程序里实现接口。这样,IBinder就是一个Stub类得对象,该对象为service提供了IPC接口,并将会向客户端公开,这样客户端就可以通过该对象与该service进行交互了。现在,如果客户端(比如一个Activity)调用bindService()来连接该服务端,客户端的onServiceConnected()回调函数将会获得从服务端的onBind()返回的IBinder对象

/**服务端*/
public class MyService extends Service {
//充当远程服务的实现,onBind方法要返回此类的实例
	public class MyBinder extends IPlayControllor.Stub {
		@Override
		public void next() throws RemoteException {
			Log.i("info", "播放下一首");
		}
		@Override
		public void pause() throws RemoteException {
			Log.i("info", "暂停播放");
		}
		@Override
		public void play() throws RemoteException {
			Log.i("info", "播放");
		}
		@Override
		public void previous() throws RemoteException {
			Log.i("info", "播放上一首");
		}
		@Override
		public void seekTo(int seekTo) throws RemoteException {
			Log.i("info", "跳转:" + seekTo);
		}
	}
	@Override
	public void onCreate() {
		super.onCreate();
		Log.i("info", "Myservice.onCreate");
	}
	@Override
	public IBinder onBind(Intent intent) {
		Log.i("info", "Myservice.onBind");
		return new MyBinder();
	}
	@Override
	public boolean onUnbind(Intent intent) {
		Log.i("info", "Myservice.onUnbind");
		return super.onUnbind(intent);
	}
	@Override
	public void onDestroy() {
		super.onDestroy();
		Log.i("info", "Myservice.onDestroy");
	}
}
3.客户端应用程序调用服务
客户端同样得访问该接口类,所以,如果服务端和客户端不在同一进程(应用程序)中,那么客户端也必须在 src/ 目录下拥有和服务端同样的一份.aidl文件的拷贝(同样是指,包名、类名、内容完全一模一样),客户端将会通过这个.aidl文件生成 android.os.Binder接口——以此来实现客户端访问AIDL中的方法。 当客户端在onServiceConnected()回调方法中获得IBinder对象后,必须通过调用
IPlayControllor.Stub.asInterface(binder)将其转化成为IPlayControllor类型。

/**客户端*/
public class MainActivity extends Activity {
	private ServiceConnection conn = new ServiceConnection() {
		@Override
public void onServiceDisconnected(ComponentName name) {
		}
		@Override
		public void onServiceConnected(ComponentName name, IBinder binder) {

			//因为Stub类继承了Binder类,又是IPlayController的实现类
			controllor = IPlayControllor.Stub.asInterface(binder);
		}
	};
	private IPlayControllor controllor;
	public void doClick(View v) {
		try {
			switch (v.getId()) {
			case R.id.btnPlay:
				controllor.play();
				break;
			case R.id.btnPause:
				controllor.pause();
				break;
			case R.id.btnPrevious:
				controllor.previous();
				break;
			case R.id.btnNext:
				controllor.next();
				break;
			case R.id.btnSeekTo:
				controllor.seekTo(10000);
				break;
			}
		} catch (RemoteException e) {

			e.printStackTrace();
		}
	}

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		//执行绑定操作
		Intent intent = new Intent("com.tarena.action.MY_SERVICE");
bindService(intent, conn, BIND_AUTO_CREATE);
	}

	@Override
	protected void onDestroy() {

		super.onDestroy();
		//解除绑定
		unbindService(conn);
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值