├── Context
│ ├── ContextWrapper (Service)
│ │ ├── ContextThemeWrapper (Activity)
Service只是一个没有界面(Theme)的Activity,它继承的是ContextWrapper。
Activity继承的是ContextThemeWrapper,所以有主题,有界面。
Service的等级和Activity差不多。由于Service没有界面,用户不可见的。所以,Service一般作为运行在后台的服务,做一个后台的工作(监听什么,记录什么,刷新什么)。
说这么多很多东西可能被概念混淆:Service虽然是不可以见的,运行的后台的,说到底也只是一个没有主题的Activity,但是还是运行在主线程中的。即使是Activity,不开子线程也是运行在主线程中的,所以Service也一样,即使用了Service,要做一些耗时的操作,也必须在子线程中,或者异步任务中。
要实现IPC(跨进程调用)时,Service上只能用AIDL的方式调用bindService;其次,还有使用BroadcastReceiver。
Local Service :运行在主进程中,因为在主进程中,所以不需要IPC,那就更不需要写一AIDL,这里的话用bindService比较方便,绑定Service的Context销毁的时候,再unbindService,就可以停止服务了。
Remote Service :在AndroidMainfest.xml的< service >
标签中指定android:process=":remote"
即可。此时,Service启动后将作为一个独立的进程。启动Service的Context所在的进程被回收后,Service一样可以运行。这样的话,有助于不同App之间的通信。
startService流程图:
bindService流程图:
startService:
就是简单的开启一个Service,然后服务运行,不能进行通信,除非调用stopService。
startService 方法被调用N次,onCreate方法只会调用一次,onStartCommand将会被调用多次(和startService的次数一致)。
bindService:
绑定Service需要一个ServiceConnection,通过这个ServiceConnection可以拿到IBinder对象,就可以和这个Service进行通信。如果要停止服务,则要使用unbindService。相当于把Service的生命周期交给绑定它的Context去管理(对应的onCreate去bind,对用的onDestory去unbind)。
调用 bindService 调用N次,onCreate方法都只会调用一次,同时onStartCommand方法始终不会被调用。
startService + bindService:
调用unbindService已经不能停止Service,必须调用stopService 或 Service自身的stopSelf。
启动又绑定Service,Service后一直会在后台运行,当然onCreate也只会调用一次,后面的onStartCommand会被调用N次。
要做成什么样子呢?就做一个绑定Service,然后进行通信。然后,下载图片,再将图片渲染在Activity的ImageView上。
还引用了一个AsyncTask模板。
先写一个Activity的回调接口 IBinderView
public interface IBinderView {
/** * 开始下载 */
void downloadStart();
/** * 下载成功 * * @param imageFilePath */
void downloadSuccess(String imageFilePath);
/** * 下载失败 */
void downloadFailure();
}
DownloadService
public class DownloadService extends Service {
private static final String TAG = "DownloadService";
private IBinder binder;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return this.binder;
}
/** * Called by the system when the service is first created. Do not call this method directly. */
@Override
public void onCreate() {
super.onCreate();
this.binder = new DownloadServiceBinder();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
/** * Called by the system to notify a Service that it is no longer used and is being removed. The * service should clean up any resources it holds (threads, registered * receivers, etc) at this point. Upon return, there will be no more calls * in to this Service object and it is effectively dead. Do not call this method directly. */
@Override
public void onDestroy() {
super.onDestroy();
}
/** * Service Binder */
public class DownloadServiceBinder extends Binder {
public IBinderView iBinderView;
public DownloadService getService() {
return DownloadService.this;
}
}
public void startDownload(String imageUrl) {
((DownloadServiceBinder) DownloadService.this.binder).iBinderView.downloadStart();
new DownloadImageAsyncTask(this).execute(imageUrl);
}
/** * 下载图片异步任务 */
public class DownloadImageAsyncTask extends AsyncTask<String, Integer, String> {
private Service service;
private String localFilePath;
public DownloadImageAsyncTask(Service service) {
super();
this.service = service;
}
/** * 对应AsyncTask第一个参数 * 异步操作,不在主UI线程中,不能对控件进行修改 * 可以调用publishProgress方法中转到onProgressUpdate(这里完成了一个handler.sendMessage(...)的过程) * * @param params The parameters of the task. * @return A result, defined by the subclass of this task. * @see #onPreExecute() * @see #onPostExecute * @see #publishProgress */
@Override
protected String doInBackground(String... params) {
URL fileUrl = null;
try {
fileUrl = new URL(params[0]);
} catch (MalformedURLException e) {
e.printStackTrace();
}
if (fileUrl == null) return null;
try {
HttpURLConnection connection = (HttpURLConnection) fileUrl.openConnection();
connection.setRequestMethod("GET");
connection.setDoInput(true);
connection.connect();
//计算文件长度
int lengthOfFile = connection.getContentLength();
/** * 不存在SD卡,就放到缓存文件夹内 */
File cacheDir = this.service.getCacheDir();
File downloadFile = new File(cacheDir, UUID.randomUUID().toString() + ".jpg");
this.localFilePath = downloadFile.getPath();
if (!downloadFile.exists()) {
File parent = downloadFile.getParentFile();
if (parent != null) parent.mkdirs();
}
FileOutputStream output = new FileOutputStream(downloadFile);
InputStream input = connection.getInputStream();
InputStream bitmapInput = connection.getInputStream();
//下载
byte[] buffer = new byte[1024];
int len;
long total = 0;
// 计算进度
while ((len = input.read(buffer)) > 0) {
total += len;
this.publishProgress((int) ((total * 100) / lengthOfFile));
output.write(buffer, 0, len);
}
output.close();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/** * 对应AsyncTask第三个参数 (接受doInBackground的返回值) * 在doInBackground方法执行结束之后在运行,此时已经回来主UI线程当中 能对UI控件进行修改 * * @param string The result of the operation computed by {@link #doInBackground}. * @see #onPreExecute * @see #doInBackground * @see #onCancelled(Object) */
@Override
protected void onPostExecute(String string) {
super.onPostExecute(string);
((DownloadServiceBinder) DownloadService.this.binder).iBinderView.downloadSuccess(this.localFilePath);
}
/** * 对应AsyncTask第二个参数 * 在doInBackground方法当中,每次调用publishProgress方法都会中转(handler.sendMessage(...))到onProgressUpdate * 在主UI线程中,可以对控件进行修改 * * @param values The values indicating progress. * @see #publishProgress * @see #doInBackground */
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
/** * 运行在主UI线程中,此时是预执行状态,下一步是doInBackground * * @see #onPostExecute * @see #doInBackground */
@Override
protected void onPreExecute() {
super.onPreExecute();
}
/** * <p>Applications should preferably override {@link #onCancelled(Object)}. * This method is invoked by the default implementation of * {@link #onCancelled(Object)}.</p> * <p/> * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and * {@link #doInBackground(Object[])} has finished.</p> * * @see #onCancelled(Object) * @see #cancel(boolean) * @see #isCancelled() */
@Override
protected void onCancelled() {
super.onCancelled();
((DownloadServiceBinder) DownloadService.this.binder).iBinderView.downloadFailure();
}
}
}
DownloadServiceActivity
public class DownloadServiceActivity extends AppCompatActivity implements View.OnClickListener, IBinderView {
private static final String OBJECT_IMAGE_URL = "http://img.blog.csdn.net/20150913233900119";
private Button startBT;
private ImageView imageIV;
private DownloadService service;
private ServiceConnection connection;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_download_service);
this.initViews();
this.initData();
this.initListeners();
}
private void initViews() {
TextView imageTV = (TextView) this.findViewById(R.id.image_tv);
imageTV.setText(OBJECT_IMAGE_URL);
this.startBT = (Button) this.findViewById(R.id.start_service_bt);
this.imageIV = (ImageView) this.findViewById(R.id.image_iv);
}
private void initData() {
this.connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
DownloadService.DownloadServiceBinder binder = (DownloadService.DownloadServiceBinder) service;
binder.iBinderView = DownloadServiceActivity.this;
DownloadServiceActivity.this.service = binder.getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
DownloadServiceActivity.this.service = null;
}
};
DownloadServiceActivity.this.bindService(
new Intent(DownloadServiceActivity.this, DownloadService.class),
DownloadServiceActivity.this.connection,
Context.BIND_AUTO_CREATE
);
}
private void initListeners() {
this.startBT.setOnClickListener(this);
}
/** * Called when a view has been clicked. * * @param v The view that was clicked. */
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_service_bt:
this.service.startDownload(OBJECT_IMAGE_URL);
break;
}
}
/** * 开始下载 */
@Override
public void downloadStart() {
this.startBT.setEnabled(false);
}
/** * 下载成功 * * @param imageFilePath */
@Override
public void downloadSuccess(String imageFilePath) {
/** * 设置按钮可用,并隐藏Dialog */
this.startBT.setEnabled(true);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight = metrics.heightPixels;
/** * ImageUtil.decodeScaleImage 解析图片 */
Bitmap bitmap = ImageUtil.decodeScaleImage(imageFilePath, screenWidth, screenHeight);
DownloadServiceActivity.this.imageIV.setImageBitmap(bitmap);
}
/** * 下载失败 */
@Override
public void downloadFailure() {
this.startBT.setEnabled(true);
}
}
AndroidManifest.xml
<service android:name="com.camnter.newlife.service.DownloadService" />
这里就需要写一个AIDL文件,提供给Activity与Remote Service通讯。
IPushMessage.aidl
interface IPushMessage {
/** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
String onMessage();
}
写完AIDL文件后,此时,会自动生成一个 IPushMessage 接口的代码。
IPushMessage
/* * This file is auto-generated. DO NOT MODIFY. * Original file: /Users/CaMnter/GitHub/AndroidLife/app/src/main/aidl/com/camnter/newlife/aidl/IPushMessage.aidl */
package com.camnter.newlife.aidl;
// Declare any non-default types here with import statements
public interface IPushMessage extends android.os.IInterface {
/** * Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.camnter.newlife.aidl.IPushMessage {
private static final java.lang.String DESCRIPTOR = "com.camnter.newlife.aidl.IPushMessage";
/** * Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/** * Cast an IBinder object into an com.camnter.newlife.aidl.IPushMessage interface, * generating a proxy if needed. */
public static com.camnter.newlife.aidl.IPushMessage asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.camnter.newlife.aidl.IPushMessage))) {
return ((com.camnter.newlife.aidl.IPushMessage) iin);
}
return new com.camnter.newlife.aidl.IPushMessage.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_basicTypes: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_onMessage: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.onMessage();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.camnter.newlife.aidl.IPushMessage {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.lang.String onMessage() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_onMessage, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_onMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public java.lang.String onMessage() throws android.os.RemoteException;
}
可以看到:
1.生成了一个静态抽象类 Stub ,用于提供给Service实现 Service 的 IBinder,然后返回给ServiceConnection。
2.生成了抽象类 Stub 的一个静态内部类 Proxy ,作为AIDL代理服务类,进行远程通信。
3.生成了AIDL文件中自定义的方法(basicTypes、onMessage)。
注意!!!!!!,在Service的实现中,要实现自动生成的 IPushMessage 接口中的静态抽象类 Stub ,以此作为 Service 的 Binder 对象,进行通信。
PushMessageService
public class PushMessageService extends Service {
private static final String TAG = "MessageService";
private IPushMessageImpl binder;
/** * AIDL implement * 实现AIDL生成静态抽象类 IPushMessage.Stub */
private class IPushMessageImpl extends IPushMessage.Stub {
/** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. * * @param anInt * @param aLong * @param aBoolean * @param aFloat * @param aDouble * @param aString */
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public String onMessage() throws RemoteException {
return UUID.randomUUID().toString();
}
}
/** * Called by the system when the service is first created. Do not call this method directly. */
@Override
public void onCreate() {
super.onCreate();
this.binder = new IPushMessageImpl();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return this.binder;
}
}
当使用ServiceConnection桥接好Service 与 Activity成功后,我们能拿到IBinder service
对象。注意!!!!!!,此时,的IBinder对象只是BinderProxy对象,不能直接转换为Binder对象,我们只能再调用AIDL生成类IPushMessage中的内部静态抽象类 Stub 的静态方法 public static com.camnter.newlife.aidl.IPushMessage asInterface(android.os.IBinder obj)
将其转为IPushMessage类型,以此来跟Service通信。
AIDLActivity
public class AIDLActivity extends AppCompatActivity {
private static final String TAG = "AIDLActivity";
private TextView aidlTV;
private String pushMessage;
private IPushMessage iPushMessage;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/* * 这里的 service 不是 Binder对象 * 而是 BinderProxy对象 * 不能 直接转为Binder( (Binder)service ),是错误的。 */
AIDLActivity.this.iPushMessage = IPushMessage.Stub.asInterface(service);
try {
AIDLActivity.this.pushMessage = AIDLActivity.this.iPushMessage.onMessage();
AIDLActivity.this.aidlTV.setText(AIDLActivity.this.pushMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
AIDLActivity.this.iPushMessage = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_aidl);
this.aidlTV = (TextView) this.findViewById(R.id.aidl_tv);
Intent intent = new Intent(this, PushMessageService.class);
this.startService(intent);
this.bindService(intent, this.connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
this.unbindService(this.connection);
super.onDestroy();
}
}
AndroidManifest.xml
<!-- action 写上 aidl生成类的所在包 -->
<service android:name=".aidl.PushMessageService" android:process=":remote">
<intent-filter>
<action android:name="com.camnter.newlife.aidl.IPushMessage" />
</intent-filter>
</service>
然后,可以看到Service作为单独进程存在了: