• Android 4 学习(19):Services


    参考《Professional Android 4 Development

     

    Services

    Serviceinvisible的,因此其优先级不高于visibleActivity,之所以说不高于,是因为我们可以设置Service为在前台运行。

    创建Service

    Android提供了Service抽象类,继承它便可以创建一个Service类:

     

    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    public class MyService extends Service {
      @Override
      public void onCreate() {
        super.onCreate();
        // TODO: Actions to perform when service is created.
      }
      @Override
      public IBinder onBind(Intent intent) {
        // TODO: Replace with service binding implementation.
        return null;
      }
    }

     

    创建Service类之后,还需要在Manifest里面注册:

     

    <service android:enabled=”true” android:name=”.MyService” android:permission=”com.paad.MY_SERVICE_PERMISSION”/>

     

    若要默认只有自己的程序可以使用这个Service,需要添加Android:permission属性。其他Application若要使用这个服务,需要加入这个permission

    执行Service

    ServicestartService()方法调用时,即可引起onStartCommand方法的调用,因此需要重写Service中的onStartCommand方法,并且onStartCommand方法可能会被多次调用。onStartCommand()方法将返回一个int值,用于指定当Serviceruntime杀掉又重启的时,系统该如何响应:

     

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
      startBackgroundTask(intent, startId);
      return Service.START_STICKY;
    }

     

    重启Service

    onStartCommand()方法可以返回这些参数,它们的意义是:

    START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。

    START_NOT_STICKY:“非粘性的”。如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。
    START_REDELIVER_INTENT:使用这个返回值时,如果未执行完onStartCommand,服务在调用stopSelf之前被kill掉,系统会自动重启该服务,并将Intent的值传入。

    参考:http://www.krislq.com/2013/05/android-class-return-value-of-onstartcommand/

    启动和停止服务

    调用startService方法可以启动服务:

     

    private void explicitStart() {
      // Explicitly start My Service
      Intent intent = new Intent(this, MyService.class);
      // TODO Add extras if required.
      startService(intent);
    }
    private void implicitStart() {
      // Implicitly start a music Service
      Intent intent = new Intent(MyMusicService.PLAY_ALBUM);
      intent.putExtra(MyMusicService.ALBUM_NAME_EXTRA, “United”);
      intent.putExtra(MyMusicService.ARTIST_NAME_EXTRA, “Pheonix”);
      startService(intent);
    }

     

    调用stopService方法可以停止服务:

     

    // Stop a service explicitly.
    stopService(new Intent(this, MyService.class));
    // Stop a service implicitly.
    Intent intent = new Intent(MyMusicService.PLAY_ALBUM);
    stopService(intent);

     

    服务自杀

    服务内部调用stopSelf方法,可以停止该服务。

    绑定ServiceActivity

    首先,Service要实现IBind接口:

     

    @Override
    public IBinder onBind(Intent intent) {
      return binder;
    }
    public class MyBinder extends Binder {
      MyMusicService getService() {
        return MyMusicService.this;
      }
    }
    private final IBinder binder = new MyBinder();

     

    ServiceConnection类用于表示ServiceActivity的绑定,每个绑定都需要创建一个ServiceConnection

     

    // Reference to the service
    private MyMusicService serviceRef;
    // Handles the connection between the service and activity
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
          // Called when the connection is made.
          serviceRef = ((MyMusicService.MyBinder)service).getService();
        }
        public void onServiceDisconnected(ComponentName className) {
          // Received when the service unexpectedly disconnects.
          serviceRef = null;
        }
    };

     

    最后,调用bindService方法,传入用于启动ServiceIntentServiceConnection和标志位:

     

    // Bind to the service
    Intent bindIntent = new Intent(MyActivity.this, MyMusicService.class);
    bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);

     

    通常情况下,Android应用在自己的内存空间中运行,并不共享内存。若要与其他进程通信,可以使用广播,或在Intent中添加Bundle参数启动其他Service的方法。如果需要更紧密的通信,可以使用Android Interface Defi nition Language(AIDL)。AIDL使用OS级别的简单变量定义了接口,可以跨应用传递对象。

     

    创建前台服务

     

    使用startForeground方法启动服务,可以使服务获得与Visible Activity相同的优先级,例如:

     

    private void startPlayback(String album, String artist) {
      int NOTIFICATION_ID = 1;
      // Create an Intent that will open the main Activity if the notification is clicked.
      Intent intent = new Intent(this, MyActivity.class);
      PendingIntent pi = PendingIntent.getActivity(this, 1, intent, 0);
      // Set the Notification UI parameters
      Notification notification = new Notification(R.drawable.icon, “Starting Playback”, System.currentTimeMillis());
      notification.setLatestEventInfo(this, album, artist, pi);
      // Set the Notification as ongoing
      notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;
      // Move the Service to the Foreground
      startForeground(NOTIFICATION_ID, notification);
    }

     

    使用stopForeground方法可以取消Service的前台属性:

     

    public void pausePlayback() {
      // Move to the background and remove the Notification
      stopForeground(true);
    }

     

    使用后台线程

    ServiceActivity一样,也是在主线程中运行的,为了更好地响应用户,我们需要使用后台线程的方式执行ServiceAndroid提供了两个抽象类来帮助我们实现:AsyncTaskIntentService

    使用AsyncTask

    AsyncTask不仅能帮助我们将费时操作放到后台执行,还可以实现和UI线程的同步。AsyncTask适合执行那些耗时比较短并且和UI线程有交互的任务,对于耗时较久的任务(例如下载),使用Service更为合适。需要注意的是AsyncTask在应用restart之后会被cancel掉。

     

    private class MyAsyncTask extends AsyncTask<String, Integer, String> {
    @Override
    protected String doInBackground(String... parameter) {
    // Moved to a background thread.
    String result = “”;
    int myProgress = 0;
    int inputLength = parameter[0].length();
    // Perform background processing task, update myProgress]
    for (int i = 1; i <= inputLength; i++) {
    myProgress = i;
    result = result + parameter[0].charAt(inputLength-i);
    try {
    Thread.sleep(100);
    } catch (InterruptedException e) { }
    publishProgress(myProgress);
    }
    // Return the value to be passed to onPostExecute
    return result;
    }
    @Override
    protected void onProgressUpdate(Integer... progress) {
    // Synchronized to UI thread.
    // Update progress bar, Notification, or other UI elements
    asyncProgress.setProgress(progress[0]);
    }
    @Override
    protected void onPostExecute(String result) {
    // Synchronized to UI thread.
    // Report results via UI update, Dialog, or notifications
    asyncTextView.setText(result);
    }
    }

     

    使用AsyncTask,首先要创建一个AsyncTask的子类类并实现doInBackgroundonProgressUpdateonPostExecute方法。这三个方法的说明:

    • doInBackground: 这个方法用于执行需要在后台线程中长期运行的操作,可以通过publishProgress方法传递参数给onProgressUpdate;在这个方法执行完毕之后,此方法将返回值传递给onProgressUpdate。
    • onProgressUpdate: 接收publishProgress方法传入的参数,更新UI
    • onPostExecute: doInBackground执行结束后,将返回值传入此方法。

    执行AsyncTask

    String input = “redrum ... redrum”;
    new MyAsyncTask().execute(input);

    Intent Service简介

    IntentService可以通过传入Intent参数调用,传入的Intent将进入一个队列中被异步执行。IntentService封装了消息的异步处理,后台线程创建以及与UI线程的同步。继承IntentService类并实现onHandleIntent方法,即可创建一个Intent Service

     

    import android.app.IntentService;
    import android.content.Intent;
    public class MyIntentService extends IntentService {
      public MyIntentService(String name) {
        super(name);
        // TODO Complete any required constructor tasks.
      }
      @Override
      public void onCreate() {
        super.onCreate();
        // TODO: Actions to perform when service is created.
      }
      @Override
      protected void onHandleIntent(Intent intent) {
      // This handler occurs on a background thread. TODO The time consuming task should be implemented here.
      // Each Intent supplied to this IntentService will be processed consecutively here. When all incoming Intents have been processed the Service will terminate itself.
      }
    }

     

    Loader简介

    Loader是一个抽象类,封装了异步加载数据的最佳实践,最典型的就是CursorLoader了。Android中创建Loader类的简单方法是继承AsyncTaskLoader类,并实现这两个功能:

    • 异步加载数据
    • 监测数据源的变化并及时更新

    手动创建线程及GUI线程同步

    尽管AsyncTaskIntent Service提供了简单易用的异步类封装,但我们也可以创建自定义的异步线程:

     

    // This method is called on the main GUI thread.
    private void backgroundExecution() {
      // This moves the time consuming operation to a child thread.
      Thread thread = new Thread(null, doBackgroundThreadProcessing, “Background”);
      thread.start();
    }
    // Runnable that executes the background processing method.
    private Runnable doBackgroundThreadProcessing = new Runnable() {
        public void run() {
          backgroundThreadProcessing();
        }
    };
    // Method which does some processing in the background.
    private void backgroundThreadProcessing() {
    // [ ... Time consuming operations ... ]
    }

     

    GUI线程同步:

    runOnUiThread方法会在UI线程执行:

     

    runOnUiThread(new Runnable() {
      public void run() {
      // Update a View or other Activity UI element.
      }
    });

     

    此外,可以使用Handler类更新UI线程:

     

    //This method is called on the main GUI thread.
    private void backgroundExecution() {
      // This moves the time consuming operation to a child thread.
      Thread thread = new Thread(null, doBackgroundThreadProcessing, “Background”);
      thread.start();
    }
    // Runnable that executes the background processing method.
    private Runnable doBackgroundThreadProcessing = new Runnable() {
      public void run() {
        backgroundThreadProcessing();
      }
    };
    // Method which does some processing in the background.
    private void backgroundThreadProcessing() {
    // [ ... Time consuming operations ... ]
    // Use the Handler to post the doUpdateGUI
    // runnable on the main UI thread.
      handler.post(doUpdateGUI);
    }
    //Initialize a handler on the main thread.
    private Handler handler = new Handler();
    // Runnable that executes the updateGUI method.
    private Runnable doUpdateGUI = new Runnable() {
      public void run() {
        updateGUI();
      }
    };
    // This method must be called on the UI thread.
    private void updateGUI() {
    // [ ... Open a dialog or modify a GUI element ... ]
    }

     

    Handler类还可以使用postDelayedpostAtTime实现推迟运行和推迟指定时间运行:

     

    // Post a method on the UI thread after 1sec.
    handler.postDelayed(doUpdateGUI, 1000);
    // Post a method on the UI thread after the device has been in use for 5mins.
    int upTime = 1000*60*5;
    handler.postAtTime(doUpdateGUI, SystemClock.uptimeMillis()+upTime);

     

    使用ALARMS

    Timer不太,Alarms属于系统服务,独立于应用程序。即使应用程序为启动,也可以使用Alarms启动应用程序并获取其服务,这样不仅减少了耦合,也减少了系统资源的占用。AndroidAlarms常与Broadcast Receiver一起使用。创建Alarm之前,首先要创建AlarmManager

     

    AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);

     

    创建,设置和取消ALARMS

    创建Alarm需要这些参数:alarm类型,触发时间,Alarm将要触发的Pending Intent。目前Alarm类型有这些:

    • RTC_WAKEUP:在指定时间启动指定Pending Intent,可以唤醒sleep中的设备。
    • RTC在指定时间启动指定Pending Intent,但不能唤醒sleep中的设备。
    • ELAPSED_REALTIME:在某段时间后启动指定的Pending Intent,某段时间是从设备启动但还没有唤醒设备算起。
    • ELAPSED_REALTIME_WAKEUP: 这个和ELAPSED_REALTIME的区别没搞明白,以后遇到了再查吧。

    下面是一个10秒后启动Pending IntentAlarm示例:

     

    AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
    // Set the alarm to wake the device if sleeping.
    int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
    // Trigger the device in 10 seconds.
    long timeOrLengthofWait = 10000;
    // Create a Pending Intent that will broadcast and action
    String ALARM_ACTION = “ALARM_ACTION”;
    Intent intentToFire = new Intent(ALARM_ACTION);
    PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0);
    // Set the alarm
    alarmManager.set(alarmType, timeOrLengthofWait, alarmIntent);

     

    取消Alarm

    alarmManager.cancel(alarmIntent);

    这里的alarmIntent是指使用Alarm启动的Pending Intent

     

    创建可重复的Alarm

     

    使用setRepeating或setInexactRepeating方法替代前面的set方法,并传递响应的参数进去,就可以实现可重复的Alarm

     

    相比setRepeating,setInexactRepeating更省电,但不能指定某个具体的时间间隔。

     

    setInexactRepeating可以接收的时间间隔参数:

     

    • INTERVAL_FIFTEEN_MINUTES
    • INTERVAL_HALF_HOUR
    • INTERVAL_HOUR
    • INTERVAL_HALF_DAY
    • INTERVAL_DAY

     

    下面这个例子指定半小时后启动Alarm,然后每隔半小时启动一次:

     

    // Get a reference to the Alarm Manager
    AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
    // Set the alarm to wake the device if sleeping.
    int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
    // Schedule the alarm to repeat every half hour.
    long timeOrLengthofWait = AlarmManager.INTERVAL_HALF_HOUR;
    // Create a Pending Intent that will broadcast and action
    String ALARM_ACTION = “ALARM_ACTION”;
    Intent intentToFire = new Intent(ALARM_ACTION);
    PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0);
    // Wake up the device to fire an alarm in half an hour, and every half-hour after that.
    alarmManager.setInexactRepeating(alarmType, timeOrLengthofWait, timeOrLengthofWait, alarmIntent);

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    SpriteKit改变Node锚点其物理对象位置不对的解决
    亲热接触Redis-第二天(Redis Sentinel)
    Java设计模式(二)-单例模式
    Android—构建安全的Androidclient请求,避免非法请求
    自己主动化測试使用mybatis更新数据库信息实例
    UML回想-通信图
    第十六课 数组的引入 【项目1-5】
    被这个样式惊醒
    netty自定义解码器
    解决netty客户端接收报文不完整的情况
  • 原文地址:https://www.cnblogs.com/jubincn/p/3521452.html
Copyright © 2020-2023  润新知