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

下面介绍远程服务和通过远程回调的方式通知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后一定能重启。

Android四大组件之Service(续)的更多相关文章

  1. Android四大组件之Service(续2)

    1.HttpRequest package com.joyfulmath.android4example.http; import java.io.BufferedReader; import jav ...

  2. Android四大组件之Service

    Android四大组件之Service Android支持服务的概念,服务是在后台运行的组件,没有用户界面,Android服务可用有与活动独立的生命周期.Android支持两种类型的服务: 本地服务: ...

  3. 【Android开发日记】之入门篇(五)——Android四大组件之Service

    这几天忙着驾校考试,连电脑都碰不到了,今天总算告一段落了~~Service作为Android的服务组件,默默地在后台为整个程序服务,辅助应用与系统中的其他组件或系统服务进行沟通.它跟Activity的 ...

  4. Android成长日记-Android四大组件之Service组件的学习

    1.什么是Service? Service是Android四大组件中与Activity最相似的组件,它们都代表可执行的程序,Service与Activity的区别在于:Service一直在后台运行,它 ...

  5. Android 四大组件之service与Broadcast

    Android 四大组件之一:service: Service有五个生命周期:onCreat,onStartCommand, onBind,onUnbind, onDestroy 主要有绑定和非绑定两 ...

  6. Android四大组件之一Service介绍-android学习之旅(十二)

    基本概念: service是android四大组件之一,运行在后台执行耗时操作,并不提供用户界面.其他组件如acticity可以通过startService启动该组件,也可以通过bindService ...

  7. Android四大组件之一 -- Service详解

    相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的Android程序员如果连Service都没听说过的话,那确实也太逊了.Service作为Android四大组件之一,在每一个应用程序 ...

  8. Android四大组件:Service

    前言 Service作为Android四大组件之一,应用非常广泛 本文将介绍对Service进行全面介绍(基础认识.生命周期.使用和应用场景) 目录 目录 1. 基础知识 定义:服务,属于Androi ...

  9. Android四大组件之Service浅见

    Service 是Android四大组件之一,可以在不显示界面的情况下在后台运行.还有一个作用是通过AIDL来实现进程间通信. Service的启动方式 Service的启动方式有两种,startSe ...

随机推荐

  1. slqite3练习

    连接 import sqlite3 con = sqlite3.connect(":memory:") c = con.cursor() # Create table c.exec ...

  2. 【RAC搭建报错】libcap.so.1:cannot open shared object file

    原文参考:http://blog.csdn.net/siyanyanyanyai/article/details/45306595 http://orax.blog.sohu.com/26207226 ...

  3. const与readonly常量

    const与readonly常量 const与readonly都是用来定义常量,但是它们有什么区别呢? 下面我们来简要的说明一下: const修饰的常量是编译时常量,如:public const St ...

  4. MySQL☞视图

    emmm,我本来最先也没注意到视图,然后再某个群里突然说起了视图,吓得本菜鸟赶紧连牛的不敢吹了,只好去科普一下,才好继续去吹牛. 什么是视图: 视图是一张虚拟的表,从视图中查看一张或多张表中的数据. ...

  5. PHP版本的讲解

    原文地址:http://dev.meettea.com/show-90-1.html 最近发现很多PHP程序员对PHP版本知识了解不是很清楚,其中不乏PHP产品主力开发人员. PHP版本主要分三支:P ...

  6. Spring学习(3):Spring概述(转载)

    1. Spring是什么? Spring是一个开源的轻量级Java SE(Java 标准版本)/Java EE(Java 企业版本)开发应用框架,其目的是用于简化企业级应用程序开发. 在面向对象思想中 ...

  7. 宿主机ssh免密登录docker容器

    一.检查系统内核 二.安装docker 1.yum install docker  -y 2.docker version                    #查看docker版本 3.syste ...

  8. CSS 实用实例

    背景颜色 1. 颜色背景 <style type="text/css">body { font-size: 16px;">h1 { font-size: ...

  9. Serverless 架构的优点和缺点

    Serverless 的优势 在我使用 Serverless Framework 开发 AWS Serverless 应用的过程中,最方便的莫过于,第一次部署和第二次.第三次部署没有什么区别.只需要执 ...

  10. apply新用法,最大值查找

    要找到数组中的最大或最小值,可以像下面这样使用apply() var values=[1,2,3,4,5,6,7,8]; var max = Math.max.apply(Math,values); ...