Service服务

Android的UI是线程不安全的,要更新程序的UI元素,必须在主线程中进行,否则会出异常。

异步消息处理机制

异步处理消息由4个部分组成:Message、Handler、MessageQueue和Looper。

  1. Message:在线程间传递的消息,主要有4个字段:
    1. int Message;
    2. int arg1:
    3. int arg2:
    4. Object obj;Object对象
    5. 只能在内部携带少量信息
  2. Handler:处理者,用于发送和处理消息
    1. sendMessage():发送消息;
    2. handlerMessage():处理消息;
    3. 在主线程中创建
  3. MessageQueue:消息队列,用于存放所有通过Handler发送的消息。每个线程只有一个MessageQueue对象;
  4. Looper:每个线程的MessageQueue的管家,调用Looper的loop()方法后,会进入一个无限循环中,每当发现MessageQueue中存在消息时,就会取出来传递到Handler的handleMessage处理。每个线程只有一个Looper对象。

代码:

public class MainActivity extends Activity implements OnClickListener {
    private String TAG="AndroidThread";
    private String className="MainActivity";
    private Button changeText;
    private TextView text;
    
    public static final int UPDATE_TEXT=1;
    private Handler handler=new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
//			super.handleMessage(msg);
            switch(msg.what) {
            case UPDATE_TEXT:
                text.setText("Change Text by Hanler (in subthread)");
                break;
            default:
                break;
            }
        }
        
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        changeText=(Button) findViewById(R.id.change_text);
        changeText.setOnClickListener(this);
        text=(TextView) findViewById(R.id.text);
    }
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch(v.getId()) {
        case R.id.change_text:
            new Thread(new Runnable() {				
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    Log.d(TAG, className+"use subthread change main thread view by Handler");
//					text.setText("update text from thread");
                    Message message=new Message();
                    message.what=UPDATE_TEXT;
                    handler.sendMessage(message);
                }
            }).start();;
            break;
        default:
                break;
        }
    }
}

AsyncTask

背后实现的原理也是基于异步消息处理机制,是一个抽象类。 AsyncTask类有三个泛型参数:

  1. Params:执行AsyncTask时需要传入的参数,用于后台任务中使用;
  2. Progress:后台执行任务时,使用它作为在前台界面上显示进度的进度单位;
  3. Result:任务执行完毕,指定返回值的类型。
    class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
    ......
    }
    

说明

  1. void:执行AsyncTask时不需要传入参数到后台任务;
  2. Integer:使用整型数据作为进度显示单位;
  3. Boolean:用布尔来反馈执行结果。

AsyncTask需要重写的4个方法

  1. onPreExecute:在后台任务开始前调用,用于界面上的初始化操作,如显示一个进度条;
  2. doInBackground:方法的代码都会在子线程中执行,处理耗时的任务。任务完成后,通过return返回任务的结果(结果类型为上面类泛型的第三个参数)。这个方法中不能进行UI操作。*可以调用publishProgress反馈当前任务进度*。
  3. onProgressUpdate:后台调用publishProgress后,这个方法会被调用,方法中的参数就是后台任务中传递过来的。这个方法可以进行UI操作,利用参数的数据更新界面元素。
  4. onPostExecute:后台任务执行完毕通过return返回时,这个方法会被调用。返回的数据作为参数传入此方法中,可以利用此数据进行UI操作。如关闭进度条等。

代码:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {
	progressDialog.show(); // 显示进度对话框
}
@Override
protected Boolean doInBackground(Void... params) {
try {
	while (true) {
	int downloadPercent = doDownload(); // 这是一个虚构的方法
	publishProgress(downloadPercent);
	if (downloadPercent >= 100) {
	break;
	}
}
} catch (Exception e) {
	return false;
}
	return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
// 在这里更新下载进度
	progressDialog.setMessage("Downloaded " + values[0] + "%");
}
@Override
protected void onPostExecute(Boolean result) {
	progressDialog.dismiss(); // 关闭进度对话框
	// 在这里提示下载结果
	if (result) {
		Toast.makeText(context, "Download succeeded",
		Toast.LENGTH_SHORT).show();
	} else {
		Toast.makeText(context, " Download failed",
		Toast.LENGTH_SHORT).show();
		}
	}
}

说明

  1. doInBackground的代码都在子线程中运行;
  2. 注意doInBackground、publishProgress、onProgressUpdate三个方法的参数,*...*是指多个参数;
  3. 通过publishProgress传递数据后,在onProgressUpdate在主线程中进行更新UI。publishProgress是一个纽带,从子线程切换到主线程。
  4. onPostExecute根据doInBackground返回的结果执行,此方法也在主线程进行。此方法进行收尾工作。
  5. 启动任务的方法:
    new DownloadTask().execute();
    

服务的基本用法

定义一个服务

  1. Service有一个唯一的onBind抽象方法;必须在子类里实现。
    public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
    	return null;
    }
    @Override
    public void onCreate() {
    	super.onCreate();
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    	return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {
    	super.onDestroy();
    }
    }
    
    1. 除了onBind方法,同时重写了onCreate、onStartCommand、onDestroy方法;
    2. onCreate在服务创建时启动;onStartCommand在服务启动时调用;onDestroy在服务销毁时调用;
    3. 服务的逻辑是在onStartCommand中写,onDestroy中回收资源;
  2. 在Manifest中注册服务
    <service android:name=".MyService" >
    </service>
    

启动和停止服务

case R.id.start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent); // 启动服务
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent); // 停止服务
break;
  1. 启动服务:通过构建一个Intent启动服务,通过startService调用Intent来启动服务;
  2. 停止服务:通过stopService调用Intent来停止服务。
  3. startService、stopService都是定义在Context类中,可以直接调用。
  4. 如果没有stopService,则服务一直运行。
  5. 注:onCreate时服务第一次创建时调用;onStartCommand则是每次启动服务时调用。

活动和服务通信

public class MyService extends Service {
private DownloadBinder mBinder = new DownloadBinder();
	class DownloadBinder extends Binder {
		public void startDownload() {
		Log.d("MyService", "startDownload executed");
	}
	public int getProgress() {
		Log.d("MyService", "getProgress executed");
		return 0;
		}
	}
@Override
public IBinder onBind(Intent intent) {
	return mBinder;
	}
......
}
  1. 通过一个继承自Binder的内部类;
  2. 在onBind方法里返回Binder的实例。
    private MyService.DownloadBinder downloadBinder;
    private ServiceConnection connection = new ServiceConnection() {
    	@Override
    	public void onServiceDisconnected(ComponentName name) {
    }
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
    	downloadBinder = (MyService.DownloadBinder) service;
    	downloadBinder.startDownload();
    	downloadBinder.getProgress();
    }
    };
    
    Intent bindIntent = new Intent(this, MyService.class);
    bindService(bindIntent, connection, BIND_AUTO_CREATE); // 绑定服务
    break;
    unbindService(connection); // 解绑服务
    break;
    
  3. 创建一个ServiceConnection的匿名类,并重新onServiceConnected、onServiceDisconnected方法;
  4. 在onServiceConnected方法里,调用Binder的方法,通过Binder实现里Service和Activity的互动机制;
  5. bindService方法用于绑定活动和服务,bindService有三个参数:第一个时Intent,第二个参数是ServiceConnection的实例,第三个参数是一个标志位,BIND_AUTO_CREATE表示活动和服务自动创建服务,同时调用Service的onCreate方法,但是onStartCommand不会指定。
  6. unbindService用于解绑Service。
  7. 因为在onServiceConnected中调用里Binder的两个方法,所有Binder的方法会执行,但是Service的onStartCommand不会执行。

服务生命周期

  1. bindService方法用来获取一个Service的持久链接,这时会回调服务中的onBind方法,onBind会返回一个Bind的对象实例;
  2. 在ServiceConnection中调用Binder的实例;
  3. stopService会onDestroy会销毁服务,unbindService也会调用onDestroy方法。
  4. 服务一旦被启动、绑定,服务就会一直运行,stopService和和unbindService同时调用才会导致服务销毁

更多技巧

前台服务

  1. 服务几乎都是在后台运行,优先级较低,有可能被系统回收;
  2. 前台服务:一直有个运行的图标在系统的状态栏显示。
    Notification notification = new Notification(R.drawable.ic_launcher,
    "Notification comes", System. currentTimeMillis());
    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
    notificationIntent, 0);
    notification.setLatestEventInfo(this, "This is title", "This is
    content", pendingIntent);
    startForeground(1, notification);
    
    1. startForeground会让Service变成一个前台服务,并在系统状态栏显示。

使用IntentService

服务的代码默认是在运行在主线程中。 IntentService用于创建一个异步、会自然停止的服务

public class MyIntentService extends IntentService {
public MyIntentService() {
	super("MyIntentService"); // 调用父类的有参构造函数
}
@Override
protected void onHandleIntent(Intent intent) {
	// 打印当前线程的id
	Log.d("MyIntentService", "Thread id is " + Thread.currentThread().
	getId());
}
@Override
public void onDestroy() {
	super.onDestroy();
	Log.d("MyIntentService", "onDestroy executed");
}
}
  1. 首先构造一个无参构造函数,在内部调用有参构造函数;
  2. 在onHandleIntent抽象方法中处理具体的逻辑;根据IntentService的特性,这个服务运行结束后会自动停止。因此onDestroy方法会自动运行。

后台执行定时任务

  1. 使用Alarm机制定期唤醒CPU,这种方式不同于Timer类(Timer长期在后台,会被手机的休眠策略导致无法运行。
  2. Alarm借助AlarmManager来实现。
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    new Thread(new Runnable() {
    	@Override
    	public void run() {
    	Log.d("LongRunningService", "executed at " + new Date().
    	toString());
    	}
    }).start();
    AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
    int anHour = 60 * 60 * 1000; // 这是一小时的毫秒数
    long triggerAtTime = SystemClock.elapsedRealtime() + anHour;
    Intent i = new Intent(this, AlarmReceiver.class);
    PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
    manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
    return super.onStartCommand(intent, flags, startId);
    }
    
    1. onStartCommand中开启子线程,记录Log;
    2. getSystemService(ALARM_SERVICE)方法获得一个AlarmManager的实例;
    3. SystemClock.eclipseRealTime()获取开机至今的经历的时间毫秒数;
    4. manager.set方法参数:
      1. ELAPSE_REALTIME_WAKEUP:定时任务的出发时间通过系统开启算起,唤起CPU;
      2. 第二个参数:定时任务的出发时间,以毫秒为单位;
      3. 第三个参数:PendingIntent,以通知Broadcast。

Broadcast Code :

public class AlarmReceiver extends BroadcastReceiver {
	@Override
	public void onReceive(Context context, Intent intent) {
		Intent i = new Intent(context, LongRunningService.class);
		context.startService(i);
	}
}
  1. 在Broadcast中,启动之前的定时服务。
  2. 这里巧妙的是,Service和Broadcast轮流互相启动。