美文网首页androidAndroid Android知识
Android应用界面开发——Service与IntentSer

Android应用界面开发——Service与IntentSer

作者: trampcr | 来源:发表于2016-08-02 16:59 被阅读1405次

    Service是Android四大组件中与Activity最相似的组件,它们都代表可执行的程序,Service与Activity的区别是:Service一直在后台运行,它没有用户界面,所以绝不会到前台运行。如果某个程序组件需要在运行时向用户呈现某种界面,或者该程序需要与用户交互,就需要使用Activity;否则就应该考虑使用Service了,最后通过使用Service实现定时更换壁纸。

    定时更换壁纸效果图:

    Service简介


    Service是一个可长期在后台运行的应用组件,并且不提供用户界面。

    • Service不是一个单独的进程。
    • Service不是一个线程。

    创建、配置Service


    开发Service分为两步:

    • 定义一个继承Service的子类。
    • 在AndroidManifest.xml文件中配置该Service。

    补充:需要在AndroidManifest.xml中声明的有:

    • activity:活动
    • activity-alias:活动别名
    • service:服务
    • provider:内容提供者
    • receiver:广播接收者
    • meta-data:数据格式
    • uses-library:第三方类库

    Service中定义了一系列生命周期方法:

    • IBinder onBind(Intent intent):该方法是Service子类必须实现的方法。该方法返回一个IBinder对象,应用程序可通过该对象与Service组件通信。
    • void onCreate():在Service第一次被创建后立即回调该方法。
    • void onDestroy():在Service被关闭之前回调该方法。
    • void onStartCommand(Intent intent, int flags, int startId):该方法的早期版本是onStart(Intent intent, int startId),每次客户端调用startService(Intent)方法启动Service时都会回调该方法。
    • boolean onUnbind(Intent intent):当该Service上绑定的所有客户端都断开连接时将会回调该方法。

    定义一个Service组件如下:

    public class FirstService extends Service {
        
        private static final String TAG = FirstService.class.getSimpleName();
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @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");
        }
    }
    

    上面的Service只是重写了Service组件的onCreate()、onStartCommand()、onDestroy()、onBind()等方法,重写这些方法时只是打印了一个字符串。

    在Android系统中运行Service有两种方式:

    • 通过Context的startService()方法:通过该方法启动Service,访问者与Service之间没有关联,即使访问者退出了,Service也仍然运行。
    • 通过Context的bindService()方法:通过该方法启动Service,访问者与Service绑定在一起,访问者一旦退出,Service也就终止了。

    启动和停止Service——startService()方式启动


    使用Activity作为Service的访问者,该Activity中包含两个按钮,一个用于启动Service,一个用于关闭Service。代码如下:

    public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    
        private Button mStartService;
        private Button mStopService;
        private Intent intent;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mStartService = (Button) findViewById(R.id.start_service);
            mStopService = (Button) findViewById(R.id.stop_service);
            intent = new Intent(MainActivity.this, FirstService.class);
    
            mStartService.setOnClickListener(this);
            mStopService.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.start_service:
                    startService(intent);
                    break;
                case R.id.stop_service:
                    stopService(intent);
                    break;
            }
        }
    }
    

    启动、关闭Service十分简单,调用Context的startService()、stopService()方法即可启动、关闭Service。

    注意:Android5.0开始,Google要求必须使用显示的Intent启动Service组件。

    运行该程序,点击启动按钮启动Service,再点击停止按钮关闭Service,在Logcat面板可以看到如下输出:

    如果在不关闭Service的情况下,连续点击三次启动Service按钮,程序会连续启动三次Service,在Logcat面板可以看到如下输出:

    从上图可以看出,每当Service被创建时会回调onCreate()方法,每次Service被启动时都会回调onStartCommand()方法;多次启动一个已有的Service不会再回调onCreate()方法,但每次启动时都会回调onStartCommand()方法。

    绑定本地Service并与之通信——bindService()方式启动

    如果Service和访问者之间需要进行方法调用或者交换数据,则应该使用bindService()和unbindService()方法启动、关闭Service。

    Context的bindService()方法包含三个参数,分别如下:

    • service:该参数通过Intent指定要启动的Service。
    • conn:该参数是一个ServiceConnection对象,该对象用于监听访问者与Service之间的连接情况。当访问者与Service之间连接成功时回调该ServiceConnection对象的onServiceConnected(ComponentName name, IBinder service)方法;当Service所在的宿主进程由于异常中止或者其他原因终止,导致该Service与访问者之间断开连接时回调该ServiceConnection对象的onServiceDisconnected(ComponentName name)方法。
      • onServiceConnected(ComponentName name, IBinder service)方法中的IBinder即可实现与被绑定Service之间的通信。
    • flags:指定绑定时是否自动创建Service(如果Service还未创建)。该参数可指定为0(不自动创建)或者BIND_AUTO_CREATE(自动创建)。

    实际开发时通常会采用继承Binder(IBinder的实现类)的方式实现自己的IBinder对象。

    下面程序示范了如何在Activity中绑定Service,并获取Service的运行状态。该程序的Service类需要真正实现onBind()方法,并让该方法返回一个有效的IBinder对象。Service类代码如下:

    public class BindService extends Service {
    
        private static final String TAG = BindService.class.getSimpleName();
    
        private int count;
        private boolean quit;
    
        //定义onBinder方法所返回的对象
        private MyBinder binder = new MyBinder();
        //通过继承Binder实现IBinder类
        public class MyBinder extends Binder{
            public int getCount(){
                return count;
            }
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            Log.d(TAG, "onBind");
            return binder;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.d(TAG, "onCreate");
            //启动一条线程,动态修改count状态值
            new Thread(){
                @Override
                public void run() {
                    while (!quit){
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count++;
                    }
                }
            }.start();
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            Log.d(TAG, "onUnbind");
            return true;
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            this.quit = true;
            Log.d(TAG, "onDestroy");
        }
    }
    

    Service类实现了onBind()方法,该方法返回一个可访问该Service状态数据(count)的IBinder对象,该对象将被传给该Service的访问者。

    接下来定义一个Activity来绑定该Service,并在Activity中通过MyBinder对象访问Service的内部状态。该Activity包含三个按钮,分别为绑定Service、解绑Service、获取Service的运行状态。

    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        private static final String TAG = MainActivity.class.getSimpleName();
        private Button mBindService;
        private Button mUnbindService;
        private Button mGetServiceState;
        private Intent intent;
        //保持所启动的Service的IBinder对象
        BindService.MyBinder binder;
    
        private ServiceConnection conn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d(TAG, "onServiceConnected");
                binder = (BindService.MyBinder) service;//①
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mBindService = (Button) findViewById(R.id.bind_service);
            mUnbindService = (Button) findViewById(R.id.unbind_service);
            mGetServiceState = (Button) findViewById(R.id.get_service_state);
            intent = new Intent(MainActivity.this, BindService.class);
    
            mBindService.setOnClickListener(this);
            mUnbindService.setOnClickListener(this);
            mGetServiceState.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.bind_service:
                    bindService(intent, conn, BIND_AUTO_CREATE);
                    break;
                case R.id.unbind_service:
                    unbindService(conn);
                    break;
                case R.id.get_service_state:
                    Toast.makeText(MainActivity.this, "Service的count值为:" + binder.getCount(), Toast.LENGTH_SHORT).show();//②
                    break;
            }
        }
    }
    

    上面①号代码用于在Activity与Service连接成功时获取Service的onBind()方法返回的MyBinder对象,②号代码可以通过MyBinder对象访问Service的运行状态。

    点击绑定Service按钮,在Logcat面板可以看到如下输出:

    点击获取Service状态按钮,可以看到如下图所示的输出:

    点击解绑Service按钮,在Logcat面板可以看到如下输出:

    如上图所示,当程序调用unbindService()方法解除对某个Service的绑定时,系统会先调用该Service的onUnbind()方法,然后再回调onDestroy()方法。

    与多次调用startService()方法启动不同的是,多次调用bindService()方法并不会执行重复绑定。

    Service的生命周期


    随着应用程序启动Service方式不同,Service的生命周期也略有差异,如下图:

    如果应用程序通过startService()方法来启动Service,Service的生命周期如上图左半部分所示。如果应用程序通过bindService()方法来启动Service,Service的生命周期如上图右半部分所示。

    当Activity调用bindService()绑定一个已通过startService()启动的Service时,系统只是把Service内部IBinder对象传给Activity,并不会把该Service生命周期完全绑定到该Activity,因而当Activity调用UNBindService()方法取消与该Service的绑定时,也只是切断该Activity与Service之间的关联,并不能停止该Service组件。要停止该Service组件,还需调用stopService()方法。

    IntentService


    首先看一下Service本身存在的两个问题。

    • Service不会专门启动一个单独的进程,Service与它所在应用位于同一个进程中。
    • Service不是一个新的线程,因此不应该在Service中直接处理耗时的任务。

    IntentService正好弥补了Service的不足:IntentService会使用队列来管理请求Intent,每当客户端代码通过Intent请求启动IntentService时,IntentService会将该Intent加入队列,然后开启一条新的worker线程来处理该Intent。对于异步的startService()请求,IntentService会按次序依次处理队列中的Intent,该线程保证同一时刻只处理一个Intent。由于IntentService使用新的worker线程处理Intent请求,因此IntentService不会阻塞主线程,所以IntentService自己就可以处理耗时任务。

    IntentService的特征:

    • IntentService会创建单独的worker线程来处理所有的Intent请求。
    • IntentService会创建单独的worker线程来处理onHandleIntent()方法实现的代码,因此开发者无须处理多线程问题。
    • 当所有请求处理完成后,IntentService会自动停止,因此开发者无须调用stopSelf()方法来停止该Service。
    • 为Service的onBind()方法提供了默认实现,默认实现的onBind()方法返回null。
    • 为Service的onStartCommand()方法提供了默认实现,该实现会将请求Intent添加到队列中。

    扩展IntentService实现Service无须重写onBind()、onStartCommand()方法,只要重写onHandleIntent()方法即可。

    定时更换壁纸


    通过AlarmManager周期性调用某个Service,从而让系统实现定时更换壁纸的功能。

    该程序界面有两个按钮,一个用于启动定时更换壁纸,一个用于关闭定时更换壁纸,代码如下:

    public class MainActivity extends AppCompatActivity {
    
        private Button mStart;
        private Button mStop;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mStart = (Button) findViewById(R.id.start);
            mStop = (Button) findViewById(R.id.stop);
            //指定启动ChangeService组件
            Intent intent = new Intent(MainActivity.this, ChangeService.class);
            //创建PendingIntent对象
            final PendingIntent pi = PendingIntent.getService(MainActivity.this, 0, intent, 0);
    
            mStart.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //获取AlarmManager对象
                    AlarmManager alarmManager = (AlarmManager) getSystemService(Service.ALARM_SERVICE);
                    //设置每隔2秒执行pi所代表的组件一次
                    alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, 2000, pi);
                    mStart.setEnabled(false);
                    mStop.setEnabled(true);
                    Toast.makeText(MainActivity.this, "壁纸定时更换启动成功啦", Toast.LENGTH_SHORT).show();
                }
            });
    
            mStop.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mStart.setEnabled(true);
                    mStop.setEnabled(false);
                    //获取AlarmManager对象
                    AlarmManager alarmManager = (AlarmManager) getSystemService(Service.ALARM_SERVICE);
                    //取消对pi的调度
                    alarmManager.cancel(pi);
                }
            });
        }
    }
    

    上面程序代码指定程序每2秒执行一次pi所代表的组件。程序中pi代表了ChangeService组件,ChangeService代码如下:

    public class ChangeService extends Service {
    
        //定义定时更换的壁纸资源
        int[] wallpapers = new int[]{
                R.drawable.author,
                R.drawable.girl,
                R.drawable.life
        };
    
        //定义系统的壁纸管理服务
        WallpaperManager wallpaperManager;
        //定义当前所显示的壁纸
        int current = 0;
    
        @Override
        public void onCreate() {
            super.onCreate();
            //初始化WallPaperManager
            wallpaperManager = WallpaperManager.getInstance(this);
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            //如果到了最后一张,系统重新开始
            if (current >= 3) {
                current = 0;
            }
            try {
                //改变壁纸
                wallpaperManager.setResource(wallpapers[current++]);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return START_STICKY;
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
    

    重写Service的onStartCommand()方法,也就是每次启动该Service时都会执行onStartCommand()方法中的代码,更换壁纸的代码就放在该方法中。

    为了允许该程序改变壁纸,还需在AndroidManifest.xml中添加权限:

    <uses-permission android:name="android.permission.SET_WALLPAPER" />
    

    运行该程序,点击开始,返回桌面即可看到系统壁纸每2秒更换一次,效果图如下:

    相关文章

      网友评论

      本文标题:Android应用界面开发——Service与IntentSer

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