Android Programming: Pushing the Limits -- Chapter 6: Services and Background Tasks

什么时候使用Service
服务类型
开启服务
后台运行
服务通信
附加资源

什么时候使用Service:

@、任何与用户界面无关的操作,可移到后台线程,然后由一个Service来控制这个线程。

服务类型:

@、First is the one that performs work for the application independent of the user’s input.

如:后台执行的音乐播放器。

@、The other type of Service is one that’s directly triggered by an action from the user.

如:照片分享应用的拍照上传服务。 完成后系统自动终止服务。

@、Service的生命周期:

1、  对于Service来说,总是会被执行的两个回调方法:onCreate和onDestroy。

2、  在onCreate中完成大部分初始化工作,在onDestroy中完成大部分清理工作。

3、  由于onCreate,onDestroy都是在主线程执行,应此要把长时间运行的操作移到后台线程中。

开启服务:

@、Context.startService:

1、  如果需要提供给外部调用,则需要配置intent-filter。

2、  调用Context.startService会触发onStartCommand,系统根据onStartCommand的返回值决定服务被关闭后是否重启。

3、  START_STICKY:系统因某些原因,如内存不足,关闭服务,如果服务的onStartCommand返回START_STICKY,则系统会重启服务,不过参数Intent传入的是null。这种情况,可能需要在onDestroy中对数据进行保存。(音乐播放器)

4、  START_NOT_STICKY:服务被系统关闭后,不会被重启。(上传服务)

5、  START_REDELIVER_INTENT:类似START_STICKY,但是参数Intent传入的是初始的Intent。

6、  可通过BroadcastReceiver方式将结果回馈给Activity。

7、  此方式需要通过调用Service.stopSelf()或者Context.stopService()来停止服务。

8、  实例,演示Service.stopSelf()

public class MyMusicPlayer extends Service implements MediaPlayer.OnCompletionListener{
    public static final String ACTION_ADD_TO_QUEUE =
            "com.example.lsp.myactivity.ADD_TO_QUEUE";
    private ConcurrentLinkedQueue<Uri> mTrackQueue;
    private MediaPlayer mMediaPlayer;

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

    @Override
    public void onCreate() {
        super.onCreate();
        mTrackQueue = new ConcurrentLinkedQueue<Uri>();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String action = intent.getAction();
        if(ACTION_ADD_TO_QUEUE.equals(action)){
            Uri trackUri = intent.getData();
            addTrackToQueue(trackUri);
        }
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if(mMediaPlayer != null){
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    /**
     *  Add track to end of queue if already palying,
     *  otherwise create a new MediaPlayer and start playing.
     */
    private synchronized void addTrackToQueue(Uri trackUri){
        if(mMediaPlayer == null){
            try{
                mMediaPlayer = MediaPlayer.create(this, trackUri);
                mMediaPlayer.setOnCompletionListener(this);
                mMediaPlayer.prepare();
                mMediaPlayer.start();
            }catch (IOException e){
                stopSelf();
            }
        }else{
            mTrackQueue.offer(trackUri);
        }
    }

    // Track completed, start playing next or stop service...
    @Override
    public void onCompletion(MediaPlayer mp) {
        mMediaPlayer.reset();
        Uri nextTrackUri = mTrackQueue.poll();
        if(nextTrackUri != null){
            try{
                mMediaPlayer.setDataSource(this, nextTrackUri);
                mMediaPlayer.prepare();
                mMediaPlayer.start();
            }catch (IOException e){
                stopSelf();
            }
        }else{
            stopSelf();
        }
    }
}

MyMusicPlayer.java

@、Context.bindService:

1、  此方法用于同一应用内组件拥有Service对象,然后通过Service对象直接访问Service方法。这种方式叫local binder。

2、  如果是跨应用调用Service,则需要AIDL。

3、  任何不属于当前前台运行的应用的Service都有可能被系统终止掉(为了free RAM)。想要在用户退出应用后,Service能够进行运行,可调用Service.startForeground()方法。

4、  当最后一个client断开连接(Context.unbindService()),服务将自动停止,除非在对后一个client断开连接后调用Service.startForeground()来保持Service alive。这也是为什么正确地调用Service.stopForeground()如此重要。

后台运行:

@、IntentService:

1、  IntentService通过Handler的方式来实现后台运行。

2、  自定义Service继承IntentService,实现onHandleIntent方法:根据入参Intent的action(intent.getAction()),实现多action的切换。

3、  多次调用时,以队列的方式处理,即onHandleIntent一次只处理一个intent。

4、  实例:

public class MyIntentService extends IntentService {
    private static final String NAME = MyIntentService.class.getSimpleName();
    public static final String ACTION_UPLOAD_PHOTO =
            "com.example.lsp.myactivity.UPLOAD_PHOTO";
    public static final String EXTRA_PHOTO = "bitmapPhoto";
    public static final String ACTION_SEND_MASSAGE =
            "com.example.lsp.myactivity.SEND_MESSAGE";
    public static final String EXTRA_MESSAGE = "messageText";
    public static final String EXTRA_RECIPIENT = "messageRecipient";

    public MyIntentService(){
        super(NAME);
        // We don‘t want intents redelivered in case we‘re shut down unexpectedly
        setIntentRedelivery(false);
    }

    /**
     * This methos is executed on its own thread, one intent at a time...
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        String action = intent.getAction();

        if(ACTION_SEND_MASSAGE.equals(action)){
            String messageText = intent.getStringExtra(EXTRA_MESSAGE);
            String messageRecipient = intent.getStringExtra(EXTRA_RECIPIENT);
            sendMessage(messageRecipient, messageText);
        }else if(ACTION_UPLOAD_PHOTO.equals(action)){
            Bitmap photo = intent.getParcelableExtra(EXTRA_PHOTO);
            uploadPhoto(photo);
        }
    }

    private void sendMessage(String messageRecipient, String messageText){
        // TODO Make network call...

        // TODO Send a broadcast that operation is completed
    }

    private void uploadPhoto(Bitmap photo){
        // TODO Make network call...

        // TODO Send a broadcast that operation is completed
        //sendBroadcast(new Intent(BROADCAST_UPLOAD_COMPLETED));
    }
}

MyIntentService.java

@、ExecutorService:

1、  实现操作并行执行。

2、  自定义服务类,继承Service类。

3、  添加ExecutorService对象作为成员变量。

4、  在自定义服务类中创建私有类,并实现Runnable接口,由此类来完成具体的功能操作。

5、  在onStartCommand()方法中通过ExecutorSerive的execute方法执行操作。

6、  实例:

public class MediaTranscoder extends Service {
    private static final int NOTIFICATION_ID = 1001;
    public static final String ACTION_TRANSCODE_MEDIA =
            "com.example.lsp.myactivity.TRANSCODE_MEDIA";
    public static final String EXTRA_OUTPUT_TYPE = "outputType";
    private ExecutorService mExecutorService;
    private int mRunningJobs = 0;
    private final Object mLock = new Object();
    private boolean mIsForeground = false;

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

    @Override
    public void onCreate() {
        super.onCreate();
        mExecutorService = Executors.newCachedThreadPool();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String action = intent.getAction();
        if(ACTION_TRANSCODE_MEDIA.equals(action)){
            String outputType = intent.getStringExtra(EXTRA_OUTPUT_TYPE);

            // Start new job and increase the running job counter
            synchronized(mLock){
                TranscodeRunnable transcodeRunnable =
                        new TranscodeRunnable(intent.getData(), outputType);
                mExecutorService.execute(transcodeRunnable);
                mRunningJobs++;
                startForegroundIfNeeded();
            }
        }
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mExecutorService.shutdownNow();
        synchronized (mLock){
            mRunningJobs = 0;
            stopForegroundIfAllDone();
        }
    }

    public void startForegroundIfNeeded(){
        if(!mIsForeground){
            Notification notification = buildNotificatin();
            startForeground(NOTIFICATION_ID, notification);
            mIsForeground = true;
        }
    }

    private Notification buildNotificatin(){
        Notification notification = null;
        // TODO Build the notification here
        return notification;
    }

    private void stopForegroundIfAllDone(){
        if(mRunningJobs == 0 && mIsForeground){
            stopForeground(true);
            mIsForeground = false;
        }
    }

    private class TranscodeRunnable implements Runnable{
        private Uri mInData;
        private String mOutputType;

        private TranscodeRunnable(Uri inData, String outputType){
            mInData = inData;
            mOutputType = outputType;
        }

        @Override
        public void run() {
            // TODO Perform transcoding here...

            //Decrease counter when we‘re done..
            synchronized (mLock){
                mRunningJobs--;
                stopForegroundIfAllDone();
            }
        }
    }
}

MediaTranscoder.java

服务通信:

@、使用BroadcastReceiver的方式

@、在bindService()模式的基础上,增加回调接口来实现通信

1、实例:

MyLocalService.java

MyActivity.java

附加资源:

Google’s changes to the Service API at

http://android-developers.blogspot.se/2010/02/service-api-changes-starting-with.html

Dianne Hackborn at

http://android-developers.blogspot.se/2010/04/multitaskingandroid-way.html

时间: 2024-10-11 11:54:38

Android Programming: Pushing the Limits -- Chapter 6: Services and Background Tasks的相关文章

Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- Messenger

Messenger类实际是对Aidl方式的一层封装.本文只是对如何在Service中使用Messenger类实现与客户端的通信进行讲解,对Messenger的底层不做说明.阅读Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- AIDL了解如何使用Aidl的方式实现服务端与客户端的通信. 在Service中使用Messenger,大部分代码还是跟Android的消息机制打交道,具体一点就是跟Handler,Mes

Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- ApiWrapper

前面两片文章讲解了通过AIDL和Messenger两种方式实现Android IPC.而本文所讲的并不是第三种IPC方式,而是对前面两种方式进行封装,这样我们就不用直接把Aidl文件,java文件拷贝到客户端了,而是为客户端提供一个aar(Anroid Archive)包.通过这个aar包对AIDL或者Messenger进行封装,最终客户端就可以像调用一般的java类一样,而不用处理通信方面的内容.(实际上书中说是要打包成jar包,但是在新建的Java Library Module中,我没能成功

Android Programming: Pushing the Limits -- Chapter 3: Components, Manifests, and Resources

Android Components Manifest文件 Resource and Assets v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} Normal 0 false 7.8 磅 0 2 false false false EN-US ZH-CN X-N

Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- AIDL

服务端: 最终项目结构: 这个项目中,我们将用到自定义类CustomData作为服务端与客户端传递的数据. Step 1:创建CustomData类 package com.ldb.android.example.aidl; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; import java.util.ArrayList; import java.util.Date; impor

Android Programming: Pushing the Limits -- Chapter 2: Efficient Java Code for Android

Android's Dalvik Java 与 Java SE 进行比较 Java代码优化 内存管理与分配 Android的多线程操作 Android’s Dalvik Java 与 Java SE 进行比较: @.Dalvik虚拟机是register-based machine:Java SE虚拟机是stack machine. @.从Android 2.2 版本开始引进JIT(Just In Time)编译器,之前是纯解释器. @.Android SDK 使用dx这个工具把Java SE s

Android Programming: Pushing the Limits -- Chapter 1: Fine-Tuning Your Development Environment

ADB命令 Application Exerciser Monkey Gradle ProGuard 代码重用 版本控制 静态代码分析 代码重构 开发者模式   ADB命令: @.adb help:查看adb命令. @.adb devices:列出所有连接的安卓设备和模拟器. @.adb push <local> <remote> 把计算机里的文件拷贝到设备中. adb push e:\test.xml /sdcard/files.ldb/ @.adb pull <remot

Android Programming: Pushing the Limits -- Chapter 4: Android User Experience and Interface Design

User Stories Android UI Design 附加资源 User Stories: @.通过写故事来设计应用. @.每个故事只关注一件事. @.不同的故事可能使用相同的组件,因此尽早地对故事进行分类. @.把目标用户构想到故事里,描述他们的基本特征,会在什么时候.什么地点使用该应用等信息,因此来确定故事的优先级. Android UI Design: @.构思应用需要展示的界面及内容,不需要详细的界面设计. @.确定各界面的跳转关系. @.用户界面原型设计,可通过工具进行,比如A

Android Programming: Pushing the Limits -- Chapter 5: Android User Interface Operations

多屏幕 自定义View 多屏幕 @.Android 4.2 开始支持多屏幕. @.举例: public class SecondDisplayDemo extends Activity { private Presentation mPresentation; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.lay

[iOS翻译]《iOS 7 Programming Pushing the Limits》系列:你可能不知道的Objective-C技巧

简介: 如果你阅读这本书,你可能已经牢牢掌握iOS开发的基础,但这里有一些小特点和实践是许多开发者并不熟悉的,甚至有数年经验的开发者也是.在这一章里,你会学到一些很重要的开发技巧,但这仍远远不够,你还需要积累更多的实践来让你的代码更强力. /* 本文翻译自<iOS 7 Programming Pushing the Limits>一书的第三章“You May Not Know”,想体会原文精髓的朋友请支持原书正版. ——————(博客园.新浪微博)葛布林大帝 */ 目录: 一. 最好的命名实践