// 将被访问节点移动到链表最后 voidafterNodeAccess(Node<K,V> e){ // move node to last LinkedHashMapEntry<K,V> last; if (accessOrder && (last = tail) != e) { LinkedHashMapEntry<K,V> p = (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after; p.after = null; if (b == null) head = a; else b.after = a; if (a != null) a.before = b; else last = b; if (last == null) head = p; else { p.before = last; last.after = p; } tail = p; ++modCount; } }
迭代器
LinkedHashMap 实现了自己的迭代器,其迭代是通过双向链表实现的。
1 2 3 4 5 6 7 8 9 10 11 12
abstractclassLinkedHashIterator{ // ...
final LinkedHashMapEntry<K,V> nextNode(){ LinkedHashMapEntry<K,V> e = next; if (modCount != expectedModCount) thrownew ConcurrentModificationException(); if (e == null) thrownew NoSuchElementException(); current = e; next = e.after; return e; } }
其 containsValue 方法也是通过遍历双向链表实现的:
1 2 3 4 5 6 7 8
publicbooleancontainsValue(Object value){ for (LinkedHashMapEntry<K,V> e = head; e != null; e = e.after) { V v = e.value; if (v == value || (value != null && value.equals(v))) returntrue; } returnfalse; }
val queue = PriorityQueue<Int>(10) { o1, o2 -> o2.compareTo(o1) } for (i in0..9) { queue.add(Random.nextInt(50)) } println(queue) for (i in0..9) { println("${queue.remove()} - $queue") }
foreach(loop_var RANGE start stop [step]) ... endforeach(loop_var)
start 表示起始数,stop 表示终止数,step 表示步长,示例:
1 2 3 4
foreach(i RANGE 1 9 2) message(${i}) endforeach(i) # 输出:13579
打印信息
1 2 3 4
message(${PROJECT_SOURCE_DIR}) message("build with debug mode") message(WARNING "this is warnning message") message(FATAL_ERROR "this build has many error") # FATAL_ERROR 会导致编译失败
Started:其他组件调用startService()方法启动一个Service。一旦启动,Service将一直运行在后台(run in the background indefinitely)即便启动Service的组件已被destroy。通常,一个被start的Service会在后台执行单独的操作,也并不给启动它的组件返回结果。比如说,一个start的Service执行在后台下载或上传一个文件的操作,完成之后,Service应自己停止。
Bound:其他组件调用bindService()方法绑定一个Service。通过绑定方式启动的Service是一个client-server结构,该Service可以与绑定它的组件进行交互。一个bound service仅在有组件与其绑定时才会运行(A bound service runs only as long as another application component is bound to it),多个组件可与一个service绑定,service不再与任何组件绑定时,该service会被destroy。
注意:
Service运行在主线程中(A service runs in the main thread of its hosting process),Service并不是一个新的线程,也不是新的进程。也就是说,若您需要在Service中执行较为耗时的操作(如播放音乐、执行网络请求等),需要在Service中创建一个新的线程。这可以防止ANR的发生,同时主线程可以执行正常的UI操作。
当系统内存低时,系统将强制停止Service的运行;若Service绑定了正在与用户交互的activity,那么该Service将不大可能被系统kill( less likely to be killed)。如果创建的是前台Service,那么该Service几乎不会被kill(almost never be killed)。否则,当创建了一个长时间在后台运行的Service后,系统会降低该Service在后台任务栈中的级别——这意味着它容易被kill(lower its position in the list of background tasks over time and the service will become highly susceptible to killing),所以在开发Service时,需要使Service变得容易被restart,因为一旦Service被kill,再restart它需要其资源可用时才行
START_STICKY:若系统在onStartCommand()执行并返回后kill了service,那么service会被recreate并回调onStartCommand()。dangerous不要重新传递最后一个Intent(do not redeliver the last intent)。相反,系统回调onStartCommand()时回传一个空的Intent,除非有 pending intents传递,否则Intent将为null。该模式适合做一些类似播放音乐的操作。
Uri uri = Uri.parse("content://com.hearing.provider/user") // 生成的Uri为:content://com.hearing.provider/user/7 Uri resultUri = ContentUris.withAppendedId(uri, 7);
Uri uri = Uri.parse("content://com.hearing.provider/user/7") //获取的结果为:7 long personid = ContentUris.parseId(uri);
VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存),一个进程总共可访问的地址空间。其大小还包括了可能不在RAM中的内存(比如虽然malloc分配了空间,但尚未写入)。 VSS 很少被用于判断一个进程的真实内存使用量。
RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存),一个进程在RAM中真实存储的总内存。但是RSS还是可能会造成误导,因为它仅仅表示该进程所使用的所有共享库的大小,它不管有多少个进程使用该共享库,该共享库仅被加载到内存一次。所以RSS并不能准确反映单进程的内存占用情况。
PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存),按比例表示使用的共享库, 例如:如果有三个进程都使用了一个共享库,共占用了30页内存。那么PSS将认为每个进程分别占用该共享库10页的大小。 PSS是非常有用的数据,因为系统中所有进程的PSS都相加的话,就刚好反映了系统中的总共占用的内存。 而当一个进程被销毁之后, 其占用的共享库那部分比例的PSS,将会再次按比例分配给余下使用该库的进程。这样PSS可能会造成一点的误导,因为当一个进程被销毁后,PSS不能准确地表示返回给全局系统的内存(the memory returned to the overall system)。
USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存),一个进程所占用的私有内存。即该进程独占的内存。 USS是非常非常有用的数据,因为它反映了运行一个特定进程真实的边际成本(增量成本)。当一个进程被销毁后,USS是真实返回给系统的内存。当进程中存在一个可疑的内存泄露时,USS是最佳观察数据。
一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS
free
1 2 3 4
$ free -h total used free shared buff/cache available Mem: 7.9G 6.6G 1.0G 17M 223M 1.1G Swap: 19G 566M 19G
$ ls -li total 0 16044073672577795 drwxrwxrwx 1 hearing hearing 4096 Aug 27 18:07 test 14918173765727074 -rw-rw-rw- 1 hearing hearing 0 Aug 27 18:06 test.txt
$ ln test.txt test1
$ ls -li total 0 16044073672577795 drwxrwxrwx 1 hearing hearing 4096 Aug 27 18:07 test 14918173765727074 -rw-rw-rw- 2 hearing hearing 0 Aug 27 18:06 test.txt 14918173765727074 -rw-rw-rw- 2 hearing hearing 0 Aug 27 18:06 test1
# 查看指定进程的限制:在 Max open files 那一行,可以看到当前设置中最大文件描述符的数量为1024 $ cat /proc/803/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 8041 8041 processes Max open files 1024 4096 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 8041 8041 signals Max msgqueue size 819200 819200 bytes Max nice priority 40 40 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us
# 查看该进程占用了多少个文件描述符 $ ll /proc/803/fd | wc -l 5
$ ll /proc/803/fd total 0 lrwx------ 1 hearing hearing 0 Aug 27 20:14 0 -> /dev/tty3 lrwx------ 1 hearing hearing 0 Aug 27 20:14 1 -> /dev/tty3 lrwx------ 1 hearing hearing 0 Aug 27 20:14 10 -> /dev/tty3 lrwx------ 1 hearing hearing 0 Aug 27 20:14 2 -> /dev/tty3
总结
实际应用过程中,如果出现“Too many open files” , 可以通过增大进程可用的文件描述符数量来解决,但往往故事不会这样结束,很多时候,并不是因为进程可用的文件描述符过少,而是因为程序bug,打开了大量的文件连接(web连接也会占用文件描述符)而没有释放。程序申请的资源在用完后及时释放,才是解决“Too many open files”的根本之道。
虽然我们在使用dpkg时,已经解决掉了软件安装过程中的大量问题,但是当依赖关系不满足时,仍然需要手动解决,而apt这个工具解决了这样的问题,linux distribution 先将软件放置到对应的服务器中,然后分析软件的依赖关系,并且记录下来,然后当客户端有安装软件需求时,通过清单列表与本地的dpkg以存在的软件数据相比较,就能从网络端获取所有需要的具有依赖属性的软件了。
publicstaticvoidsetupIfNeeded(final Activity activity, final Runnable whenDone){ // Termux can only be run as the primary user (device owner) since only that // account has the expected file system paths. Verify that: UserManager um = (UserManager) activity.getSystemService(Context.USER_SERVICE); boolean isPrimaryUser = um.getSerialNumberForUser(android.os.Process.myUserHandle()) == 0; if (!isPrimaryUser) { Termux.mInstance.setInstalled(false); Termux.mInstance.getTermuxHandle().initFail(); return; }
final File PREFIX_FILE = new File(Termux.PREFIX_PATH); if (PREFIX_FILE.isDirectory()) { whenDone.run(); return; }
new Thread() { @Override publicvoidrun(){ try { final String STAGING_PREFIX_PATH = Termux.FILES_PATH + "/usr-staging"; final File STAGING_PREFIX_FILE = new File(STAGING_PREFIX_PATH);
if (STAGING_PREFIX_FILE.exists()) { deleteFolder(STAGING_PREFIX_FILE); }
finalbyte[] buffer = newbyte[8096]; final List<Pair<String, String>> symlinks = new ArrayList<>(50);
final URL zipUrl = determineZipUrl(); try (ZipInputStream zipInput = new ZipInputStream(zipUrl.openStream())) { ZipEntry zipEntry; while ((zipEntry = zipInput.getNextEntry()) != null) { if (zipEntry.getName().equals("SYMLINKS.txt")) { BufferedReader symlinksReader = new BufferedReader(new InputStreamReader(zipInput)); String line; while ((line = symlinksReader.readLine()) != null) { String[] parts = line.split("←"); if (parts.length != 2) thrownew RuntimeException("Malformed symlink line: " + line); String oldPath = parts[0]; String newPath = STAGING_PREFIX_PATH + "/" + parts[1]; symlinks.add(Pair.create(oldPath, newPath));
privatestaticvoidensureDirectoryExists(File directory){ if (!directory.isDirectory() && !directory.mkdirs()) { thrownew RuntimeException("Unable to create directory: " + directory.getAbsolutePath()); } }
/** * Get bootstrap zip url for this systems cpu architecture. */ privatestatic URL determineZipUrl()throws MalformedURLException { String archName = determineTermuxArchName(); String url = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? "https://termux.org/bootstrap-" + archName + ".zip" : "https://termux.net/bootstrap/bootstrap-" + archName + ".zip"; returnnew URL(url); }
privatestatic String determineTermuxArchName(){ // Note that we cannot use System.getProperty("os.arch") since that may give e.g. "aarch64" // while a 64-bit runtime may not be installed (like on the Samsung Galaxy S5 Neo). // Instead we search through the supported abi:s on the device, see: // http://developer.android.com/ndk/guides/abis.html // Note that we search for abi:s in preferred order (the ordering of the // Build.SUPPORTED_ABIS list) to avoid e.g. installing arm on an x86 system where arm // emulation is available. for (String androidArch : Build.SUPPORTED_ABIS) { switch (androidArch) { case"arm64-v8a": return"aarch64"; case"armeabi-v7a": return"arm"; case"x86_64": return"x86_64"; case"x86": return"i686"; } } thrownew RuntimeException("Unable to determine arch from Build.SUPPORTED_ABIS = " + Arrays.toString(Build.SUPPORTED_ABIS)); }
// Enable UTF-8 mode and disable flow control to prevent Ctrl+S from locking up the display. structtermiostios; tcgetattr(ptm, &tios); tios.c_iflag |= IUTF8; tios.c_iflag &= ~(IXON | IXOFF); tcsetattr(ptm, TCSANOW, &tios);
pid_t pid = fork(); if (pid < 0) { return throw_runtime_exception(env, "Fork failed"); } elseif (pid > 0) { *pProcessId = (int) pid; return ptm; } else { // Clear signals which the Android java process may have blocked: sigset_t signals_to_unblock; sigfillset(&signals_to_unblock); sigprocmask(SIG_UNBLOCK, &signals_to_unblock, 0);
close(ptm); setsid();
int pts = open(devname, O_RDWR); if (pts < 0) exit(-1);
dup2(pts, 0); dup2(pts, 1); dup2(pts, 2);
DIR* self_dir = opendir("/proc/self/fd"); if (self_dir != NULL) { int self_dir_fd = dirfd(self_dir); structdirent* entry; while ((entry = readdir(self_dir)) != NULL) { int fd = atoi(entry->d_name); if(fd > 2 && fd != self_dir_fd) close(fd); } closedir(self_dir); }
clearenv(); if (envp) for (; *envp; ++envp) putenv(*envp);
if (chdir(cwd) != 0) { char* error_message; // No need to free asprintf()-allocated memory since doing execvp() or exit() below. if (asprintf(&error_message, "chdir(\"%s\")", cwd) == -1) error_message = "chdir()"; perror(error_message); fflush(stderr); } execvp(cmd, argv); // Show terminal output about failing exec() call: char* error_message; if (asprintf(&error_message, "exec(\"%s\")", cmd) == -1) error_message = "exec()"; perror(error_message); _exit(1); } }
操作terminal
1 2 3 4 5 6 7 8 9 10
/** * A queue written to from a separate thread when the process outputs, and read by main thread to process by * terminal emulator. */ privatefinal ByteQueue mProcessToTerminalIOQueue = new ByteQueue(4096); /** * A queue written to from the main thread due to user interaction, and read by another thread which forwards by * writing to the {@link #mTerminalFileDescriptor}. */ privatefinal ByteQueue mTerminalToProcessIOQueue = new ByteQueue(4096);
# Annotation注释不能混淆 -keepattributes *Annotation* #对于NDK开发 本地的native方法不能被混淆 -keepclasseswithmembernames class * { native <methods>; } #保持View的子类里面的set、get方法不被混淆(*代替任意字符) -keepclassmembers public class * extends android.view.View { void set*(***); *** get*(); }
#保持Activity子类里面的参数类型为View的方法不被混淆,如被XML里面应用的onClick方法 # We want to keep methods in Activity that could be used in the XML attribute onClick -keepclassmembers class * extends android.app.Activity { public void *(android.view.View); }
#保持枚举类型values()、以及valueOf(java.lang.String)成员不被混淆 -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }
#保持实现Parcelable接口的类里面的Creator成员不被混淆 -keepclassmembers class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator CREATOR; }
#保持R类静态成员不被混淆 -keepclassmembers class **.R$* { public static <fields>; }
#不警告support包中不使用的引用 -dontwarn android.support.** -keep class android.support.annotation.Keep -keep @android.support.annotation.Keep class * {*;} #保持使用了Keep注解的方法以及类不被混淆 -keepclasseswithmembers class * { @android.support.annotation.Keep <methods>; } #保持使用了Keep注解的成员域以及类不被混淆 -keepclasseswithmembers class * { @android.support.annotation.Keep <fields>; } -keepclasseswithmembers class * { @android.support.annotation.Keep <init>(...); }
# 删除日志 (一定要把 -dontoptimize 配置去掉,否则无法删除日志) -assumenosideeffects class org.apache.log4j.** {*;} -assumenosideeffects class de.mindpipe.android.logging.log4j.LogConfigurator {*;} -assumenosideeffects class android.util.Log { public static boolean isLoggable(java.lang.String, int); public static int v(...); public static int i(...); public static int w(...); public static int d(...); public static int e(...); }