Android四大组件之Service(续)

时间:2023-03-08 18:29:14

本地服务启动和通过广播的方式回调是非常简单的。

下面介绍远程服务和通过远程回调的方式通知activity的方式。

1.service定义:

package com.joyfulmath.globalsearch.imagesearch.service;

import java.util.ArrayList;

import com.joyfulmath.globalsearch.imagesearch.service.aidl.IBuildService;
import com.joyfulmath.globalsearch.imagesearch.service.aidl.ICallBack;
import com.joyfulmath.globalsearch.imagesearch.service.aidl.PersonImageItem; import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log; /*
* image operator running in service thread and listener the contentobserver
* */
public class ImageSearchBuildService extends Service implements onServiceEngineListener{
private static final String TAG = "ImageSearch.BuildService"; public final static String ACTION = "com.joyfulmath.intent.imagesearch.BUILD_IMAGE";
public final static String ACTION_METHOD = "action.method";
public final static String ACTION_REBUILD_DB = "action.rebuild.db"; private ImageSearchOperator mImageSearchOperator = null;
private ImageSearchBuildServiceImpl iServiceImpl = null;
private RemoteCallbackList<ICallBack> mCallbacks = new RemoteCallbackList<ICallBack>();
private long mCurrentCookie = -1;
@Override
public IBinder onBind(Intent intent) {
mCurrentCookie = intent.getLongExtra("cookie",-1);
Log.i(TAG, "[onBind] mCurrentCookie:"+mCurrentCookie);
iServiceImpl = new ImageSearchBuildServiceImpl(mImageSearchOperator);
return iServiceImpl;
} @Override
public void onCreate() {
Log.i(TAG, "[onCreate]");
super.onCreate();
mImageSearchOperator = new ImageSearchOperator(this,this);
mImageSearchOperator.startOperatorThread();
} @Override
public void onDestroy() {
Log.i(TAG, "[onDestroy]");
super.onDestroy();
if(mImageSearchOperator!=null)
{
mImageSearchOperator.exitOperatorThread();
}
iServiceImpl = null;
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "[onStartCommand]");
String method = intent.getStringExtra(ACTION_METHOD);
if(method!=null)
{
if(method.equals(ACTION_REBUILD_DB))
{
//start rebuild db
mImageSearchOperator.rebuildImageSearchDbHandle();
}
} return Service.START_NOT_STICKY;
} @Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "[onUnbind]");
if(mImageSearchOperator!=null)
{
mImageSearchOperator.abortFindPersonHandle();
} return super.onUnbind(intent);
} private class ImageSearchBuildServiceImpl extends IBuildService.Stub{ public ImageSearchOperator mOperator = null;
public ImageSearchBuildServiceImpl(ImageSearchOperator operator)
{
mOperator = operator;
} @Override
public int getServiceStatus() throws RemoteException {
return mOperator.mOperatorStatus;
} @Override
public int findPerson(String path, int filter)
throws RemoteException {
Log.i(TAG, "findPerson imageFilter:"+filter+" path: "+path);
mOperator.findPersonHandle(path, filter,mCurrentCookie);
return 0;
} @Override
public void registerCallback(ICallBack cb) throws RemoteException {
// TODO Auto-generated method stub
if(cb!=null)
{
mCallbacks.register(cb);
}
} @Override
public void unregisterCallback(ICallBack cb) throws RemoteException {
// TODO Auto-generated method stub
if(cb!=null)
{
mCallbacks.unregister(cb);
}
} } @Override
public void onEngineStatusUpdate(int status) {
nofityStatusChanged(status);
} @Override
public void onQueryResult(long sessionid,String srcPath, int imageFilter,
ArrayList<PersonImageItem> mResultItems) {
if(mCurrentCookie == sessionid)
{
onFindPersonResult(srcPath,imageFilter,mResultItems);
}
else
{
Log.w(TAG, "onQueryResult old session:"+sessionid+" currentSession:"+mCurrentCookie);
}
} private void nofityStatusChanged(int status) {
// synchronized (this) {
// int n = mCallbacks.beginBroadcast();
// Log.i(TAG, "nofityStatusChanged n:"+n);
// try {
// for (int i = 0; i < n; i++) {
// mCallbacks.getBroadcastItem(i).onServiceStatusChanged(status);
// }
// } catch (RemoteException e) {
// Log.e(TAG, "RemoteException:"+e.getMessage());
// }
// mCallbacks.finishBroadcast();
// } } private void onFindPersonResult(String srcPath, int imageFilter,
ArrayList<PersonImageItem> mResultItems)
{
try{
synchronized (this) {
Log.i(TAG, "onFindPersonResult filter:"+imageFilter);
int n = mCallbacks.beginBroadcast();
try {
for (int i = 0; i < n; i++) {
mCallbacks.getBroadcastItem(i).onQueryResult(mResultItems, srcPath, imageFilter);
}
} catch (RemoteException e) {
Log.e(TAG, "remote error:"+e);
}
mCallbacks.finishBroadcast();
}
}catch (Exception e) {
Log.i(TAG, "onFindPersonResult Wrong:"+e.getMessage());
}
}
}

2.IBuildService

package com.joyfulmath.globalsearch.imagesearch.service.aidl;
import com.joyfulmath.globalsearch.imagesearch.service.aidl.PersonImageItem;
import com.joyfulmath.globalsearch.imagesearch.service.aidl.ICallBack; interface IBuildService{
int getServiceStatus();
int findPerson(String path,int filter);
void registerCallback(ICallBack cb);
void unregisterCallback(ICallBack cb);
}

3.数据传递类型:

package com.joyfulmath.globalsearch.imagesearch.service.aidl;

import android.os.Parcel;
import android.os.Parcelable; public class PersonImageItem implements Parcelable { public int persion_id;
public int contact_id = -1;
public int type = -1;
public int image_id = -1;
public int imageSize;
public String Bitmap_Path = ""; public PersonImageItem(int personId,int contact_id,String path,int imageId, int imageSize,int type)
{
this.persion_id = personId;
this.contact_id = contact_id;
this.Bitmap_Path = path;
this.image_id = imageId;
this.imageSize = imageSize;
this.type = type;
} public PersonImageItem(Parcel source)
{
readFromParcel(source);
} public PersonImageItem(PersonImageItem person) {
this.persion_id = person.persion_id;
this.contact_id = person.contact_id;
this.Bitmap_Path =person.Bitmap_Path;
this.image_id = person.image_id;
this.imageSize = person.imageSize;
this.type = person.type;
} public PersonImageItem(int persionId, int contact_id,int type)
{
this.persion_id = persionId;
this.contact_id = contact_id;
this.type = type;
} public final static Parcelable.Creator<PersonImageItem> CREATOR = new Parcelable.Creator<PersonImageItem>() { @Override
public PersonImageItem createFromParcel(Parcel source) {
return new PersonImageItem(source);
} @Override
public PersonImageItem[] newArray(int size) {
// TODO Auto-generated method stub
return new PersonImageItem[size];
} }; @Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
} @Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeInt(persion_id);
dest.writeInt(contact_id);
dest.writeInt(image_id);
dest.writeInt(imageSize);
dest.writeInt(type);
dest.writeString(Bitmap_Path); } public void readFromParcel(Parcel source)
{
persion_id = source.readInt();
contact_id = source.readInt();
image_id = source.readInt();
imageSize = source.readInt();
type = source.readInt();
Bitmap_Path = source.readString();
}
}
package com.joyfulmath.globalsearch.imagesearch.service.aidl;
parcelable PersonImageItem;
PersonImageItem是service返回给client端的结果类型。
package com.joyfulmath.globalsearch.imagesearch.service.aidl;

import com.joyfulmath.globalsearch.imagesearch.service.aidl.PersonImageItem;
interface ICallBack{
void onQueryResult(in List<PersonImageItem> items,String path,int filter);
void onServiceStatusChanged(int status);
}

client端engine,启动service,调用service服务以及观察service返回的结果。

package com.joyfulmath.globalsearch.imagesearch.client;

import java.util.ArrayList;
import java.util.List; import com.joyfulmath.globalsearch.imagesearch.AlertListAdapter.ListAdapterInfo;
import com.joyfulmath.globalsearch.imagesearch.client.resulttype.ContactResultItem;
import com.joyfulmath.globalsearch.imagesearch.client.resulttype.SdcardImageResultItem;
import com.joyfulmath.globalsearch.imagesearch.service.FaceRecognizeEngine;
import com.joyfulmath.globalsearch.imagesearch.service.ImageSearchBuildService;
import com.joyfulmath.globalsearch.imagesearch.service.aidl.IBuildService;
import com.joyfulmath.globalsearch.imagesearch.service.aidl.ICallBack;
import com.joyfulmath.globalsearch.imagesearch.service.aidl.PersonImageItem;
import com.joyfulmath.globalsearch.imagesearch.utils.ImageSearchUtils; import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.provider.MediaStore;
import android.util.Log; public class SearchEngine extends BroadcastReceiver implements Runnable{ private static final String TAG = "ImageSearch.Engine";
private Context mContext = null;
private ContentResolver mContentResolver = null;
private onEngineListener mListener=null;
public Handler mEngineHandler =null;
public Looper mEngineLooper =null;
public SearchWorkQueue mWorkQueue = null;
public int mbgEngineStatus = ImageSearchUtils.OPERATOR_STATUS_NONE;
private IBuildService iServce = null;
private long mSessionCookie = -1; public SearchEngine(Context context,onEngineListener listener) {
mContext = context;
mContentResolver = mContext.getContentResolver();
mListener = listener;
mbgEngineStatus = ImageSearchUtils.OPERATOR_STATUS_NONE;
mSessionCookie = generateCookie();
startHandleThread();
registerBroadCast();
bindService();
}
  //监听service的回调
private ICallBack.Stub mCallback = new ICallBack.Stub() { @Override
public void onServiceStatusChanged(int status) throws RemoteException {
// TODO Auto-generated method stub
mbgEngineStatus = status;
} @Override
public void onQueryResult(List<PersonImageItem> items, String path,
int filter) throws RemoteException {
Log.i(TAG, "ICallBack onQueryResult filter:"+filter);
if(items.size()>0)
{
Log.i(TAG, "ICallBack onQueryResult items:"+items.size()); ArrayList<GenericResultItem> mReults = new ArrayList<GenericResultItem>(items.size()); switch(filter)
{
case ImageSearchUtils.FIND_TYPE_CONTACT:
for(PersonImageItem person:items)
{
ContactResultItem contactitem = new ContactResultItem(person, mContext);
mReults.add(contactitem);
}
break;
case ImageSearchUtils.FIND_TYPE_IMAGE_STORE:
for(PersonImageItem person:items)
{
SdcardImageResultItem contactitem = new SdcardImageResultItem(person);
mReults.add(contactitem);
}
break;
} if(mWorkQueue!=null)
{
mWorkQueue.notifyWorkQueue(mReults,filter);
}
}
else
{
Log.i(TAG, "ICallBack onQueryResult items null");
if(mWorkQueue!=null)
{
mWorkQueue.notifyWorkQueue(null,filter);
}
}
}
};

  //service connect
private ServiceConnection conn = new ServiceConnection() { @Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
Log.i(TAG, "onServiceDisconnected");
iServce = null;
} @Override
public void onServiceConnected(ComponentName name, IBinder service) {
iServce = IBuildService.Stub.asInterface(service);
Log.i(TAG, "onServiceConnected");
try {
iServce.registerCallback(mCallback);
} catch (RemoteException e) {
Log.w(TAG, "onServiceConnected wrong:"+e.getMessage());
}
}
};

  //启动service并绑定
private void bindService()
{
Log.i(TAG, "[bindService] cookie:"+mSessionCookie);
Intent intent = new Intent();
intent.setAction(ImageSearchBuildService.ACTION);
intent.putExtra("cookie", mSessionCookie);
mContext.startService(intent);
mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
}

  //断开service
private void unBindService() {
try {
if (iServce != null) {
iServce.unregisterCallback(mCallback);
}
mContext.unbindService(conn);
} catch (Exception e) {
}
} public void findPersonInService(String path, int imageFilter) throws RemoteException
{
Log.i(TAG, "findPersonInService imageFilter:"+imageFilter);
if(iServce!=null)
{
iServce.findPerson(path, imageFilter);
}
} private void startHandleThread()
{
new Thread(this).start();
} private void exitHandleThread(){
if(mEngineLooper!=null)
{
mEngineLooper.quit();
mEngineLooper = null;
}
mEngineHandler = null;
mContentResolver = null;
} public boolean isDeviceSupportEngine()
{
return FaceRecognizeEngine.checkDeviceSupport();
} public void Release() {
unBindService();
unRegisterReceiver(); exitHandleThread();
Log.i(TAG, "[Release] done");
} public void abortSearchImageAsync()
{ } public void SearchImageAsyncHandle(String path,int imageFilter)
{
Log.i(TAG, "SearchImageAsyncHandle imageFilter"+imageFilter);
//send message to async all search image one by one
Message msg = mEngineHandler.obtainMessage();
msg.what = ImageSearchUtils.ENGINE_START_SEARCH;
Bundle data = new Bundle();
data.putString("path", path);
data.putInt("filter", imageFilter);
msg.setData(data);
mEngineHandler.sendMessage(msg);
} private void searchImageTask(String path,int imageFilter)
{
try{
if(mWorkQueue == null)
{
mWorkQueue = new SearchWorkQueue("clientEngine",this);
}
mWorkQueue.prepareWorkQueue(imageFilter, path);
mWorkQueue.query();
}catch (Exception e) {
// TODO: handle exception
}
} public void searhImageQueueHandle()
{
Message msg = mEngineHandler.obtainMessage();
msg.what = ImageSearchUtils.ENGINE_SEARCH_QUEUE;
mEngineHandler.sendMessage(msg);
} private void searchImageQueueTask()
{
if(mWorkQueue!=null)
{
mWorkQueue.query();
}
} public void onSearchResult(String path, ArrayList<GenericResultItem> itmes)
{
if(mListener!=null)
{
mListener.onSearchResult(path, itmes);
}
} @Override
public void onReceive(Context context, Intent intent) {
} public void registerBroadCast()
{ } public void unRegisterReceiver()
{
} public void decodeUriToBitmapHandle(Uri uri)
{
Message msg = mEngineHandler.obtainMessage();
msg.what = ImageSearchUtils.ENGINE_OP_DEOCDE_URI;
Bundle data = new Bundle();
data.putParcelable("uri", uri);
msg.setData(data);
mEngineHandler.sendMessage(msg);
} public void prepareSrcImageItemsHandle()
{
Message msg = mEngineHandler.obtainMessage();
msg.what = ImageSearchUtils.ENGINE_OP_PREPARE_SRC_APP;
mEngineHandler.sendMessage(msg);
} private void decodeUriTask(Uri uri) {
try {
Log.i(TAG, "DecodeUriTask uri" + uri.toString());
String[] projects = new String[1];
projects[0] = MediaStore.Images.Media.DATA;
Cursor cursor = mContentResolver.query(uri, projects, null, null,
null);
String path = null;
if (cursor != null) {
while (cursor.moveToNext()) {
path = cursor.getString(cursor
.getColumnIndex(MediaStore.Images.Media.DATA));
break;
}
cursor.close();
} if (!path.isEmpty()) {
Bitmap bitmap = BitmapFactory.decodeFile(path);
if (mListener != null) {
mListener.onEngineDecodeUri(bitmap, path);
}
} else { if (mListener != null) {
mListener.onEngineDecodeUri(null, null);
}
} } catch (Exception e) {
Log.i(TAG, "DecodeUriTask error:" + e.getMessage());
if (mListener != null) {
mListener.onEngineDecodeUri(null, null);
}
} finally {
}
} private void prepareSrcSelectItemTask()
{
try {
PackageManager pm = mContext.getPackageManager();
Intent mainIntent = new Intent(ImageSearchUtils.ACTION_CAMERA_PICKER);
List<ResolveInfo> resolveInfos = pm
.queryIntentActivities(mainIntent, PackageManager.MATCH_DEFAULT_ONLY);
ArrayList<ListAdapterInfo> infos = new ArrayList<ListAdapterInfo>(2);
if(!resolveInfos.isEmpty()&& resolveInfos.size()>0)
{
Log.i(TAG, "PrepareSrcSelectItemTask Camera is find");
ResolveInfo reInfo = resolveInfos.get(0);
String appLabel = (String) reInfo.loadLabel(pm); // 获得应用程序的Label
Drawable icon = reInfo.loadIcon(pm); // 获得应用程序图标
ListAdapterInfo info = new ListAdapterInfo(appLabel,icon,ImageSearchUtils.REQUEST_CAMERA_PICK);
infos.add(info);
} Intent mainIntent2 = new Intent(ImageSearchUtils.ACTION_GALLERY_PICKER);
List<ResolveInfo> resolveInfos2 = pm
.queryIntentActivities(mainIntent2, PackageManager.MATCH_DEFAULT_ONLY);
if(!resolveInfos2.isEmpty() && resolveInfos2.size()>0)
{
Log.i(TAG, "PrepareSrcSelectItemTask Gallery is find");
ResolveInfo reInfo2 = resolveInfos2.get(0);
// String activityName2 = reInfo2.activityInfo.name; // 获得该应用程序的启动Activity的name
// String pkgName2 = reInfo2.activityInfo.packageName; // 获得应用程序的包名
String appLabel2 = (String) reInfo2.loadLabel(pm); // 获得应用程序的Label
Drawable icon2 = reInfo2.loadIcon(pm); // 获得应用程序图标
ListAdapterInfo info2 = new ListAdapterInfo(appLabel2,icon2,ImageSearchUtils.REQUEST_GALLERY_PICK);
infos.add(info2);
}
if(mListener!=null)
{
mListener.onPrepareActivityInfo(infos);
} } catch (Exception e) {
Log.i(TAG, "PrepareSrcSelectItemTask error:"+e.getMessage());
}
finally{
}
} public static class EngineHandler extends Handler{ private SearchEngine mSearchEngine = null;
public EngineHandler(Looper loop, SearchEngine engine)
{
super(loop);
mSearchEngine = engine;
} @Override
public void handleMessage(Message msg) {
switch(msg.what)
{
case ImageSearchUtils.ENGINE_OP_DEOCDE_URI:
Bundle datauri = msg.getData();
Uri uri = datauri.getParcelable("uri");
mSearchEngine.decodeUriTask(uri);
break;
case ImageSearchUtils.ENGINE_OP_PREPARE_SRC_APP:
mSearchEngine.prepareSrcSelectItemTask();
break;
case ImageSearchUtils.ENGINE_START_SEARCH:
Bundle data = msg.getData();
String path = data.getString("path");
int imageFilter = data.getInt("filter");
mSearchEngine.searchImageTask(path, imageFilter);
break;
case ImageSearchUtils.ENGINE_SEARCH_QUEUE:
mSearchEngine.searchImageQueueTask();
break;
} } } @Override
public void run() {
Looper.prepare();
mEngineLooper = Looper.myLooper();
mEngineHandler = new EngineHandler(mEngineLooper,this);
Log.i(TAG, "frontground engine handle running TID:"+Process.myTid());
Looper.loop();
} private long generateCookie()
{
return System.currentTimeMillis();
} }

onstartcommand返回类型?

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

START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。

START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
START_STICKY的兼容版本,但不保证服务被kill后一定能重启。