Android Low memory killer

2024-03-31 07:32
文章标签 android memory low killer

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

http://blog.sina.com.cn/s/blog_4d66a3cb0100prfe.html


附上lowmemorykiller.c代码

/* drivers/misc/lowmemorykiller.c** The lowmemorykiller driver lets user-space specify a set of memory thresholds* where processes with a range of oom_score_adj values will get killed. Specify* the minimum oom_score_adj values in* /sys/module/lowmemorykiller/parameters/adj and the number of free pages in* /sys/module/lowmemorykiller/parameters/minfree. Both files take a comma* separated list of numbers in ascending order.** For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and* "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill* processes with a oom_score_adj value of 8 or higher when the free memory* drops below 4096 pages and kill processes with a oom_score_adj value of 0 or* higher when the free memory drops below 1024 pages.** The driver considers memory used for caches to be free, but if a large* percentage of the cached memory is locked this can be very inaccurate* and processes may not get killed until the normal oom killer is triggered.** Copyright (C) 2007-2008 Google, Inc.** This software is licensed under the terms of the GNU General Public* License version 2, as published by the Free Software Foundation, and* may be copied, distributed, and modified under those terms.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the* GNU General Public License for more details.**/#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/oom.h>
#include <linux/sched.h>
#include <linux/rcupdate.h>
#include <linux/notifier.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/swap.h>
#include <linux/fs.h>
#include <linux/cpuset.h>#ifdef CONFIG_HIGHMEM
#define _ZONE ZONE_HIGHMEM
#else
#define _ZONE ZONE_NORMAL
#endifstatic uint32_t lowmem_debug_level = 1;
static short lowmem_adj[6] = {0,1,6,12,
};
static int lowmem_adj_size = 4;
static int lowmem_minfree[6] = {3 * 512,	/* 6MB */2 * 1024,	/* 8MB */4 * 1024,	/* 16MB */16 * 1024,	/* 64MB */
};
static int lowmem_minfree_size = 4;
static int lmk_fast_run = 1;static unsigned long lowmem_deathpending_timeout;#define lowmem_print(level, x...)			\do {						\if (lowmem_debug_level >= (level))	\pr_info(x);			\} while (0)static int test_task_flag(struct task_struct *p, int flag)
{struct task_struct *t = p;do {task_lock(t);if (test_tsk_thread_flag(t, flag)) {task_unlock(t);return 1;}task_unlock(t);} while_each_thread(p, t);return 0;
}static DEFINE_MUTEX(scan_mutex);int can_use_cma_pages(gfp_t gfp_mask)
{int can_use = 0;int mtype = allocflags_to_migratetype(gfp_mask);int i = 0;int *mtype_fallbacks = get_migratetype_fallbacks(mtype);if (is_migrate_cma(mtype)) {can_use = 1;} else {for (i = 0;; i++) {int fallbacktype = mtype_fallbacks[i];if (is_migrate_cma(fallbacktype)) {can_use = 1;break;}if (fallbacktype == MIGRATE_RESERVE)break;}}return can_use;
}void tune_lmk_zone_param(struct zonelist *zonelist, int classzone_idx,int *other_free, int *other_file,int use_cma_pages)
{struct zone *zone;struct zoneref *zoneref;int zone_idx;for_each_zone_zonelist(zone, zoneref, zonelist, MAX_NR_ZONES) {zone_idx = zonelist_zone_idx(zoneref);if (zone_idx == ZONE_MOVABLE) {if (!use_cma_pages)*other_free -=zone_page_state(zone, NR_FREE_CMA_PAGES);continue;}if (zone_idx > classzone_idx) {if (other_free != NULL)*other_free -= zone_page_state(zone,NR_FREE_PAGES);if (other_file != NULL)*other_file -= zone_page_state(zone,NR_FILE_PAGES)- zone_page_state(zone, NR_SHMEM);} else if (zone_idx < classzone_idx) {if (zone_watermark_ok(zone, 0, 0, classzone_idx, 0)) {if (!use_cma_pages) {*other_free -= min(zone->lowmem_reserve[classzone_idx] +zone_page_state(zone, NR_FREE_CMA_PAGES),zone_page_state(zone, NR_FREE_PAGES));} else {*other_free -=zone->lowmem_reserve[classzone_idx];}} else {*other_free -=zone_page_state(zone, NR_FREE_PAGES);}}}
}#ifdef CONFIG_HIGHMEM
void adjust_gfp_mask(gfp_t *gfp_mask)
{struct zone *preferred_zone;struct zonelist *zonelist;enum zone_type high_zoneidx;if (current_is_kswapd()) {zonelist = node_zonelist(0, *gfp_mask);high_zoneidx = gfp_zone(*gfp_mask);first_zones_zonelist(zonelist, high_zoneidx, NULL,&preferred_zone);if (high_zoneidx == ZONE_NORMAL) {if (zone_watermark_ok_safe(preferred_zone, 0,high_wmark_pages(preferred_zone), 0,0))*gfp_mask |= __GFP_HIGHMEM;} else if (high_zoneidx == ZONE_HIGHMEM) {*gfp_mask |= __GFP_HIGHMEM;}}
}
#else
void adjust_gfp_mask(gfp_t *unused)
{
}
#endifvoid tune_lmk_param(int *other_free, int *other_file, struct shrink_control *sc)
{gfp_t gfp_mask;struct zone *preferred_zone;struct zonelist *zonelist;enum zone_type high_zoneidx, classzone_idx;unsigned long balance_gap;int use_cma_pages;gfp_mask = sc->gfp_mask;adjust_gfp_mask(&gfp_mask);zonelist = node_zonelist(0, gfp_mask);high_zoneidx = gfp_zone(gfp_mask);first_zones_zonelist(zonelist, high_zoneidx, NULL, &preferred_zone);classzone_idx = zone_idx(preferred_zone);use_cma_pages = can_use_cma_pages(gfp_mask);balance_gap = min(low_wmark_pages(preferred_zone),(preferred_zone->present_pages +KSWAPD_ZONE_BALANCE_GAP_RATIO-1) /KSWAPD_ZONE_BALANCE_GAP_RATIO);if (likely(current_is_kswapd() && zone_watermark_ok(preferred_zone, 0,high_wmark_pages(preferred_zone) + SWAP_CLUSTER_MAX +balance_gap, 0, 0))) {if (lmk_fast_run)tune_lmk_zone_param(zonelist, classzone_idx, other_free,other_file, use_cma_pages);elsetune_lmk_zone_param(zonelist, classzone_idx, other_free,NULL, use_cma_pages);if (zone_watermark_ok(preferred_zone, 0, 0, _ZONE, 0)) {if (!use_cma_pages) {*other_free -= min(preferred_zone->lowmem_reserve[_ZONE]+ zone_page_state(preferred_zone, NR_FREE_CMA_PAGES),zone_page_state(preferred_zone, NR_FREE_PAGES));} else {*other_free -=preferred_zone->lowmem_reserve[_ZONE];}} else {*other_free -= zone_page_state(preferred_zone,NR_FREE_PAGES);}lowmem_print(4, "lowmem_shrink of kswapd tunning for highmem ""ofree %d, %d\n", *other_free, *other_file);} else {tune_lmk_zone_param(zonelist, classzone_idx, other_free,other_file, use_cma_pages);if (!use_cma_pages) {*other_free -=zone_page_state(preferred_zone, NR_FREE_CMA_PAGES);}lowmem_print(4, "lowmem_shrink tunning for others ofree %d, ""%d\n", *other_free, *other_file);}
}static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
{struct task_struct *tsk;struct task_struct *selected = NULL;int rem = 0;int tasksize;int i;short min_score_adj = OOM_SCORE_ADJ_MAX + 1;int minfree = 0;int selected_tasksize = 0;short selected_oom_score_adj;int array_size = ARRAY_SIZE(lowmem_adj);int other_free;int other_file;unsigned long nr_to_scan = sc->nr_to_scan;if (nr_to_scan > 0) {if (mutex_lock_interruptible(&scan_mutex) < 0)return 0;}other_free = global_page_state(NR_FREE_PAGES);if (global_page_state(NR_SHMEM) + total_swapcache_pages() <global_page_state(NR_FILE_PAGES))other_file = global_page_state(NR_FILE_PAGES) -global_page_state(NR_SHMEM) -total_swapcache_pages();elseother_file = 0;tune_lmk_param(&other_free, &other_file, sc);if (lowmem_adj_size < array_size)array_size = lowmem_adj_size;if (lowmem_minfree_size < array_size)array_size = lowmem_minfree_size;for (i = 0; i < array_size; i++) {minfree = lowmem_minfree[i];if (other_free < minfree && other_file < minfree) {min_score_adj = lowmem_adj[i];break;}}if (nr_to_scan > 0)lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %hd\n",nr_to_scan, sc->gfp_mask, other_free,other_file, min_score_adj);rem = global_page_state(NR_ACTIVE_ANON) +global_page_state(NR_ACTIVE_FILE) +global_page_state(NR_INACTIVE_ANON) +global_page_state(NR_INACTIVE_FILE);if (nr_to_scan <= 0 || min_score_adj == OOM_SCORE_ADJ_MAX + 1) {lowmem_print(5, "lowmem_shrink %lu, %x, return %d\n",nr_to_scan, sc->gfp_mask, rem);if (nr_to_scan > 0)mutex_unlock(&scan_mutex);return rem;}selected_oom_score_adj = min_score_adj;rcu_read_lock();for_each_process(tsk) {struct task_struct *p;short oom_score_adj;if (tsk->flags & PF_KTHREAD)continue;/* if task no longer has any memory ignore it */if (test_task_flag(tsk, TIF_MM_RELEASED))continue;if (time_before_eq(jiffies, lowmem_deathpending_timeout)) {if (test_task_flag(tsk, TIF_MEMDIE)) {rcu_read_unlock();/* give the system time to free up the memory */msleep_interruptible(20);mutex_unlock(&scan_mutex);return 0;}}p = find_lock_task_mm(tsk);if (!p)continue;oom_score_adj = p->signal->oom_score_adj;if (oom_score_adj < min_score_adj) {task_unlock(p);continue;}tasksize = get_mm_rss(p->mm);task_unlock(p);if (tasksize <= 0)continue;if (selected) {if (oom_score_adj < selected_oom_score_adj)continue;if (oom_score_adj == selected_oom_score_adj &&tasksize <= selected_tasksize)continue;}selected = p;selected_tasksize = tasksize;selected_oom_score_adj = oom_score_adj;lowmem_print(3, "select '%s' (%d), adj %hd, size %d, to kill\n",p->comm, p->pid, oom_score_adj, tasksize);}if (selected) {lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \"   to free %ldkB on behalf of '%s' (%d) because\n" \"   cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \"   Free memory is %ldkB above reserved.\n" \"   Free CMA is %ldkB\n" \"   Total reserve is %ldkB\n" \"   Total free pages is %ldkB\n" \"   Total file cache is %ldkB\n" \"   GFP mask is 0x%x\n",selected->comm, selected->pid,selected_oom_score_adj,selected_tasksize * (long)(PAGE_SIZE / 1024),current->comm, current->pid,other_file * (long)(PAGE_SIZE / 1024),minfree * (long)(PAGE_SIZE / 1024),min_score_adj,other_free * (long)(PAGE_SIZE / 1024),global_page_state(NR_FREE_CMA_PAGES) *(long)(PAGE_SIZE / 1024),totalreserve_pages * (long)(PAGE_SIZE / 1024),global_page_state(NR_FREE_PAGES) *(long)(PAGE_SIZE / 1024),global_page_state(NR_FILE_PAGES) *(long)(PAGE_SIZE / 1024),sc->gfp_mask);if (lowmem_debug_level >= 2 && selected_oom_score_adj == 0) {show_mem(SHOW_MEM_FILTER_NODES);dump_tasks(NULL, NULL);}lowmem_deathpending_timeout = jiffies + HZ;send_sig(SIGKILL, selected, 0);set_tsk_thread_flag(selected, TIF_MEMDIE);rem -= selected_tasksize;rcu_read_unlock();/* give the system time to free up the memory */msleep_interruptible(20);} elsercu_read_unlock();lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n",nr_to_scan, sc->gfp_mask, rem);mutex_unlock(&scan_mutex);return rem;
}static struct shrinker lowmem_shrinker = {.shrink = lowmem_shrink,.seeks = DEFAULT_SEEKS * 16
};static int __init lowmem_init(void)
{register_shrinker(&lowmem_shrinker);return 0;
}static void __exit lowmem_exit(void)
{unregister_shrinker(&lowmem_shrinker);
}#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
static short lowmem_oom_adj_to_oom_score_adj(short oom_adj)
{if (oom_adj == OOM_ADJUST_MAX)return OOM_SCORE_ADJ_MAX;elsereturn (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
}static void lowmem_autodetect_oom_adj_values(void)
{int i;short oom_adj;short oom_score_adj;int array_size = ARRAY_SIZE(lowmem_adj);if (lowmem_adj_size < array_size)array_size = lowmem_adj_size;if (array_size <= 0)return;oom_adj = lowmem_adj[array_size - 1];if (oom_adj > OOM_ADJUST_MAX)return;oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj);if (oom_score_adj <= OOM_ADJUST_MAX)return;lowmem_print(1, "lowmem_shrink: convert oom_adj to oom_score_adj:\n");for (i = 0; i < array_size; i++) {oom_adj = lowmem_adj[i];oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj);lowmem_adj[i] = oom_score_adj;lowmem_print(1, "oom_adj %d => oom_score_adj %d\n",oom_adj, oom_score_adj);}
}static int lowmem_adj_array_set(const char *val, const struct kernel_param *kp)
{int ret;ret = param_array_ops.set(val, kp);/* HACK: Autodetect oom_adj values in lowmem_adj array */lowmem_autodetect_oom_adj_values();return ret;
}static int lowmem_adj_array_get(char *buffer, const struct kernel_param *kp)
{return param_array_ops.get(buffer, kp);
}static void lowmem_adj_array_free(void *arg)
{param_array_ops.free(arg);
}static struct kernel_param_ops lowmem_adj_array_ops = {.set = lowmem_adj_array_set,.get = lowmem_adj_array_get,.free = lowmem_adj_array_free,
};static const struct kparam_array __param_arr_adj = {.max = ARRAY_SIZE(lowmem_adj),.num = &lowmem_adj_size,.ops = ¶m_ops_short,.elemsize = sizeof(lowmem_adj[0]),.elem = lowmem_adj,
};
#endifmodule_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR);
#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
__module_param_call(MODULE_PARAM_PREFIX, adj,&lowmem_adj_array_ops,.arr = &__param_arr_adj,S_IRUGO | S_IWUSR, -1);
__MODULE_PARM_TYPE(adj, "array of short");
#else
module_param_array_named(adj, lowmem_adj, short, &lowmem_adj_size,S_IRUGO | S_IWUSR);
#endif
module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size,S_IRUGO | S_IWUSR);
module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
module_param_named(lmk_fast_run, lmk_fast_run, int, S_IRUGO | S_IWUSR);module_init(lowmem_init);
module_exit(lowmem_exit);MODULE_LICENSE("GPL");




    Android中,进程的生命周期都是由系统控制的,即使用户关掉了程序,进程依然是存在于内存之中。这样设计的目的是为了下次能快速启动。当然,随着系统运行时间的增长,内存会越来越少。Android Kernel 会定时执行一次检查,杀死一些进程,释放掉内存。

    那么,如何来判断,那些进程是需要杀死的呢?答案就是我们的标题:Low memory killer机制。

    Android Low memory killer是基于linuxOOMout of memory  规则改进而来的。 OOM通过一些比较复杂的评分机制,对进程进行打分,然后将分数高的进程判定为bad进程,杀死并释放内存。OOM只有当系统内存不足的时候才会启动检查,而Low memory killer 则是定时进行检查。

     Low memory killer 主要是通过进程的oom_adj 来判定进程的重要程度。oom_adj的大小和进程的类型以及进程被调度的次序有关。

     Low memory killer 的具体实现可参看:kernel/drivers/misc/lowmemorykiller.c 

     其原理很简单,在linux中,存在一个kswapd的内核线程,当linux回收存放分页的时候,kswapd线程将会遍历一张shrinker链表,并执行回调,定义如下:


     所以只要注册 Shrinker,变可以在内存分页回收时根据规则释放内存,下面我们来看看其实现。 

  首先定义shrinker结构体,lowmem_shrink为回调函数的指针,当有内存分页回收的时候,这个函数将会被调用。


 

   初始化模块时进行注册,结束时注销。



     Android中,存在着一张内存阈值表,这张阈值表是可以在init.rc中进行配置的,

合理配置这张表,对于小内存设备有非常重要的作用。

     我们来看lowmemorykiller.c中这张默认的阈值表:

 


 

Lowmeme_adj中各项数值代表阈值的警戒级数,lowmem_minfree代表对应级数的剩余内存。也就是说,当系统的剩余内存为小于6MB时候,警戒级数为0,当系统内存剩余小于8M而大于6M的时候,警戒级数为1,当内存小于64M大于16MB的时候,警戒级数为12. Low memory killer 的规则就是根据当前系统的剩余内存多少来获取当前的警戒级数,如果进程的oom_adj大于警戒级数并且最大,进程将会被杀死(相同omm_adj的,则杀死占用内存较多的)。Omm_adj越小,代表进程越重要。一些前台的进程,oom_adj会比较小,而后台的服务,omm_adj会比较大,所以当内存不足的时候,Low memory killer 杀掉的必然先杀掉的是后台服务而不是前台的进程。

OK,现在我们来看具体代码,也就是lowmem_shrink这个回调函数:

 


 

首先通过global_page_state获取当前剩余内存大小,然后根据剩余内存和内存阈值表查找当前的内存警戒数min_adj。接着遍历所有进程,找到oom_adj大于min_adj并且oom_adj最大的进程:

进程的oom_adj 小于警戒阈值,则无视。 


获取这个进程所占用的内存大小tasksize,如果小于比我们当前选出进程的内存,则无视。



如果大于则选中这个进程:


经过for_each的遍历,selected 就是我们选出要释放掉的bad进程,它具有下面两个条件:

  • Oom_adj大于当前警戒阈值并且最大
  • 在同样大小的oom_adj中,占用内存最多

 

最后,我们释放掉这个进程的内存,通过force_sig(SIGKILL, selected)来向进程发送一个不可以忽略或阻塞的SIGKILL信号。

 

阈值表可以通过/sys/module/lowmemorykiller/parameters/adj/sys/module/lowmemorykiller/parameters/minfree进行配置,例如在init.rc中:

# Write value must be consistent with the above properties.
   write /sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15
 
   write /proc/sys/vm/overcommit_memory 1
   write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144
 
   class_start default
 

 

进程oom_adj同样可以进行设置,通过write /proc/<PID>/oom_adj  ,在init.rc中,init进程的pid1omm_adj被配置为-16,永远不会被杀死。

   # Set init its forked children's oom_adj.
   write /proc/1/oom_adj -16

 

   Low memory killer的基本原理我们应该弄清了,正如我前面所说的,进程omm_adj的大小是跟进程的类型以及进程被调度的次序有关。进程的类型,可以在ActivityManagerService中清楚的看到: 

    static final int EMPTY_APP_ADJ;

    static final int HIDDEN_APP_MAX_ADJ;

    static final int HIDDEN_APP_MIN_ADJ;

    static final int HOME_APP_ADJ;

    static final int BACKUP_APP_ADJ;

    static final int SECONDARY_SERVER_ADJ;

    static final int HEAVY_WEIGHT_APP_ADJ;

    static final int PERCEPTIBLE_APP_ADJ;

    static final int VISIBLE_APP_ADJ;

    static final int FOREGROUND_APP_ADJ;

    static final int CORE_SERVER_ADJ = -12;

    static final int SYSTEM_ADJ = -16; 

   ActivityManagerService定义各种进程的oom_adj,CORE_SERVER_ADJ代表一些核心的服务的omm_adj,数值为-12,由前面的分析可知道,这类进程永远也不会被杀死。

其他未赋值的都在static块中进行了初始化,是通过system/rootdir/init.rc进行配置的:


 

   在init.rc中:

# Define the oom_adj values for the classes of processes that can be

# killed by the kernel.  These are used in ActivityManagerService.

   setprop ro.FOREGROUND_APP_ADJ 0

   setprop ro.VISIBLE_APP_ADJ 1

   setprop ro.SECONDARY_SERVER_ADJ 2

   setprop ro.HIDDEN_APP_MIN_ADJ 7

   setprop ro.CONTENT_PROVIDER_ADJ 14

   setprop ro.EMPTY_APP_ADJ 15

 

# Define the memory thresholds at which the above process classes will

# be killed.  These numbers are in pages (4k).

   setprop ro.FOREGROUND_APP_MEM 1536

   setprop ro.VISIBLE_APP_MEM 2048

   setprop ro.SECONDARY_SERVER_MEM 4096

   setprop ro.HIDDEN_APP_MEM 5120

   setprop ro.CONTENT_PROVIDER_MEM 5632

   setprop ro.EMPTY_APP_MEM 6144

 

  由此我们知道EMPTY_APP 最容易被杀死,其实是CONTENT_PROVIDER FOREGROUND的进程很难被杀死。

  现在我们再来说影响oom_adj的第二个因素,进程的调度次序。这涉及到了ActivityManagerService的复杂调度,我们下次再来看吧。呵呵。

 

  See you next time !!!


这篇关于Android Low memory killer的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Android WebView无法加载H5页面的常见问题和解决方法

《AndroidWebView无法加载H5页面的常见问题和解决方法》AndroidWebView是一种视图组件,使得Android应用能够显示网页内容,它基于Chromium,具备现代浏览器的许多功... 目录1. WebView 简介2. 常见问题3. 网络权限设置4. 启用 JavaScript5. D

Android如何获取当前CPU频率和占用率

《Android如何获取当前CPU频率和占用率》最近在优化App的性能,需要获取当前CPU视频频率和占用率,所以本文小编就来和大家总结一下如何在Android中获取当前CPU频率和占用率吧... 最近在优化 App 的性能,需要获取当前 CPU视频频率和占用率,通过查询资料,大致思路如下:目前没有标准的

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

Android kotlin语言实现删除文件的解决方案

《Androidkotlin语言实现删除文件的解决方案》:本文主要介绍Androidkotlin语言实现删除文件的解决方案,在项目开发过程中,尤其是需要跨平台协作的项目,那么删除用户指定的文件的... 目录一、前言二、适用环境三、模板内容1.权限申请2.Activity中的模板一、前言在项目开发过程中,尤

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

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影