Android ANR原理分析

2024-05-27 11:18
文章标签 分析 android 原理 anr

本文主要是介绍Android ANR原理分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、概述

ANR(Application Not responding),是指应用程序未响应,Android系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成ANR。一般地,这时往往会弹出一个提示框,告知用户当前xxx未响应,用户可选择继续等待或者Force Close。

那么哪些场景会造成ANR呢?

  • Service Timeout:服务在20s内未执行完成;
  • BroadcastQueue Timeout:比如前台广播在10s内执行完成
  • ContentProvider Timeout:内容提供者执行超时
  • inputDispatching Timeout: 输入事件分发超时5s,包括按键分发事件的超时。

二、ANR触发时机

2.1 Service Timeout

Service Timeout触发时机,简单说就是AMS中的mHandler收到SERVICE_TIMEOUT_MSG消息时触发。

在前面文章startService流程分析详细介绍Service启动流程,在Service所在进程attach到system_server进程的过程中会调用realStartServiceLocked()方法

2.1.1 realStartServiceLocked

[-> ActiveServices.java]

private final void realStartServiceLocked(ServiceRecord r,ProcessRecord app, boolean execInFg) throws RemoteException {...//发送delay消息(SERVICE_TIMEOUT_MSG),【见小节2.1.2】bumpServiceExecutingLocked(r, execInFg, "create");try {...//最终执行服务的onCreate()方法app.thread.scheduleCreateService(r, r.serviceInfo,mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),app.repProcState);} catch (DeadObjectException e) {...} finally {if (!created) {//当service启动完毕,则remove SERVICE_TIMEOUT_MSG消息【见小节2.1.3】serviceDoneExecutingLocked(r, inDestroying, inDestroying);...}}
}

2.1.2 bumpServiceExecutingLocked

该方法的主要工作发送delay消息(SERVICE_TIMEOUT_MSG)

private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {...scheduleServiceTimeoutLocked(r.app);
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {if (proc.executingServices.size() == 0 || proc.thread == null) {return;}long now = SystemClock.uptimeMillis();Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_TIMEOUT_MSG);msg.obj = proc;//当超时后仍没有remove该SERVICE_TIMEOUT_MSG消息,则执行service Timeout流程【见2.1.4】mAm.mHandler.sendMessageAtTime(msg,proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}
  • 对于前台服务,则超时为SERVICE_TIMEOUT,即timeout=20s;
  • 对于后台服务,则超时为SERVICE_BACKGROUND_TIMEOUT,即timeout=200s;

2.1.3 serviceDoneExecutingLocked

该方法的主要工作是当service启动完成,则移除service Timeout消息。

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,boolean finishing) {...if (r.executeNesting <= 0) {if (r.app != null) {r.app.execServicesFg = false;r.app.executingServices.remove(r);if (r.app.executingServices.size() == 0) {//当前服务所在进程中没有正在执行的servicemAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);...}...
}

2.1.4 SERVICE_TIMEOUT_MSG

到此不难理解,当SERVICE_TIMEOUT_MSG消息成功发送时,则AMS中的mHandler收到该消息则触发调用serviceTimeout

final class MainHandler extends Handler {public void handleMessage(Message msg) {switch (msg.what) {case SERVICE_TIMEOUT_MSG: {...//【见小节2.1.5】mServices.serviceTimeout((ProcessRecord)msg.obj);} break;...}...}
}

2.1.5 serviceTimeout

[-> ActiveServices.java]

void serviceTimeout(ProcessRecord proc) {String anrMessage = null;synchronized(mAm) {if (proc.executingServices.size() == 0 || proc.thread == null) {return;}final long now = SystemClock.uptimeMillis();final long maxTime =  now -(proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);ServiceRecord timeout = null;long nextTime = 0;for (int i=proc.executingServices.size()-1; i>=0; i--) {ServiceRecord sr = proc.executingServices.valueAt(i);if (sr.executingStart < maxTime) {timeout = sr;break;}if (sr.executingStart > nextTime) {nextTime = sr.executingStart;}}if (timeout != null && mAm.mLruProcesses.contains(proc)) {Slog.w(TAG, "Timeout executing service: " + timeout);StringWriter sw = new StringWriter();PrintWriter pw = new FastPrintWriter(sw, false, 1024);pw.println(timeout);timeout.dump(pw, "    ");pw.close();mLastAnrDump = sw.toString();mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);anrMessage = "executing service " + timeout.shortName;}}if (anrMessage != null) {//当存在timeout的service,则执行appNotResponding【见小节3.1】mAm.appNotResponding(proc, null, null, false, anrMessage);}
}

其中anrMessage的内容为”executing service [发送超时serviceRecord信息]”;

2.2 BroadcastQueue Timeout

BroadcastQueue Timeout触发时机,简单说就是BroadcastQueue中的mHandler收到BROADCAST_TIMEOUT_MSG消息时触发。

在前面文章Android Broadcast广播机制分析详细介绍广播启动流程,在发送广播过程中会执行scheduleBroadcastsLocked方法来处理相关的广播,然后会调用到processNextBroadcast方法来处理下一条广播。

processNextBroadcast执行过程分4步骤:

  • step1. 处理并行广播
  • step2. 处理当前有序广播
  • step3. 获取下条有序广播
  • step4. 处理下条有序广播

2.2.1 processNextBroadcast

[-> BroadcastQueue.java]

final void processNextBroadcast(boolean fromMsg) {synchronized(mService) {...//step 2: 处理当前有序广播do {r = mOrderedBroadcasts.get(0);//获取所有该广播所有的接收者int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;if (mService.mProcessesReady && r.dispatchTime > 0) {long now = SystemClock.uptimeMillis();if ((numReceivers > 0) &&(now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {//当广播处理时间超时,则强制结束这条广播【见小节2.2.5】broadcastTimeoutLocked(false);...}}if (r.receivers == null || r.nextReceiver >= numReceivers|| r.resultAbort || forceReceive) {if (r.resultTo != null) {//处理广播消息消息performReceiveLocked(r.callerApp, r.resultTo,new Intent(r.intent), r.resultCode,r.resultData, r.resultExtras, false, false, r.userId);r.resultTo = null;}//取消BROADCAST_TIMEOUT_MSG消息【见小节2.2.3】cancelBroadcastTimeoutLocked();}} while (r == null);...//step 3: 获取下条有序广播r.receiverTime = SystemClock.uptimeMillis();if (!mPendingBroadcastTimeoutMessage) {long timeoutTime = r.receiverTime + mTimeoutPeriod;//设置广播超时时间,发送BROADCAST_TIMEOUT_MSG【见小节2.2.2】setBroadcastTimeoutLocked(timeoutTime);}...}
}

对于广播超时处理时机:

  1. 首先在step3的过程中setBroadcastTimeoutLocked(timeoutTime) 设置超时广播消息;
  2. 然后在step2根据广播处理情况来处理:
    • 当广播接收者等待时间过长,则调用broadcastTimeoutLocked(false);
    • 当,cancelBroadcastTimeoutLocked

2.2.2 setBroadcastTimeoutLocked

final void setBroadcastTimeoutLocked(long timeoutTime) {if (! mPendingBroadcastTimeoutMessage) {Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);mHandler.sendMessageAtTime(msg, timeoutTime);mPendingBroadcastTimeoutMessage = true;}
}

设置定时广播BROADCAST_TIMEOUT_MSG,即当前往后推mTimeoutPeriod时间广播还没处理完毕,则进入广播超时流程。

  • 对于前台广播,则超时为BROADCAST_FG_TIMEOUT,即timeout=10s;
  • 对于后台广播,则超时为BROADCAST_BG_TIMEOUT,即timeout=60s。

2.2.3 cancelBroadcastTimeoutLocked

final void cancelBroadcastTimeoutLocked() {if (mPendingBroadcastTimeoutMessage) {mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);mPendingBroadcastTimeoutMessage = false;}
}

移除广播超时消息BROADCAST_TIMEOUT_MSG

2.2.4 BROADCAST_TIMEOUT_MSG

到此不难理解,当BROADCAST_TIMEOUT_MSG消息成功发送时,则AMS中的mHandler收到该消息则触发调用serviceTimeout

private final class BroadcastHandler extends Handler {public void handleMessage(Message msg) {switch (msg.what) {case BROADCAST_TIMEOUT_MSG: {synchronized (mService) {//【见小节2.2.5】broadcastTimeoutLocked(true);}} break;...}...}
}

2.2.5 broadcastTimeoutLocked

[-> BroadcastRecord.java]

final void broadcastTimeoutLocked(boolean fromMsg) {if (fromMsg) {mPendingBroadcastTimeoutMessage = false;}if (mOrderedBroadcasts.size() == 0) {return;}long now = SystemClock.uptimeMillis();BroadcastRecord r = mOrderedBroadcasts.get(0);if (fromMsg) {if (mService.mDidDexOpt) {//延迟timeouts直到dexopt结束mService.mDidDexOpt = false;long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;setBroadcastTimeoutLocked(timeoutTime);return;}if (!mService.mProcessesReady) {//当系统还没有准备就绪时,广播处理流程中不存在广播超时return;}long timeoutTime = r.receiverTime + mTimeoutPeriod;if (timeoutTime > now) {//过早的timeout,重新设置广播超时setBroadcastTimeoutLocked(timeoutTime);return;}}BroadcastRecord br = mOrderedBroadcasts.get(0);if (br.state == BroadcastRecord.WAITING_SERVICES) {//广播已经处理完成,但需要等待已启动service执行完成。当等待足够时间,则处理下一条广播。br.curComponent = null;br.state = BroadcastRecord.IDLE;processNextBroadcast(false);return;}r.receiverTime = now;//当前BroadcastRecord的anr次数执行加1操作r.anrCount++;if (r.nextReceiver <= 0) {return;}ProcessRecord app = null;String anrMessage = null;Object curReceiver = r.receivers.get(r.nextReceiver-1);//根据情况记录广播接收者丢弃的EventLoglogBroadcastReceiverDiscardLocked(r);if (curReceiver instanceof BroadcastFilter) {BroadcastFilter bf = (BroadcastFilter)curReceiver;if (bf.receiverList.pid != 0&& bf.receiverList.pid != ActivityManagerService.MY_PID) {synchronized (mService.mPidsSelfLocked) {app = mService.mPidsSelfLocked.get(bf.receiverList.pid);}}} else {app = r.curApp;}if (app != null) {anrMessage = "Broadcast of " + r.intent.toString();}if (mPendingBroadcast == r) {mPendingBroadcast = null;}//继续移动到下一个广播接收者finishReceiverLocked(r, r.resultCode, r.resultData,r.resultExtras, r.resultAbort, false);scheduleBroadcastsLocked();if (anrMessage != null) {//【见小节2.2.6】mHandler.post(new AppNotResponding(app, anrMessage));}
}

2.2.6 AppNotResponding

[-> BroadcastQueue.java]

private final class AppNotResponding implements Runnable {...public void run() {//【见小节3.1】mService.appNotResponding(mApp, null, null, false, mAnnotation);}
}

2.3 ContentProvider Timeout

2.3.1 AMS.appNotRespondingViaProvider

public void appNotRespondingViaProvider(IBinder connection) {enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS, "appNotRespondingViaProvider()");final ContentProviderConnection conn = (ContentProviderConnection) connection;if (conn == null) {return;}final ProcessRecord host = conn.provider.proc;//无法找到provider所处的进程if (host == null) {return;}final long token = Binder.clearCallingIdentity();try {//【见小节3.1】appNotResponding(host, null, null, false, "ContentProvider not responding");} finally {Binder.restoreCallingIdentity(token);}
}

Timeout时间20s

调用链:

ContentProviderClient.NotRespondingRunnable.runContextImpl.ApplicationContentResolver.appNotRespondingViaProviderActivityThread.appNotRespondingViaProviderAMP.appNotRespondingViaProviderAMS.appNotRespondingViaProvider

2.4 inputDispatching Timeout

在native层InputDispatcher.cpp中经过层层调用,此处先省略过程,后续再展开,从native层com_android_server_input_InputManagerService调用到java层InputManagerService。

2.4.1 IMS.notifyANR

[-> InputManagerService.java]

private long notifyANR(InputApplicationHandle inputApplicationHandle,InputWindowHandle inputWindowHandle, String reason) {//【见小节2.4.2】return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, inputWindowHandle, reason);
}

mWindowManagerCallbacks为InputMonitor对象

2.4.2 notifyANR

[-> InputMonitor.java]

public long notifyANR(InputApplicationHandle inputApplicationHandle,InputWindowHandle inputWindowHandle, String reason) {AppWindowToken appWindowToken = null;WindowState windowState = null;boolean aboveSystem = false;synchronized (mService.mWindowMap) {if (inputWindowHandle != null) {windowState = (WindowState) inputWindowHandle.windowState;if (windowState != null) {appWindowToken = windowState.mAppToken;}}if (appWindowToken == null && inputApplicationHandle != null) {appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;}//输出input事件分发超时logif (windowState != null) {Slog.i(WindowManagerService.TAG, "Input event dispatching timed out "+ "sending to " + windowState.mAttrs.getTitle()+ ".  Reason: " + reason);int systemAlertLayer = mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);aboveSystem = windowState.mBaseLayer > systemAlertLayer;} else if (appWindowToken != null) {Slog.i(WindowManagerService.TAG, "Input event dispatching timed out "+ "sending to application " + appWindowToken.stringName+ ".  Reason: " + reason);} else {Slog.i(WindowManagerService.TAG, "Input event dispatching timed out "+ ".  Reason: " + reason);}mService.saveANRStateLocked(appWindowToken, windowState, reason);}if (appWindowToken != null && appWindowToken.appToken != null) {//【见小节2.5.1】boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(reason);if (! abort) {return appWindowToken.inputDispatchingTimeoutNanos;}} else if (windowState != null) {//AMP经过binder,最终调用到AMS【见小节2.4.3】long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut(windowState.mSession.mPid, aboveSystem, reason);if (timeout >= 0) {return timeout * 1000000L; //转化为纳秒}}return 0;
}

2.4.3 AMS.inputDispatchingTimedOut

public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {...ProcessRecord proc;long timeout;synchronized (this) {synchronized (mPidsSelfLocked) {proc = mPidsSelfLocked.get(pid); //根据pid查看进程record}timeout = getInputDispatchingTimeoutLocked(proc);}//【见小节2.4.4】if (!inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {return -1;}return timeout;
}

inputDispatching的超时为KEY_DISPATCHING_TIMEOUT,即timeout = 5s

2.4.4 AMS.inputDispatchingTimedOut

public boolean inputDispatchingTimedOut(final ProcessRecord proc,final ActivityRecord activity, final ActivityRecord parent,final boolean aboveSystem, String reason) {...final String annotation;if (reason == null) {annotation = "Input dispatching timed out";} else {annotation = "Input dispatching timed out (" + reason + ")";}if (proc != null) {...mHandler.post(new Runnable() {public void run() {//【见小节3.1】appNotResponding(proc, activity, parent, aboveSystem, annotation);}});}return true;
}

调用链:

InputManagerService.notifyANRInputMonitor.notifyANRAMP.inputDispatchingTimedOutAMS.inputDispatchingTimedOut

2.5 keyDispatching Timeout

keyDispatching timout与inputDispatching Timeout流畅基本一致。

调用链:

InputManagerService.notifyANRInputMonitor.notifyANRActivityRecord.Token.keyDispatchingTimedOutAMS.inputDispatchingTimedOut

Token.keyDispatchingTimedOut

[-> ActivityRecord.java]

final class ActivityRecord {static class Token extends IApplicationToken.Stub {public boolean keyDispatchingTimedOut(String reason) {ActivityRecord r;ActivityRecord anrActivity;ProcessRecord anrApp;synchronized (mService) {r = tokenToActivityRecordLocked(this);if (r == null) {return false;}anrActivity = r.getWaitingHistoryRecordLocked();anrApp = r != null ? r.app : null;}return mService.inputDispatchingTimedOut(anrApp, anrActivity, r, false, reason);}...}
}

对于keyDispatching Timeout的ANR,当触发该类型ANR时,如果不再有输入事件,则不会弹出ANR对话框;只有在下一次input事件产生后5s才弹出ANR提示框。

三、ANR工作

3.1 appNotResponding

[-> ActivityManagerService.java]

final void appNotResponding(ProcessRecord app, ActivityRecord activity,ActivityRecord parent, boolean aboveSystem, final String annotation) {ArrayList<Integer> firstPids = new ArrayList<Integer>(5);SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);if (mController != null) {try {// 0 == continue, -1 = kill process immediatelyint res = mController.appEarlyNotResponding(app.processName, app.pid, annotation);if (res < 0 && app.pid != MY_PID) {app.kill("anr", true);}} catch (RemoteException e) {mController = null;Watchdog.getInstance().setActivityController(null);}}long anrTime = SystemClock.uptimeMillis();if (MONITOR_CPU_USAGE) {updateCpuStatsNow();}synchronized (this) {// PowerManager.reboot() 会阻塞很长时间,因此忽略关机时的ANRif (mShuttingDown) {Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);return;} else if (app.notResponding) {Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);return;} else if (app.crashing) {Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);return;}app.notResponding = true;//记录ANREventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,app.processName, app.info.flags, annotation);// Dump thread traces as quickly as we can, starting with "interesting" processes.firstPids.add(app.pid);int parentPid = app.pid;if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid;if (parentPid != app.pid) firstPids.add(parentPid);if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);for (int i = mLruProcesses.size() - 1; i >= 0; i--) {ProcessRecord r = mLruProcesses.get(i);if (r != null && r.thread != null) {int pid = r.pid;if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {if (r.persistent) {firstPids.add(pid);} else {lastPids.put(pid, Boolean.TRUE);}}}}}//输出ANR到main log.StringBuilder info = new StringBuilder();info.setLength(0);info.append("ANR in ").append(app.processName);if (activity != null && activity.shortComponentName != null) {info.append(" (").append(activity.shortComponentName).append(")");}info.append("\n");info.append("PID: ").append(app.pid).append("\n");if (annotation != null) {info.append("Reason: ").append(annotation).append("\n");}if (parent != null && parent != activity) {info.append("Parent: ").append(parent.shortComponentName).append("\n");}final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);//dump栈信息File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker, lastPids,NATIVE_STACKS_OF_INTEREST);String cpuInfo = null;if (MONITOR_CPU_USAGE) {updateCpuStatsNow();synchronized (mProcessCpuTracker) {//输出各个进程的CPU使用情况cpuInfo = mProcessCpuTracker.printCurrentState(anrTime);}//输出CPU负载info.append(processCpuTracker.printCurrentLoad());info.append(cpuInfo);}info.append(processCpuTracker.printCurrentState(anrTime));Slog.e(TAG, info.toString());if (tracesFile == null) {//发送signal 3来dump栈信息Process.sendSignal(app.pid, Process.SIGNAL_QUIT);}//将anr信息添加到dropboxaddErrorToDropBox("anr", app, app.processName, activity, parent, annotation,cpuInfo, tracesFile, null);if (mController != null) {try {// 0 == show dialog, 1 = keep waiting, -1 = kill process immediatelyint res = mController.appNotResponding(app.processName, app.pid, info.toString());if (res != 0) {if (res < 0 && app.pid != MY_PID) {app.kill("anr", true);} else {synchronized (this) {mServices.scheduleServiceTimeoutLocked(app);}}return;}} catch (RemoteException e) {mController = null;Watchdog.getInstance().setActivityController(null);}}boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;synchronized (this) {mBatteryStatsService.noteProcessAnr(app.processName, app.uid);if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {app.kill("bg anr", true);return;}// Set the app's notResponding state, and look up the errorReportReceivermakeAppNotRespondingLocked(app,activity != null ? activity.shortComponentName : null,annotation != null ? "ANR " + annotation : "ANR",info.toString());//弹出ANR对话框Message msg = Message.obtain();HashMap<String, Object> map = new HashMap<String, Object>();msg.what = SHOW_NOT_RESPONDING_MSG;msg.obj = map;msg.arg1 = aboveSystem ? 1 : 0;map.put("app", app);if (activity != null) {map.put("activity", activity);}mUiHandler.sendMessage(msg);}
}

主要发送ANR, 则会输出

  • 各个进程的CPU使用情况;
  • CPU负载;
  • IOWait;
  • traces文件

四、其他

导致ANR常见情形:

  • I/O阻塞
  • 网络阻塞;
  • onReceiver执行时间超过10s;
  • 多线程死锁

避免ANR:

  • UI线程尽量只做跟UI相关的工作
  • 耗时的工作()比如数据库操作,I/O,网络操作),采用单独的工作线程处理
  • 用Handler来处理UIthread和工作thread的交互

UI线程,例如:

  • Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick(),etc
  • AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel,etc
  • Mainthread handler: handleMessage(), post*(runnable r), etc

ANR分析:需要关注CPU/IO,trace死锁等数据。

这篇关于Android ANR原理分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1007325

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

hdu4407容斥原理

题意: 有一个元素为 1~n 的数列{An},有2种操作(1000次): 1、求某段区间 [a,b] 中与 p 互质的数的和。 2、将数列中某个位置元素的值改变。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.Inpu