当前位置: 首页 > news >正文

怎样在网站上做友情链接天元建设集团有限公司单位性质

怎样在网站上做友情链接,天元建设集团有限公司单位性质,建筑业服务平台,深圳网站建设公司哪家可以建app一. 了解服务(Service)的概念 service是安卓开发中一个很重要组件,意为“服务”。与我们常见的activity不同,“服务”是默默的在背后进行工作的,通常,它用于在后台为我们执行一些耗时,或者需要…

一. 了解服务(Service)的概念

service是安卓开发中一个很重要组件,意为“服务”。与我们常见的activity不同,“服务”是默默的在背后进行工作的,通常,它用于在后台为我们执行一些耗时,或者需要长时间执行的一些操作的。通常的,我们使用 Intent来启动一个服务(需要在manifest文件中注册,也可以像注册activity一样,给它分配进程)。Service可以不需要UI就在后台运行,不用管开启它的页面是否被销毁,只要进程还在就可以在后台运行。

 - Service生命周期:


public class MyService extends Service {@Overridepublic void onCreate() {super.onCreate();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {return startCommandReturnId;}@Overridepublic void onDestroy() {super.onDestroy();}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}
}

 二. 问题背景

产品需要在我们的业务启动之后,在状态栏展示一个“xx应用正在进行中”中的一个通知,同时,要求app退后台后也不能被结束,即需要保活。于是,我们将目光投向了service

....// 业务启动流程结束
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && (guildInfo != null)) {getApp().applicationContext.startForegroundService(serviceIntent)
} else {getApp().applicationContext.startService(serviceIntent)}
// Service类中
public int onStartCommand(Intent intent, int flags, int startId) {....
mAudioNotification = createAudioNotification(); //创建通知栏展示内容
startForeground(NID, mAudioNotification); // 展示服务通知....
}

 于是我们的业务中出现了这样的代码,乍看之下 好像也没什么问题。可是外发之后,收到的crash反馈却只增不减。

Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord

 这类堆栈引起了我们的注意。原来我们使用了新提供的 startForegroundService,而这个API比较特殊,它要求我们在调用之后,收到 onStartCommand 回调后 5s内必须调用 startForeground, 否则会有ANR,而如果在调用 startForeground 之前,调用了 stopService 或者 stopSelf ,则会直接抛出 crash 我们这里问题的原因就是,在startForeground之前,调用了 stopService。问题找到了,是业务自己调用导致的。

但,似乎这个API没有提供给我们一个可以合适取消service的时机呢。既然这个API 限制这么多,我们又为什么要选择它呢?它和之前常用的startService又有什么区别呢?

三. 源代码分析

@Overridepublic ComponentName startService(Intent service) {warnIfCallingFromSystemProcess();return startServiceCommon(service, false, mUser);}@Overridepublic ComponentName startForegroundService(Intent service) {warnIfCallingFromSystemProcess();return startServiceCommon(service, true, mUser);}private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user)

 这两个API最终的都是调用的 startServiceCommon,区别只在于 其中的参数 requireForeground 字段赋值不同。那么这个字段究竟做了哪些处理呢?

在Android8.0的行为变更说明中,我们看到,不在允许随意创建 后台服务,所以改为 调用 startForegroundService的形式。

 

  1.  不满足条件(如:O以上版本后台启动服务)调用startService会抛异常
private ComponentName startServiceCommon(Intent service, boolean requireForeground,UserHandle user) {try {validateServiceIntent(service);service.prepareToLeaveProcess(this);ComponentName cn = ActivityManager.getService().startService(mMainThread.getApplicationThread(), service,service.resolveTypeIfNeeded(getContentResolver()), requireForeground,getOpPackageName(), getAttributionTag(), user.getIdentifier());if (cn != null) {// 异常匹配if (cn.getPackageName().equals("!")) {throw new SecurityException("Not allowed to start service " + service+ " without permission " + cn.getClassName());} else if (cn.getPackageName().equals("!!")) {throw new SecurityException("Unable to start service " + service+ ": " + cn.getClassName());} else if (cn.getPackageName().equals("?")) {throw ServiceStartNotAllowedException.newInstance(requireForeground,"Not allowed to start service " + service + ": " + cn.getClassName());}}....return cn;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

if (forcedStandby || (!r.startRequested && !fgRequired)) {// 调用startService,(!r.startRequested && !fgRequired) 条件为truefinal int allowed = mAm.getAppStartModeLOSP(r.appInfo.uid, r.packageName,r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);if (allowed != ActivityManager.APP_START_MODE_NORMAL) {if (allowed == ActivityManager.APP_START_MODE_DELAYED || forceSilentAbort) {return null;}if (forcedStandby) {if (fgRequired) {return null;}}UidRecord uidRec = mAm.mProcessList.getUidRecordLOSP(r.appInfo.uid);return new ComponentName("?", "app is in background uid " + uidRec);}}

// Unified app-op and target sdk check@GuardedBy(anyOf = {"this", "mProcLock"})int appRestrictedInBackgroundLOSP(int uid, String packageName, int packageTargetSdk) {// 安卓8限制if (packageTargetSdk >= Build.VERSION_CODES.O) {if (DEBUG_BACKGROUND_CHECK) {Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");}return ActivityManager.APP_START_MODE_DELAYED_RIGID;}.....}

2.  提高服务优先级

我们知道,在安卓系统内存紧张时,前台应用是有高优先级的,不会被清理掉。所以,我们需要将“不可见”的服务 升级为前台服务,前台服务是被认为用于已知的正在运行的服务,当系统需要释放内存时不会优先杀掉该进程。

所以 我们在启动service后,收到onStartCommand时,调用 startForeground,同时传入需要展示在前台的notification

3.  为什么调用startForgroundService后,再调用stop或者没有及时调用startForeground会crash/ANR呢?

startServiceCommon

-> AMS.startService

-> ActiveServices.startServiceLocked

-> startServiceInnerLocked

-> bringUpServiceLocked

->realStartServiceLocked

-> sendServiceArgsLocked

private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,boolean oomAdjusted) throws TransactionTooLargeException {...ArrayList<ServiceStartArgs> args = new ArrayList<>();while (r.pendingStarts.size() > 0) {ServiceRecord.StartItem si = r.pendingStarts.remove(0);...if (r.fgRequired && !r.fgWaiting) {if (!r.isForeground) {<!--监听是否5S内startForeground-->scheduleServiceForegroundTransitionTimeoutLocked(r);} ...try {r.app.thread.scheduleServiceArgs(r, slice);}
void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {if (r.app.mServices.numberOfExecutingServices() == 0 || r.app.getThread() == null) {return;}Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG);msg.obj = r;r.fgWaiting = true;mAm.mHandler.sendMessageDelayed(msg, mAm.mConstants.mServiceStartForegroundTimeoutMs);}
/*** How long the Context.startForegroundService() grace period is to get around to* calling Service.startForeground() before we generate ANR.*/volatile int mServiceStartForegroundTimeoutMs = DEFAULT_SERVICE_START_FOREGROUND_TIMEOUT_MS;

 修改 r.fgWaiting = true启动任务延迟TimeoutMs后发送 SERVICE_FOREGROUND_TIMEOUT_MSG

// handler处理 SERVICE_FOREGROUND_TIMEOUT_MSG
void serviceForegroundTimeout(ServiceRecord r) {ProcessRecord app;synchronized (mAm) {if (!r.fgRequired || !r.fgWaiting || r.destroying) {return;}app = r.app;if (app != null && app.isDebugging()) {// The app's being debugged; let it ridereturn;}if (DEBUG_BACKGROUND_CHECK) {Slog.i(TAG, "Service foreground-required timeout for " + r);}r.fgWaiting = false;stopServiceLocked(r, false);}if (app != null) {
// 就是我们之前遇到的异常final String annotation = "Context.startForegroundService() did not then call "+ "Service.startForeground(): " + r;Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_ANR_MSG);SomeArgs args = SomeArgs.obtain();args.arg1 = app;args.arg2 = annotation;msg.obj = args;mAm.mHandler.sendMessageDelayed(msg,mAm.mConstants.mServiceStartForegroundAnrDelayMs);}}

 如果在没有调用startForegroun前调用了stop,则会抛出 SERVICE_FOREGROUND_CRASH_MSG 的msg

private final void bringDownServiceLocked(ServiceRecord r) {...if (r.fgRequired) {r.fgRequired = false;r.fgWaiting = false;mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);if (r.app != null) {Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);msg.obj = r.app;msg.getData().putCharSequence(ActivityManagerService.SERVICE_RECORD_KEY, r.toString());mAm.mHandler.sendMessage(msg);}}

解决方案 -  startForegroundService后 必须按照要求调用 startForground 😊

四. 总结

笔者的业务场景下 其实不需要用这么严格的API,正常的在 应用处于前台时,启动前台服务 就按照常用的startService -> startForeground 调用即可。如果一定需要使用 startForegroundService 就要注意到以上的几个问题。

http://www.yayakq.cn/news/282371/

相关文章:

  • 视频网站seo实战肃宁县网站建设
  • 茶文化网站制作网站介绍经过下拉怎么做
  • 西安网站开发建郑州市做网站
  • 北京市工程建设信息交易网站哪些网站做外贸效果好
  • 邓州网站优化品牌策划案模板
  • 做网站的素材哪里找的广西新闻最新消息今天
  • 平台型网站建设预算表网站seo策略
  • 涉县专业做网站单页面网站如何seo
  • 网站制作 牛商网网站正在建设中中文模板
  • 南阳网站建设培训h5开发环境
  • 唐山网站制作方案长沙手机网站首页设计公司
  • 公司做网站要注意什么别墅装修设计图片大全 效果图
  • 做打鱼网站犯法不成熟的国产crm系统
  • 网站做404网站建设维护杭州
  • 易销云建站公司国家信息企业公示系统查询
  • 二维码怎么做网站网站访问量
  • 网站建设系统设计产品软文范例1000字
  • 企业网站模板下载哪家好徐州网站建设优化宣传
  • 公司建一个网站吗贵阳好的网站建设公司
  • 襄阳做网站排行榜wordpress二维码插件付费
  • 做网站的需求实验室网站建设
  • 智能模板网站建设收费骑行网站模板
  • form e哪个网站做商城网站不易优化
  • 网站建设业务方法昆明网站建设服务至上
  • 网站域名注册价格手机设计房子的软件3d下载
  • 中国建设银行手机网站首页wordpress导航菜单加图片
  • 网站仿制做网站窗体属性栏设置文字居中
  • 使用 加速乐 网站变慢上海装饰公司网站建设
  • 网站建设价格一览表电子邮件免费注册
  • 建设公司起名哪个网站好浦东新区手机网站设计