我希望我的代码在模拟器上运行时的运行方式与在设备上运行时的运行方式略有不同。 (例如,使用10.0.2.2而不是公共URL自动针对开发服务器运行。)检测Android应用程序何时在模拟器中运行的最佳方法是什么?
这个解决方案怎么样:
public static boolean isEmulator() {
return Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.startsWith("unknown")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK built for x86")
|| Build.MANUFACTURER.contains("Genymotion")
|| (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|| "google_sdk".equals(Build.PRODUCT);
}
我从来没有找到一个很好的方法来判断你是否在模拟器中。
但如果您只是需要检测您是否在开发环境中,则可以执行以下操作:
if(Build.PRODUCT.matches(".*_?sdk_?.*")){
//-- emulator --
}else{
//-- other device --
}
希望这有帮助....
不知道是否有更好的方法来检测emu,但模拟器将在根目录中包含文件 if(Debug.isDebuggerConnected() ) {
// Things to do in debug environment...
}
。
它是模拟器特定的启动脚本,它不应该在非模拟器构建中存在。
这是我的解决方案(仅当您在调试计算机上运行Web服务器时才有效):我创建了一个后台任务,该任务在应用程序启动时启动。它查找init.goldfish.rc
,如果它存在,则将全局参数(IsDebug)更改为true。这是一种无声的方式来找出你在哪里跑步。
http://10.0.2.2
来自主要活动onCreate:
public class CheckDebugModeTask extends AsyncTask<String, Void, String> {
public static boolean IsDebug = false;
public CheckDebugModeTask()
{
}
@Override
protected String doInBackground(String... params) {
try {
HttpParams httpParameters = new BasicHttpParams();
int timeoutConnection = 1000;
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
int timeoutSocket = 2000;
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
String url2 = "http://10.0.2.2";
HttpGet httpGet = new HttpGet(url2);
DefaultHttpClient client = new DefaultHttpClient(httpParameters);
HttpResponse response2 = client.execute(httpGet);
if (response2 == null || response2.getEntity() == null || response2.getEntity().getContent() == null)
return "";
return "Debug";
} catch (Exception e) {
return "";
}
}
@Override
protected void onPostExecute (String result)
{
if (result == "Debug")
{
CheckDebugModeTask.IsDebug = true;
}
}
使用此功能:
CheckDebugModeTask checkDebugMode = new CheckDebugModeTask();
checkDebugMode.execute("");
从电池,仿真器:电源始终是AC充电器。温度始终为0。
并且您可以使用 public static final boolean isEmulator() {
int rating = 0;
if ((Build.PRODUCT.equals("sdk")) || (Build.PRODUCT.equals("google_sdk"))
|| (Build.PRODUCT.equals("sdk_x86")) || (Build.PRODUCT.equals("vbox86p"))) {
rating++;
}
if ((Build.MANUFACTURER.equals("unknown")) || (Build.MANUFACTURER.equals("Genymotion"))) {
rating++;
}
if ((Build.BRAND.equals("generic")) || (Build.BRAND.equals("generic_x86"))) {
rating++;
}
if ((Build.DEVICE.equals("generic")) || (Build.DEVICE.equals("generic_x86")) || (Build.DEVICE.equals("vbox86p"))) {
rating++;
}
if ((Build.MODEL.equals("sdk")) || (Build.MODEL.equals("google_sdk"))
|| (Build.MODEL.equals("Android SDK built for x86"))) {
rating++;
}
if ((Build.HARDWARE.equals("goldfish")) || (Build.HARDWARE.equals("vbox86"))) {
rating++;
}
if ((Build.FINGERPRINT.contains("generic/sdk/generic"))
|| (Build.FINGERPRINT.contains("generic_x86/sdk_x86/generic_x86"))
|| (Build.FINGERPRINT.contains("generic/google_sdk/generic"))
|| (Build.FINGERPRINT.contains("generic/vbox86p/vbox86p"))) {
rating++;
}
return rating > 4;
}
来记录主机值,不同的模拟器具有不同的主机值。
Google在Flutter的Build.HOST
中使用此代码来确定设备是否为模拟器:
device-info plugin
另一个选择是查看ro.hardware属性并查看它是否设置为goldfish。不幸的是,似乎没有一种简单的方法可以从Java中做到这一点,但是使用private boolean isEmulator() {
return (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|| Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.startsWith("unknown")
|| Build.HARDWARE.contains("goldfish")
|| Build.HARDWARE.contains("ranchu")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK built for x86")
|| Build.MANUFACTURER.contains("Genymotion")
|| Build.PRODUCT.contains("sdk_google")
|| Build.PRODUCT.contains("google_sdk")
|| Build.PRODUCT.contains("sdk")
|| Build.PRODUCT.contains("sdk_x86")
|| Build.PRODUCT.contains("vbox86p")
|| Build.PRODUCT.contains("emulator")
|| Build.PRODUCT.contains("simulator");
}
从C中做到这一点很简单。
以上建议的检查property_get()的解决方案对我有用,直到我今天更新到Android 2.2发布的最新SDK工具。
因此,我目前切换到以下解决方案,到目前为止工作的缺点是你需要把PHONE_STATE读取权限(ANDROID_ID
)
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
一种方法的所有答案
private void checkForDebugMode() {
ISDEBUGMODE = false; //(Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID) == null);
TelephonyManager man = (TelephonyManager) getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
if(man != null){
String devId = man.getDeviceSoftwareVersion();
ISDEBUGMODE = (devId == null);
}
}
我找到了新的模拟器static boolean checkEmulator()
{
try
{
String buildDetails = (Build.FINGERPRINT + Build.DEVICE + Build.MODEL + Build.BRAND + Build.PRODUCT + Build.MANUFACTURER + Build.HARDWARE).toLowerCase();
if (buildDetails.contains("generic")
|| buildDetails.contains("unknown")
|| buildDetails.contains("emulator")
|| buildDetails.contains("sdk")
|| buildDetails.contains("genymotion")
|| buildDetails.contains("x86") // this includes vbox86
|| buildDetails.contains("goldfish")
|| buildDetails.contains("test-keys"))
return true;
}
catch (Throwable t) {Logger.catchedError(t);}
try
{
TelephonyManager tm = (TelephonyManager) App.context.getSystemService(Context.TELEPHONY_SERVICE);
String non = tm.getNetworkOperatorName().toLowerCase();
if (non.equals("android"))
return true;
}
catch (Throwable t) {Logger.catchedError(t);}
try
{
if (new File ("/init.goldfish.rc").exists())
return true;
}
catch (Throwable t) {Logger.catchedError(t);}
return false;
}
。
参考:Build.HARDWARE = "ranchu"
而且我也发现Android官方方式来检查是否是模拟器。我认为这对我们来说是个很好的参考。
自Android API Level 23 [Android 6.0]
https://groups.google.com/forum/#!topic/android-emulator-dev/dltBnUW_HzU
我们有package com.android.internal.util;
/**
* @hide
*/
public class ScreenShapeHelper {
private static final boolean IS_EMULATOR = Build.HARDWARE.contains("goldfish");
}
检查是否模拟器。
自Android API Level 24 [Android 7.0]
ScreenShapeHelper.IS_EMULATOR
我们有package android.os;
/**
* Information about the current build, extracted from system properties.
*/
public class Build {
/**
* Whether this build was for an emulator device.
* @hide
*/
public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1");
}
检查是否模拟器。
官方检查模拟器是否不是新的方式,也可能还不够,上面的答案也提到了。
但这可能告诉我们,官方将提供官方检查是否模拟器的方式。
正如使用上面提到的所有方法,现在我们也可以使用两种方式来检查是否模拟器。
Build.IS_EMULATOR
并等待官方开放SDK。
一个常见的似乎是qazxsw poi
我的建议:
尝试来自github的How to access the com.android.internal
package and @hide
。
易于检测Android模拟器
- 检查Device Farm中的实际设备(this)
- 蓝叠
- Genymotion
- Android模拟器
- 安迪46.2.207.0
- 我们玩
- Nox App Player
- Koplayer
- .....
如何使用示例:
https://aws.amazon.com/device-farm/
你可以查看IMEI#,EmulatorDetector.with(this)
.setCheckTelephony(true)
.addPackageName("com.bluestacks")
.setDebug(true)
.detect(new EmulatorDetector.OnEmulatorDetectorListener() {
@Override
public void onResult(boolean isEmulator) {
}
});
如果我在模拟器上回忆这返回0.但是,没有文件我能找到保证。虽然模拟器可能并不总是返回0,但是看起来非常安全,注册的手机不会返回0.在非手机Android设备上会发生什么,或者没有安装SIM卡或者当前未注册的设备网络?
这似乎是一个坏主意,依赖于此。
这也意味着你需要请求获得阅读电话状态的许可,如果你还没有要求其它的话,那就不好了。
如果不是这样,那么在您最终生成已签名的应用程序之前,总会在某处稍微翻一下。
http://developer.android.com/reference/android/telephony/TelephonyManager.html#getDeviceId%28%29
如果应用程序在模拟器上运行,则应返回true。
我们应该注意的是没有检测到所有的仿真器,因为只有几个不同的仿真器。这很容易检查。我们必须确保不会将实际设备检测为仿真器。
我使用名为“Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")
”的应用来检查这一点。
在这个应用程序中,您可以看到许多设备的各种信息(可能是世界上大多数设备;如果列表中缺少您正在使用的设备,它将自动添加)。
实际上,2.2上的ANDROID_ID总是等于9774D56D682E549C(根据Android Device Info Share +我自己的实验)。
所以,你可以检查这样的事情:
this thread
不是最漂亮,但它确实起作用。
这适合我
String androidID = ...;
if(androidID == null || androidID.equals("9774D56D682E549C"))
do stuff;
将文件放在模拟器的文件系统中;由于该文件不会存在于真实设备上,因此该文件应该稳定,可靠且易于修复。
我收集了关于这个问题的所有答案,并提出了检测Android是否在虚拟机/模拟器上运行的功能:
public boolean isEmulator() {
return Build.MANUFACTURER.equals("unknown");
}
在Emulator,Genymotion和Bluestacks上测试(2015年10月1日)。
检查答案,使用LeapDroid,Droid4x或Andy仿真器时没有一个工作,
对所有情况有效的是:
public boolean isvm(){
StringBuilder deviceInfo = new StringBuilder();
deviceInfo.append("Build.PRODUCT " +Build.PRODUCT +"\n");
deviceInfo.append("Build.FINGERPRINT " +Build.FINGERPRINT+"\n");
deviceInfo.append("Build.MANUFACTURER " +Build.MANUFACTURER+"\n");
deviceInfo.append("Build.MODEL " +Build.MODEL+"\n");
deviceInfo.append("Build.BRAND " +Build.BRAND+"\n");
deviceInfo.append("Build.DEVICE " +Build.DEVICE+"\n");
String info = deviceInfo.toString();
Log.i("LOB", info);
Boolean isvm = false;
if(
"google_sdk".equals(Build.PRODUCT) ||
"sdk_google_phone_x86".equals(Build.PRODUCT) ||
"sdk".equals(Build.PRODUCT) ||
"sdk_x86".equals(Build.PRODUCT) ||
"vbox86p".equals(Build.PRODUCT) ||
Build.FINGERPRINT.contains("generic") ||
Build.MANUFACTURER.contains("Genymotion") ||
Build.MODEL.contains("Emulator") ||
Build.MODEL.contains("Android SDK built for x86")
){
isvm = true;
}
if(Build.BRAND.contains("generic")&&Build.DEVICE.contains("generic")){
isvm = true;
}
return isvm;
}
private static String getSystemProperty(String name) throws Exception {
Class systemPropertyClazz = Class.forName("android.os.SystemProperties");
return (String) systemPropertyClazz.getMethod("get", new Class[]{String.class}).invoke(systemPropertyClazz, new Object[]{name});
}
public boolean isEmulator() {
boolean goldfish = getSystemProperty("ro.hardware").contains("goldfish");
boolean emu = getSystemProperty("ro.kernel.qemu").length() > 0;
boolean sdk = getSystemProperty("ro.product.model").equals("sdk");
return goldfish || emu || sdk;
}
if ("sdk".equals( Build.PRODUCT )) {
// Then you are running the app on the emulator.
Log.w("MyAPP", "\n\n Emulator \n\n");
}
所有BUILD引用都是build.prop值,因此您必须考虑如果您要将其放入发布代码中,您可能会有一些root用户因任何原因修改了它们。除非特别试图模拟仿真器,否则几乎不需要使用generic作为品牌的修改。
指纹是构建编译和内核编译签名。有些版本使用泛型,通常直接来自Google。
在已修改的设备上,IMEI也有可能被清零,因此除非您完全阻止修改的设备,否则这是不可靠的。
Goldfish是所有其他设备扩展的基础android构建。每个Android设备都有一个init.goldfish.rc,除非因未知原因被黑客入侵和删除。
好的Android ID对我不起作用,我目前正在使用:
Build.FINGERPRINT.contains("generic")
由于Genymotion的底层仿真引擎是VirtualBox,并且不会很快改变,我发现以下代码最可靠:
if (Build.BRAND.equalsIgnoreCase("generic")) {
// Is the emulator
}
基于其他答案的提示,这可能是最强大的方式:
"google_sdk".equals( Build.PRODUCT );
下面的代码如何判断您的应用程序是否使用调试密钥签名?它没有检测到模拟器,但它可能适用于您的目的?
isEmulator = "goldfish".equals(Build.HARDWARE)
这段代码适合我
public void onCreate Bundle b ) {
super.onCreate(savedInstanceState);
if ( signedWithDebugKey(this,this.getClass()) ) {
blah blah blah
}
blah
blah
blah
}
static final String DEBUGKEY =
"get the debug key from logcat after calling the function below once from the emulator";
public static boolean signedWithDebugKey(Context context, Class<?> cls)
{
boolean result = false;
try {
ComponentName comp = new ComponentName(context, cls);
PackageInfo pinfo = context.getPackageManager().getPackageInfo(comp.getPackageName(),PackageManager.GET_SIGNATURES);
Signature sigs[] = pinfo.signatures;
for ( int i = 0; i < sigs.length;i++)
Log.d(TAG,sigs[i].toCharsString());
if (DEBUGKEY.equals(sigs[0].toCharsString())) {
result = true;
Log.d(TAG,"package has been signed with the debug key");
} else {
Log.d(TAG,"package signed with a key other than the debug key");
}
} catch (android.content.pm.PackageManager.NameNotFoundException e) {
return false;
}
return result;
}
如果该设备没有SIM卡,则返回空字符串:“”
由于Android模拟器总是返回“Android”作为网络运营商,我使用上面的代码。
以下两项都设置为“google_sdk”:
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
String networkOperator = tm.getNetworkOperatorName();
if("Android".equals(networkOperator)) {
// Emulator
}
else {
// Device
}
所以它应该足以使用以下任一行。
Build.PRODUCT
Build.MODEL
要么
"google_sdk".equals(Build.MODEL)
我尝试了几种技术,但确定了稍微修改后的版本,检查Build.PRODUCT,如下所示。这似乎从仿真器到仿真器有很大不同,这就是我目前拥有3个检查的原因。我想我可以检查一下product.contains(“sdk”),但认为下面的检查有点安全。
"google_sdk".equals(Build.PRODUCT)
仅供参考 - 我发现我的Kindle Fire有Build.BRAND =“generic”,而且有些模拟器没有网络运营商的“Android”。
我只是寻找public static boolean isAndroidEmulator() {
String model = Build.MODEL;
Log.d(TAG, "model=" + model);
String product = Build.PRODUCT;
Log.d(TAG, "product=" + product);
boolean isEmulator = false;
if (product != null) {
isEmulator = product.equals("sdk") || product.contains("_sdk") || product.contains("sdk_");
}
Log.d(TAG, "isEmulator=" + isEmulator);
return isEmulator;
}
,_sdk
或_sdk_
,甚至只是sdk_
中的sdk
部分:
Build.PRODUCT