使用internal(com.android.internal)和hidden(@hide)APIs
方案一:使用android 反射可以实现
public class FileUtils
{
public static final int S_IRWXU = 00700;
public static final int S_IRUSR = 00400;
public static final int S_IWUSR = 00200;
public static final int S_IXUSR = 00100;
public static final int S_IRWXG = 00070;
public static final int S_IRGRP = 00040;
public static final int S_IWGRP = 00020;
public static final int S_IXGRP = 00010;
public static final int S_IRWXO = 00007;
public static final int S_IROTH = 00004;
public static final int S_IWOTH = 00002;
public static final int S_IXOTH = 00001;
public static final class FileStatus {
public int dev;
public int ino;
public int mode;
public int nlink;
public int uid;
public int gid;
public int rdev;
public long size;
public int blksize;
public long blocks;
public long atime;
public long mtime;
public long ctime;
}
public static native boolean getFileStatus (String path, FileStatus status);
private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\w%+,./=_-]+");
public static boolean copyFile(File srcFile, File destFile) {
boolean result = false;
try {
InputStream in = new FileInputStream(srcFile);
try {
result = copyToFile(in, destFile);
} finally {
in.close();
}
} catch (IOException e) {
result = false;
}
return result;
}
public static boolean copyToFile (InputStream inputStream, File destFile) {
try {
if (destFile.exists()) {
destFile.delete();
}
OutputStream out = new FileOutputStream(destFile);
try {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) >= 0) {
out.write(buffer, 0, bytesRead);
}
} finally {
out.close();
}
return true;
} catch (IOException e) {
return false;
}
}
public static boolean isFilenameSafe(File file) {
return SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
}
public static String readTextFile(File file, int max, String ellipsis) throws IOException {
InputStream input = new FileInputStream(file);
try {
long size = file.length();
if (max > 0 || (size > 0 && max == 0)) {
if (size > 0 && (max == 0 || size < max)) max = (int) size;
byte[] data = new byte[max + 1];
int length = input.read(data);
if (length <= 0) return "";
if (length <= max) return new String(data, 0, length);
if (ellipsis == null) return new String(data, 0, max);
return new String(data, 0, max) + ellipsis;
} else if (max < 0) { // "tail" mode: keep the last N
int len;
boolean rolled = false;
byte[] last = null, data = null;
do {
if (last != null) rolled = true;
byte[] tmp = last; last = data; data = tmp;
if (data == null) data = new byte[-max];
len = input.read(data);
} while (len == data.length);
if (last == null && len <= 0) return "";
if (last == null) return new String(data, 0, len);
if (len > 0) {
rolled = true;
System.arraycopy(last, len, last, 0, last.length - len);
System.arraycopy(data, 0, last, last.length - len, len);
}
if (ellipsis == null || !rolled) return new String(last);
return ellipsis + new String(last);
} else {
ByteArrayOutputStream contents = new ByteArrayOutputStream();
int len;
byte[] data = new byte[1024];
do {
len = input.read(data);
if (len > 0) contents.write(data, 0, len);
} while (len == data.length);
return contents.toString();
}
} finally {
input.close();
}
}
}
网上的搜索到的信息
android系统中,有很多类和方法是@hide属性的,这意味着这些类和方法是不会出现在androidsdk中的。但是有的时候我们的程序却需要调用这些个类和方法来完成一些工作。怎么办呢?一个办法是把这些个类和方法从android源代码中拷贝出来,放到我们的程序中一起编译使用。不过有可能会遇到一个比较极端的情况,那就是你花费了很久,终于把自己的程序编译成功了,却发现你几乎把整个android
framework的源代码都拷贝出来了。有些时候这样显然是得不偿失的。你花费了大量的时间,却收效甚微。那我们现在来看一下第二种方法,利用java 的反射机制。关于java的反射机制大家可以参考java的文档或者网络
减小字体 增大字体
上一些关于这个方面的文章。这里,我只介绍如何在android里使用。下面看一个例子(将黄色的代码复制一下就可以测试了,方法都是上面的)。
try {
Class hideClass = Class.forName("android.os.FileUtils");
//Method hideSubprocess = hidecClass.getMethod("createSubprocess",
// String.class, String.class, String.class, int[].class);
Method[] hideMethod = hideClass.getMethods();
int i = 0;
for (;i < hideMethod.length;i++)
{
Log.i(LOG_TAG,hideMethod[i].getName());
}
Method copyFile = hideClass.getMethod("copyFile", File.class,File.class);
File tmpPackageFile = getFileStreamPath(TMP_FILE_NAME);
if (tmpPackageFile == null) {
Log.w(LOG_TAG, "Failed to create temp file");
return ;
}
if (tmpPackageFile.exists()) {
tmpPackageFile.delete();
}
//this.openFileOutput("", mode);
try {
copyFile.invoke(hideClass.newInstance(), new File("/sdcard/MainActivity.apk"),tmpPackageFile);
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}catch (ClassNotFoundException e) {
//throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (SecurityException e) {
//throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (IllegalArgumentException e) {
//throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
其实使用基本上和一般的java reflection没有什么很大的区别。
还有一种整理好的TextFileUtils
package com.my.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
*文本文件常用的创建、写入、读取、删除
*
*@2010-8-19
*@下午03:36:12
*/
public class TextFileUtils {
/**
* 创建Text文件
* @param path
*/
public static void createText(final String path) {
File filename ;
try {
filename = new File(path);
if (!filename.exists()) {
filename.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
}// end method createText()
/**
* 删除Text文件
* @param path
*/
public void deleteText(String path) {
try {
RandomAccessFile file = new RandomAccessFile(path, "rw");
file.setLength(0);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 读取text内容
* @param path
* @return
*/
public static String readText(String path) {
FileReader fileread;
File filename = new File(path);
String line = null;
try {
fileread = new FileReader(filename);
BufferedReader bfr = new BufferedReader(fileread);
try {
line = bfr.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return line;
}// end method readText()
/**
* 向Text文件中写入内容
* @param body
* @param path
*/
public static void writeText(String content, String path) {
// 先读取原有文件内容,然后进行写入操作
RandomAccessFile mm = null;
File filename = new File(path);
try {
mm = new RandomAccessFile(filename, "rw");
mm.writeBytes(content);
} catch (IOException e1) {
// TODO 自动生成 catch 块
e1.printStackTrace();
} finally {
if (mm != null) {
try {
mm.close();
} catch (IOException e2) {
// TODO 自动生成 catch 块
e2.printStackTrace();
}
}
}
}// end method writeText()
}
方案二:不使用反射来实现
下面主要讲解方案二的使用方法:
使用internal(com.android.internal)和hidden(@hide)APIs
Part One
原文路径:http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-1-introduction/
Android有两种类型的API是不能经由SDK访问的。
第一种是位于com.android.internal包中的API。我将称之为internal API。第二种API类型是一系列被标记为@hide属性的类和方法。从严格意义上来讲,这不是一个单一的API,而是一组小的被隐藏的API,但我仍将其假设为一种API,并称之为hidden API。
Hidden API 例子
你可以查看一下android的源码,并能找到一些变量、函数和类等,都被@hide属性标记了。
下面的例子就是在WifiManager(API 10源码)中隐藏的变量。
另一个例子是在WifiManager(API 10源码)中隐藏了setWifiApEnabled函数。
因此,只要你看到@hide属性,那你看到的就是hidden API。
Internal和hidden API的区别
Hidden API之所以被隐藏,是想阻止开发者使用SDK中那些未完成或不稳定的部分(接口或架构)。举个例子,Bluetooth API在API 5(Android 2.0)上才开放;在API 3 和4上都是用@hide属性隐藏了。当这些API被验证和清理后,Google的开发者会移除@hide属性,并让其在API 5官方化。很多地方在API 4 和5之间发生了变化。如果你的程序依赖某些隐藏的API,当其部署到新的平台上时,就有可能陷入困境。
对于internal API来说,从来都没有计划将其开放出来。它就是Android的“内部厨房”,对开发者来说,应该将其视作黑盒。凡事都会有变化的。如果你依赖某些internal API,也有可能在新的Android release上,这些internal API发生变化,从而令你失望。
总结一下区别:
Hidden API = 进行中的工作;
Internal API = 黑盒;
Internal和hidden API的编译时 vs. 运行时
当你使用Android SDK进行开发的时候,你引用了一个非常重要的jar文件——android.jar。它位于Android SDK平台的文件夹中(SDK_DIR/platforms/platform-X/android.jar,其中,X表示API等级)。这个android.jar移掉了com.android.internal包中所有的类,也移掉了所有标记有@hide的类,枚举,字段和方法。
但当你在设备上启动应用程序时,它将加载framework.jar(简单来说,它和android.jar等同),而其未移掉internal API和hidden API。(但它对开发者来说,并不能友好地访问,因此,我将向大家展示不通过反射如何使用这些API)。
关于internal API,还有一件事需要说明。Eclipse的ADT插件增加了一个额外的规则,那就是禁止使用com.android.internal包中的任何东西。所以,即便是我们可以拿到最原始的android.jar(未删减版),也没有轻松的办法通过Eclipse使用这些internal API。
你可以亲自检查一下。创建一个新的Android工程(或者使用已有的)。查看一下它引用的类库(右击project Properties –> Java Build Path –> Libraries)。
重要的总结:internal和hidden API在SDK中是按照一样的方式处理的(都从android.jar中移除了),但internal API更惨的是,还被Eclipse的ADT插件显式禁止了。
不通过反射使用internal和hidden API
这些文章的终极目标是让开发者能够不通过反射使用Internal和Hidden API。如果你完成了接下来部分中描述的步骤,你将能使用这些Internal和Hidden API,如同公开的API。你不再需要使用反射。
注:如果你正在使用这些非公开的API,你必须知道,你的程序有着极大的风险。基本上,无法保证在下一次的Android OS更新时,这些API不被破坏,也无法保证不同的运营商有着一致的行为。你自己决定吧。
接下来有三个场景:
1. Internal 和hidden API都可用(场景A)
2. 只Hidden API可用(场景B)
3. 只Internal API可用(场景C)
场景A是B、C的总和。场景B是最简单的一个(不需要对Eclipse的ADT修改)。
场景A:阅读Part1, 2, 3, 4, 5
场景B:阅读Part1, 2, 3, 5
场景C:阅读Part1, 2, 3, 4, 5
Part 2
原文路径:http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-2-hacking-around/
在上一篇中,我解释了为什么我们不通过反射就会很难使用internal和hidden API。这是因为android.jar中就没包含这些API,因此,没人能够在编译时引用这些类。
这篇文章将描述如何还原最初的android.jar。这将允许我们像使用公开的API那样使用internal和hidden API。
如何得到原版android.jar?
我们需要修改android.jar,这样它才能包含所有的*.class文件(包括internal和hidden API类)。有两种办法:
1) Android是一个开源工程。我们可以下载源码并搭建编译环境,这样它就不能移除那些internal和hidden的类了。这个办法比较困难;
2) 每个模拟器或真机在运行时都会有一个等同android.jar的东西。我们可以从这里拿到jar文件,提取出原始的.class文件,并拷贝到Android SDK的android.jar中。
我将采用方案2。它易于开始,还不需要搭建Linux环境及编译环境等。
从设备上获取framework.jar
你可以使用命令行(adb pull)从模拟器或设备上下载文件,或者使用DDMS(借助Eclipse或SDK中的应用)。
注意:模拟器通常在.dex文件中包含代码,而真机一般在优化版的dex文件中包含代码——odex文件。操作odex文件比较困难,这也是为什么我选择模拟器的原因。
与Android SDK中的android.jar等同的文件是framework.jar。这个文件位于设备的:/system/framework/framework.jar
adb pull /system/framework/framework.jar
当framework.jar从设备上下下来之后,重命名为framework.zip并解压到独立的文件夹中,看起来是这个样子的:
classes.dex正是我们需要的。
创建framework-classes.zip
首先,我们需要把.dex文件转换成.jar格式。你可以使用通用的工具dex2jar。只需要运行:
dev2jar classes.dex
当转换结束时,你应该得到了classes.dex.dex2jar.jar文件。重命名为framework-classes.zip。使用zip查看器,进入到framework-classes.zip/com/android/internal/:
恭喜你,你已经拥有了所有的.class文件,包括internal和hidden API(尽管截图只确认了internal部分)。
创建original-android.jar
Android SDK的android.jar位于ANDROID_SDK/platforms/android-X/android.jar(X表示API等级)。
拷贝android.jar成custom-android.jar。解压至custom-android文件夹。将framework-classes.zip中所有的.class文件拷贝到custom-android文件夹中(你需要覆盖所有已经存在的.class文件)。
然后,压缩custom-android文件成original-android.zip。重命名为original-android.jar。
步骤总结
1. 选择你的目标平台X
2. 创建目标平台X的模拟器
3. 启动模拟器,下载/system/framework/framework.jar
4. 重命名framework.jar -> framework.zip
5. 从framework.zip中抽取classes.dex
6. 使用dex2jar工具,将其转换成classes.jar
7. 重命名classes.jar -> framework-classes.zip
8. 拷贝android.jar –> custom-android.zip
9. 解压custom-android.zip至custom-android文件夹
10. 将framework-classes.zip中所有文件拷贝至custom-android文件夹(覆盖存在的文件)
11. 压缩custom-android文件夹成original-android.zip
12. 重命名original-android.zip -> original-android.jar
打完收功。
总结
我们还原了android.jar,使其包含所有的internal和hidden API的.class文件。这只是第一步。下一步将创建定制的android平台,使其使用未删节版的android.jar,并将其添加到Android SDK platforms文件夹中。
Part 3
原文路径:http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-3-custom-android-platform/
在上一篇中,我已经展示了如何创建一个包含所有internal和hidden API的original-android.jar。
接下来的工作就是要修改已经存在的Android平台(SDK_DIR/platforms/platform-X/android.jar,X表示API等级)。你可以直接使用Part2中创建的original-android.jar替换android.jar。但这样的话,你的所有工程都将直接使用internal和hidden API而没有任何限制。这不够方便,因为在多数的工程中你不希望这样。甚至,你可能更希望禁止这些API(ADT/android.jar的默认行为)。但对于一些特定的工程,你希望能够使用这些internal和hidden API。
为了达到这样的灵活性,你需要创建一个新的自定义的Android平台。当不需要访问internal和hidden API时,你只需使用原有的Android平台。当你使用这些API时,你使用自定义的Android平台。
Android SDK文件夹结构
让我们看一下Android SDK树是如何组织的:
我们需要“platforms”文件夹。看一下里面:
这里列出了支持的Android平台。
现在,我们看一下它是如何与Eclipse设定关联的。选择你的工程,右击–> Properties –> Android。你将会看到一组支持的Android平台(与…/platforms/folder相似)。下面是截图:
创建新的平台
为了创建一个新的平台,我们需要拷贝android-9文件夹 -> android-9-internals。让我们做一些修正:
1. 删除其中的android.jar
2. 拷贝original-android.jar,并改名为android.jar
3. 修改build.prop文件:
…
ro.build.version.sdk=9 -> ro.build.version.sdk=-9
…
ro.build.version.release=2.3 -> ro.build.version.release=2.3.extended
…
重启Eclipse。并确认你能看到新的平台。下面是我所看到的:
为什么我选择API等级为-9?这是因为它必须是一个数字,而且它不能是9(或者其它已经存在的API等级)。否则,你自定义的平台将不能被使用(它在列表里可见,但选中后也不能正常工作,编译时仍然使用相应API等级的原始平台)。
下面是引用类库的截图(当前工程选中了自定义的平台):
总结
在上一篇中,我已经告诉你如何创建一个未删节版的android.jar。在这一篇中,我向你展示了如何创建一个自定义的Android平台,并在其中使用original-android.jar。这对于hidden API来说已经足够了。但对于internal API来说,还需要另一步。这是因为ADT仍然不允许使用com.android.internal包中的类(参见上图中的“forbidden”访问规则)。下一节我将向你展示如何定制ADT来允许使用internal包中的类。
============华丽的分割线=============
在实际的操作过程中,我创建的自定义的android.jar(API 10)不能被Eclipse成功加载,会出现以下的错误框,如同网站上其它人操作的结果一样,期待解决方案。
不过,作者提供了可用的自定义的android.jar,如果不想自己尝试的话,可以直接从网站下载,地址将在Part5中给出,稍等。
Part 4
原文路径:http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-4-customizing-adt/
在上一篇文章里,我描述了如何创建一个自定义的original-android.jar,以及如何创建一个自定义的Android平台来使用这个original-android.jar。这对Hidden API来说足够了。但对Internal API来说,仍然还有一个包袱:Eclipse的ADT插件。它限制使用com.android.internal包中的任何类。
有几种方法可以解决这个访问限制。
1) ADT源码可以下载。因此,删除/修改代码中的某些代码,从而编译出一个新的ADT是可以的。麻烦的是你需要搭建64位Linux系统,下载源码,编译等。它需要花费一些时间。当有新的ADT版本时,你需要重来一遍。
2) 另外的方法就是修改ADT的字节码。用一个类似于“com/android/internax/**”的字符串替换“com/android/internal/**”。
第二种方法可以用脚本实现。并且不需要访问源码以及可在Windows上操作。这也是为什么我在这篇中选用第二种解决方案的原因。
修改ADT的字节码
进入Eclipse的plugins文件夹。找到文件名看起来像“com.android.ide.eclipse.adt_*.jar”的文件。备份一下这个文件(以防中间有错误发生)。并拷贝这个文件到一个“experimental”文件夹,在这里,我们要完成对其字节码的修改。
重命名*.jar为*.zip。解压这个文件到单独的文件夹。参看以下图片:
现在,进入到com/android/ide/eclipse/adt/internal/project子文件夹。
找到AndroidClasspathContainerInitializer.class文件。
这个文件包含“com/android/internal/**”字符串。接下来就是要替换这个字符串,例如“com/android/internax/**”。改变字符串的长度理论上是安全的,但最好还是替换其中的一个字母,并保持长度一致。
我使用notepad++修改的,它支持非可印刷字符,因此在对其修改时,不要触碰修改非可印刷字符。
当做完这个,保存文件。压缩这个文件夹,保证文件名与原始文件一模一样。在我这里,文件名是:com.android.ide.eclipse.adt_8.0.1.v201012062107-82219.zip。
注意:确保压缩文件的正确性。比较原始文件和修改文件的根文件结构。
现在,用修改后的版本替换原来的ADT的*.jar文件。然后,启动Eclipse。
在使用库窗口,你应该看到下面的样子,一切都变得那么的美好:
步骤总结
1. 关闭Eclipse
2. 从Eclipse的plugin文件夹中拷贝出ADT插件的jar文件
3. 重命名.jar -> .zip,然后解压至独立的文件夹
4. 找到com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.class文件
5. 用“com/android/internax/**”替换“com/android/internal/**”
6. 压缩这个文件夹
7. 重命名 .zip -> .jar
8. 用修改后的jar替换原始的ADT jar文件
9. 启动Eclipse
结论
这是不使用反射也能使用Internal API的最后一步。
Part 5
原文路径:https://devmaze.wordpress.com/2011/01/19/using-com-android-internal-part-5-summary-and-example/
为了能够使用Internal和Hidden API,你需要:
1. 创建自定义的original-android.jar,包含所有的.class文件
2. 创建自定义的Android平台来使用original-android.jar
3. 修改ADT插件,允许使用com.android.internal包(只为Internal API)
4. 创建新的工程,引用自定义的Android平台(本文中的例子)
在本文中,我将向你们展示如何使用那些Internal和Hidden API。
此外,在本文的结尾,我列出了一些自定义的Android平台,它们都包含Internal和Hidden API。我附带了它们,是为了可能你不想花太多时间在这方面,但又想快速的尝试什么。
例子
创建一个新工程,选择2.3.extender平台:
下面是代码:
这个代码使用了Internal API(PowerProfile)和Hidden API(isWifiApEnabled)。我不用使用反射就能编译并运行这些代码。
自定义平台
下面有些平台,是我为自己创建的。只用拷贝它们到SDK_DIR\platforms文件夹下。这只是让Hidden API可用。对于Internal API,你需要修改你的ADT插件。
API 3:http://www.megaupload.com/?d=S1F2MKYZ
API 4:http://www.megaupload.com/?d=VUCTRI3Y
API 7:http://www.megaupload.com/?d=7ITNILBK
API 8:http://www.megaupload.com/?d=EXT5FKKT
API 9:http://www.megaupload.com/?d=EXT5FKKT
API 10:http://www.megaupload.com/?d=FCV78A9M
==============华丽的分割线=============
我尝试了其中的几个自定义平台,发现,internal 和hidden API真的是可用了,但也有一些意外的问题,如AlertDialog.Builder(Context context)居然说Context参数是多余的。。
没花时间去研究为什么会这样,如果哪位童鞋知道原因,告诉我哈~~
使用internal(com.android.internal)和hidden(@hide)APIs相关推荐
- 使用internal(com.android.internal)和hidden(@hide)APIs – Part 5
xirihanlin 2011.06.07 原文路径:https://devmaze.wordpress.com/2011/01/19/using-com-android-internal-part- ...
- 使用内部(com.android.internal)和隐藏(@hide)API手记
使用内部(com.android.internal)和隐藏(@hide)API手记 内部API和隐藏API的不同 隐藏API隐藏是为了防止开发人员使用SDK中未完成或者未稳定(接口和架构方面看)的部分 ...
- has leaked window com.android.internal.policy.impl.PhoneWindow解决(Dialog.cancel、dismiss、hide区别)
项目中有个Activity中用到了ProgressDialog,而在跳转到下个Activity时就会报这个错: has leaked window com.android.internal.polic ...
- 使用内部(com.android.internal)和隐藏(@hide)API[第4部分,定制ADT]
本文翻译自http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-4-customizing-adt/ 在前面的 ...
- android Eclipse导入com.android.internal.R和layoutlib.jar报错解决方案
今天导入一个项目,发现运行不能,红叉叉表示com.android.internal.R.的一系列引用都失败. 查了一下,这些都在一个SDK自带包layoutlib.jar中.SDK路径\platfor ...
- android.view.WindowLeaked: Activity has leaked window com.android.internal.p
android.view.WindowLeaked: Activity has leaked window com.android.internal.policy.impl.PhoneWindow$ ...
- Internal类或Internal成员讲解
Internal类或Internal成员讲解 原文地址:http://www.cnblogs.com/JeffreyZhao/archive/2009/08/26/internal-member-is ...
- C#中internal和protect internal的理解
[internal] internal关键字是用来修饰类和类的成员的,表示该内容只能在本程序集中访问. 程序集:一般输出形式为dll或exe的文件. 例子: 如果某个程序集中,有如下的类: names ...
- Android 9.0 要禁用 hide APIs了,你的反射要用不了了!
Android 9.0? Hi,大家好,我是承香墨影! 距离 Android 8.0 发布,已经过了五个月,虽然现在占有率并不高,不过呢,Google 已经着手准备下一版本的 Android 系统. ...
最新文章
- 分享Ubuntu 16.04 几个国内更新源
- 你还在为20倍的连麦成本发愁吗?
- Ubuntu 源列表
- 深度学习之数学基础(临时记录)
- ruby 批量下载王者荣耀皮肤
- TSP旅行商问题的Hopfield求解过程
- java多线程按行读取文件_“java”中多线程按行读取txt且每个线程读的内容不能重复,这么求“demo”?...
- 一文带你全面解析postman工具的使用(基础篇)
- 在页面加载后执行任务
- EXCEL-数据透视表、日数据整理成月数据
- MySQL事务之脏读问题
- 数据结构(主席树,Bit):XTU 1247/COGS 2344. pair-pair
- 电荷耦合器件架构及工作原理
- 前字节程序员感叹“字节范”就是工作上不设边界,待遇上延迟满足。
- 计算机数制与转换教案,《数制转换》教案
- 打开Windows系统某设置的方法有哪些?
- uC/OS-III系统移植STM32F103C8
- Mac修改密码导致钥匙串
- 创业低谷期时怎么度过?
- 2015全部企业校招情况+薪资+户口指标!