Android开发笔记(一百一十七)app省电方略

电源管理PowerManager

PowerManager是Android的电源管理类,用于管理电源操作如睡眠、唤醒、重启以及调节屏幕亮度等等。

PowerManager的对象从系统服务POWER_SERVICE中获取,它的主要方法如下:

goToSleep : 睡眠,即锁屏。

wakeUp : 唤醒,即解锁。

reboot : 重启。

另有下列几个隐藏的方法:

getMinimumScreenBrightnessSetting : 获取屏幕亮度的最小值。

getMaximumScreenBrightnessSetting : 获取屏幕亮度的最大值。

getDefaultScreenBrightnessSetting : 获取屏幕亮度的默认值。

setBacklightBrightness : 设置屏幕亮度。

但对多数开发者来说,PowerManager在实际开发中毫无用处,因为一旦调用该类的方法,你的app运行时就会崩溃,查看日志报错“java.lang.SecurityException: Neither user 10150 nor current process has android.permission.DEVICE_POWER.”这个错误信息倒是容易看懂,好吧,那我便在AndroidManifest.xml中加上DEVICE_POWER的权限。可是加了权限之后,ADT又提示错误“Permission
is only granted to system apps”。这下傻眼了,怎么会说“权限只授予系统应用程序”呢?不过这难不倒我,咱把app工程clean一下,错误提示就不见了,然后重新Run之,结果Console栏出现红色文字“Installation error: INSTALL_FAILED_SHARED_USER_INCOMPATIBLE”,还是不行呀。

找了大量的资料,才发现这是因为电源管理的权限,只有系统程序(打了系统签名)才可以获得,用户程序无法获取这个权限。大伙对该问题基本是束手无策,只有Stack Overflow上的大神给了个解决方案,主要做三方面的修改:

1、在AndroidManifest.xml中加上DEVICE_POWER、REBOOT、SHUTDOWN的权限。

2、在AndroidManifest.xml的manifest节点中增加属性说明“android:sharedUserId="android.uid.system"”,这表示使用系统用户的uid。

3、为了能够共享系统用户的uid,你的app得采用系统签名打包,即先找到目标Android系统的platform.pk8和platform.x509.pem密钥文件,然后使用signapk.jar将apk签名到指定密钥。

这个解决方案理论上可行,但就真机来说,每个品牌每个型号的手机,其系统签名都是不一样的。因此,就算你真的搞出来一个系统应用,那也仅适用于该签名版本的Android系统,而不能用于其他签名的Android系统,所以PowerManager只能是手机厂商内部使用了。

下面是PowerManager几个用途的示例代码(一般用不到,仅供参考):

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;

//注意,PowerManager只有系统应用才能操作,普通应用不能操作,所以下面代码仅供参考
public class PowerUtil {

	private final static String TAG = "PowerUtil";

	private static int getValue(Context ctx, String methodName, int defValue) {
		int value = defValue;
		PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
		try {
			Class<?> pmClass = Class.forName(pm.getClass().getName());
			Field field = pmClass.getDeclaredField("mService");
			field.setAccessible(true);
			Object iPM = field.get(pm);
			Class<?> iPMClass = Class.forName(iPM.getClass().getName());
			Method method = iPMClass.getDeclaredMethod(methodName);
			method.setAccessible(true);
			value = (Integer) method.invoke(iPM);
		} catch (Exception e) {
			e.printStackTrace();
		}
		Log.d(TAG, "methodName="+methodName+", value="+value);
		return value;
	}

	public static int getMinLight(Context ctx) {
		return getValue(ctx, "getMinimumScreenBrightnessSetting", 0);
	}

	public static int getMaxLight(Context ctx) {
		return getValue(ctx, "getMaximumScreenBrightnessSetting", 255);
	}

	public static int getDefLight(Context ctx) {
		return getValue(ctx, "getDefaultScreenBrightnessSetting", 100);
	}

    //设置屏幕亮度。light取值0-255
    public static void setLight(Context ctx, int light) {
		PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
        try {
            Class<?> pmClass = Class.forName(pm.getClass().getName());
            // 得到PowerManager类中的成员mService(mService为PowerManagerService类型)
            Field field = pmClass.getDeclaredField("mService");
            field.setAccessible(true);
            // 实例化mService
            Object iPM = field.get(pm);
            // 得到PowerManagerService对应的Class对象
            Class<?> iPMClass = Class.forName(iPM.getClass().getName());
            /*
             * 得到PowerManagerService的函数setBacklightBrightness对应的Method对象,
             * PowerManager的函数setBacklightBrightness实现在PowerManagerService中
             */
            Method method = iPMClass.getDeclaredMethod("setBacklightBrightness", int.class);
            method.setAccessible(true);
            // 调用实现PowerManagerService的setBacklightBrightness
            method.invoke(iPM, light);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void resetLight(Context ctx, int light) {
        try {
            Object power;
            Class <?> ServiceManager = Class.forName("android.os.ServiceManager");
            Class <?> Stub = Class.forName("android.os.IPowerManager$Stub");

            Method getService = ServiceManager.getMethod("getService", new Class[] {String.class});
            //Method asInterface = GetStub.getMethod("asInterface", new Class[] {IBinder.class});//of this class?
            Method asInterface = Stub.getMethod("asInterface", new Class[] {IBinder.class});    //of this class?
            IBinder iBinder = (IBinder) getService.invoke(null, new Object[] {Context.POWER_SERVICE});//
            power = asInterface.invoke(null,iBinder);//or call constructor Stub?//

            Method setBacklightBrightness = power.getClass().getMethod("setBacklightBrightness", new Class[]{int.class});
            setBacklightBrightness.invoke(power, new Object[]{light});
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //锁屏
    public static void lockScreen(Context ctx) {
		PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
        pm.goToSleep(SystemClock.uptimeMillis());
    }

    //解锁
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
	public static void unLockScreen(Context ctx) {
		PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
        pm.wakeUp(SystemClock.uptimeMillis());
    }

    //重启
    public static void reboot(Context ctx) {
		PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
        pm.reboot(null);
    }

    //关机
    public static void shutDown(Context ctx) {
        Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN");
        intent.putExtra("android.intent.extra.KEY_CONFIRM", false);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        // 弹出系统内置的对话框,选择确定关机或取消关机
        ctx.startActivity(intent);
    }

}

电池管理BatteryManager

BatteryManager名为电池管理,然而查看该类的源代码,里面只有一些常量定义,并非真正意义上的电池管理。事实上,开发者并不能直接管理电池,要想获取电池的相关信息,得通过监听电量改变事件来得知。

电池的电量改变事件,其动作名称是Intent.ACTION_BATTERY_CHANGED,因为接受该事件要求app必须处于活动状态,所以用来监听的广播接收器不能在AndroidManifest.xml中静态注册,而只能在app代码中通过registerReceiver方法来动态注册。下面是电量改变事件中携带的参数信息:

BatteryManager.EXTRA_SCALE : 电量刻度,通过getIntExtra获取。通常是100

BatteryManager.EXTRA_LEVEL : 当前电量,通过getIntExtra获取。

BatteryManager.EXTRA_STATUS : 当前状态,通过getIntExtra获取。

--BATTERY_STATUS_UNKNOWN = 1; 表示未知

--BATTERY_STATUS_CHARGING = 2; 表示正在充电

--BATTERY_STATUS_DISCHARGING = 3; 表示正在断电

--BATTERY_STATUS_NOT_CHARGING = 4; 表示不在充电

--BATTERY_STATUS_FULL = 5; 表示充满

BatteryManager.EXTRA_HEALTH : 健康程度,通过getIntExtra获取。

--BATTERY_HEALTH_UNKNOWN = 1; 表示未知

--BATTERY_HEALTH_GOOD = 2; 表示良好

--BATTERY_HEALTH_OVERHEAT = 3; 表示过热

--BATTERY_HEALTH_DEAD = 4; 表示坏了

--BATTERY_HEALTH_OVER_VOLTAGE = 5; 表示短路

--BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6; 表示未知错误

--BATTERY_HEALTH_COLD = 7; 表示冷却

BatteryManager.EXTRA_VOLTAGE : 当前电压,通过getIntExtra获取。

BatteryManager.EXTRA_PLUGGED : 当前电源,通过getIntExtra获取。

--0 表示电池

--BATTERY_PLUGGED_AC = 1; 表示充电器

--BATTERY_PLUGGED_USB = 2; 表示USB

--BATTERY_PLUGGED_WIRELESS = 4; 表示无线

BatteryManager.EXTRA_TECHNOLOGY : 当前技术,通过getStringExtra获取。比如返回Li-ion表示锂电池。

BatteryManager.EXTRA_TEMPERATURE : 当前温度,通过getIntExtra获取。

BatteryManager.EXTRA_PRESENT : 是否提供电池,通过getBooleanExtra获取。

除了电量改变事件,还有几个事件与电池有关,如下所示

Intent.ACTION_BATTERY_LOW : 电池电量过低,静态注册时使用android.intent.action.BATTERY_LOW

Intent.ACTION_BATTERY_OKAY : 电池电量恢复,静态注册时使用android.intent.action.BATTERY_OKAY

Intent.ACTION_POWER_CONNECTED : 连上外部电源,静态注册时使用android.intent.action.ACTION_POWER_CONNECTED

Intent.ACTION_POWER_DISCONNECTED : 断开外部电源,静态注册时使用android.intent.action.ACTION_POWER_DISCONNECTED

下面是电池事件的监听截图:

下面是监听电池事件的代码示例:

import com.example.exmbattery.util.DateUtils;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Bundle;
import android.widget.TextView;

public class BatteryActivity extends Activity {

	private TextView tv_battery_change;
	private static TextView tv_power_status;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_battery);

		tv_battery_change = (TextView) findViewById(R.id.tv_battery_change);
		tv_power_status = (TextView) findViewById(R.id.tv_power_status);
	}

	@Override
	protected void onStart() {
		super.onStart();
		batteryChangeReceiver = new BatteryChangeReceiver();
		IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
		registerReceiver(batteryChangeReceiver, filter);
	}

	@Override
	protected void onStop() {
		super.onStop();
		unregisterReceiver(batteryChangeReceiver);
	}

    private BatteryChangeReceiver batteryChangeReceiver;
    private class BatteryChangeReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null) {
            	int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
            	int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
            	int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, 0);
            	int healthy = intent.getIntExtra(BatteryManager.EXTRA_HEALTH, 0);
            	int voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);
            	int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 3);
            	String technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);
            	int temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
            	boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false);

            	String desc = String.format("%s : 收到广播:%s",
            			DateUtils.getNowDateTime(), intent.getAction());
            	desc = String.format("%s\n电量刻度=%d", desc, scale);
            	desc = String.format("%s\n当前电量=%d", desc, level);
            	desc = String.format("%s\n当前状态=%s", desc, mStatus[status]);
            	desc = String.format("%s\n健康程度=%s", desc, mHealthy[healthy]);
            	desc = String.format("%s\n当前电压=%d", desc, voltage);
            	desc = String.format("%s\n当前电源=%s", desc, mPlugged[plugged]);
            	desc = String.format("%s\n当前技术=%s", desc, technology);
            	desc = String.format("%s\n当前温度=%d", desc, temperature/10);
            	desc = String.format("%s\n是否提供电池=%s", desc, present?"是":"否");
            	tv_battery_change.setText(desc);
            }
        }
    }

    private static String[] mStatus = {"不存在", "未知", "正在充电", "正在断电", "不在充电", "充满"};
    private static String[] mHealthy = {"不存在", "未知", "良好", "过热", "坏了", "短路", "未知错误", "冷却"};
    private static String[] mPlugged = {"电池", "充电器", "USB", "不存在", "无线"};

    private static String mChange = "";
    public static class PowerChangeReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null) {
            	mChange = String.format("%s\n%s : 收到广播:%s",
            			mChange, DateUtils.getNowDateTime(), intent.getAction());
            	tv_power_status.setText(mChange);
            }
        }
    }
}

省电方法/屏幕开关事件

前面说了许多废话,赶快回到本文的主题——省电。app开发与服务器程序开发不同,app所在的移动设备是很缺电的,几天就要充一次电,所以如果你的app特别耗电,一天甚至半天就把用户手机搞没电了,那么通常逃脱不了被卸载的悲惨命运。因此,为人为己,开发者还是尽可能让app运行的时候省电些,绿色环保的低碳生活,从开发app做起。

然而目前尚无法检测每个应用的耗电程度,一般是靠经验判断,基本原则就是:越消耗资源的,耗电就越大。具体到代码编写,主要有以下省电措施:

1、能用整型数计算,就不用浮点数计算。

2、能用json解析,就不用xml解析。

3、能用网络定位,就不用GPS定位。

4、尽量减少大文件的下载(如先压缩再下载,或者缓存已下载的文件)。

5、用完系统资源,要及时回收。占着茅坑不拉屎,用户手机会很蛋疼。相关例子参见《Android开发笔记(七十五)内存泄漏的处理

6、能用线程处理,就不用进程处理。

7、多用缓存复用对象资源。如屏幕尺寸只需获取一次,其后可到缓存中读取,全局变量技术参见《Android开发笔记(二十八)利用Application实现内存读写》。相关例子还可参见《Android开发笔记(七十六)线程池管理》、《Android开发笔记(七十七)图片缓存算法

8、能用定时器广播,就不用后台常驻服务。

9、能用内存存储,就不用文件存储。

省电措施虽多,那要如何得知省电效果呢?在实际开发中,耗电大户其实是在后台默默运行的Service服务,想想看,手机待机的时候,屏幕都不亮了,可是手机里面还有一些不知疲倦的Service在愚公移山,愚公也是要吃饭的呀。我做过实验,一个app在系统待机时仍然满血Service运行,一小时后手机电量消耗4%;同一个app改造后在系统待机时不运行任何Service,一小时后手机电量消耗2%;一小时相差2%,十小时便相差20%啊,原来我们手机的电量就是这样被一点一点耗光的。

既然如此,我们若想避免app在手机待机时仍在做无用功,就要在屏幕关闭时结束指定任务,在屏幕点亮时再开始指定任务。这里用到了下面三个屏幕开关事件:

Intent.ACTION_SCREEN_ON : 屏幕点亮事件

Intent.ACTION_SCREEN_OFF : 屏幕关闭事件

Intent.ACTION_USER_PRESENT : 用户解锁事件,静态注册时使用android.intent.action.USER_PRESENT

使用上述三个事件要注意几点:

1、屏幕点亮事件和屏幕关闭事件必须在代码中动态注册。如果在AndroidManifest.xml中静态注册,则不起任何作用。

2、在关闭屏幕时,系统先暂停所有活动页面,然后才关闭屏幕;同样的,在点亮屏幕时,系统点亮屏幕,然后才恢复活动页面。所以这几个事件不能在Activity中注册/注销,只能在自定义Application的onCreate方法中注册,在onTerminate方法中注销。

3、Activity要想获取屏幕开关事件,得通过自定义的Application类去间接获取。

下面是屏幕开关事件的捕捉截图:

下面是屏幕开关事件的代码:

import com.example.exmbattery.util.DateUtils;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class LockScreenReceiver extends BroadcastReceiver {

	private static final String TAG = "LockScreenReceiver";

	@Override
	public void onReceive(Context context, Intent intent) {
		if (intent != null) {
			String mChange = "";
			mChange = String.format("%s\n%s : 收到广播:%s", mChange,
					DateUtils.getNowDateTime(), intent.getAction());
			if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
				mChange = String.format("%s\n这是屏幕点亮事件", mChange);
			} else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
				mChange = String.format("%s\n这是屏幕关闭事件", mChange);
			} else if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)) {
				mChange = String.format("%s\n这是用户解锁事件", mChange);
			}
			Log.d(TAG, mChange);
			MainApplication.getInstance().setChangeDesc(mChange);
		}
	}

}

下面是自定义Application的代码:

import android.app.Application;
import android.content.Intent;
import android.content.IntentFilter;

public class MainApplication extends Application {

	private static MainApplication mApp;
	private LockScreenReceiver mReceiver;
	private String mChange = "";

	public static MainApplication getInstance() {
		return mApp;
	}

	public String getChangeDesc() {
		return mApp.mChange;
	}

	public void setChangeDesc(String change) {
		mApp.mChange = mApp.mChange + change;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		mApp = this;
		mReceiver = new LockScreenReceiver();
		IntentFilter filter = new IntentFilter();
		filter.addAction(Intent.ACTION_SCREEN_ON);
		filter.addAction(Intent.ACTION_SCREEN_OFF);
		filter.addAction(Intent.ACTION_USER_PRESENT);
		registerReceiver(mReceiver, filter);
	}

	@Override
	public void onTerminate() {
		unregisterReceiver(mReceiver);
		super.onTerminate();
	}

}

下面是显示屏幕开关事件的页面代码

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class ScreenActivity extends Activity {

	private static TextView tv_screen;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_screen);
		tv_screen = (TextView) findViewById(R.id.tv_screen);
	}

	@Override
	protected void onStart() {
		super.onStart();
		tv_screen.setText(MainApplication.getInstance().getChangeDesc());
	}

}

点此查看Android开发笔记的完整目录

时间: 2024-10-26 23:58:17

Android开发笔记(一百一十七)app省电方略的相关文章

Android开发笔记(九十七)图片的特效处理

图片特效用到的函数 本文讲述的图片特效处理包括:怀旧.光照.光晕.底片.浮雕.模糊.锐化.黑白.冰冻.素描,所有这些特效都是基于一定的算法,对图像每个点的RGB值进行计算,并汇总所有点的计算结果生成新图片. 特效处理主要用到Bitmap类的三个方法: createBitmap : 创建一张新图片. getPixels : 从指定图片中获取所有点的像素数组. setPixels : 对指定图片设置所有点的像素数组. 图片怀旧效果 现实生活中的老相片都是泛黄的,而黄色又是由绿色和红色混合而成,所以怀

【转】Android开发笔记(序)写在前面的目录

原文:http://blog.csdn.net/aqi00/article/details/50012511 知识点分类 一方面写写自己走过的弯路掉进去的坑,避免以后再犯:另一方面希望通过分享自己的经验教训,与网友互相切磋,从而去芜存菁进一步提升自己的水平.因此博主就想,入门的东西咱就不写了,人不能老停留在入门上:其次是想拾缺补漏,写写虽然小众却又用得着的东西:另外就是想以实用为主,不求大而全,但求小而精:还有就是有的知识点是java的,只是Android开发也会经常遇上,所以蛮记下来.个人的经

[APP] Android 开发笔记 003

接上节 [APP] Android 开发笔记 002 5. 使用ant release 打包 1)制作 密钥文件 release.keystore (*.keystore) keytool -genkey -v -keystore "release.keystore" -alias "release" -keyalg "RSA" -validity "10000" 这里需要注意的是: -keystore "relea

Android开发笔记(一百一十九)工具栏ToolBar

Toolbar 在前面的博文<Android开发笔记(二十)顶部导航栏>中,我们学习了ActionBar的用法,可是ActionBar着实是不怎么好用,比如文字风格不能定制.图标不能定制,而且还存在低版本的兼容性问题,所以实际开发中大家还是不倾向使用ActionBar.为此,Android提供了加强版的工具栏控件即Toolbar,因为Toolbar继承自ViewGroup,而且可在布局文件中像其它布局视图一样使用,所以灵活性大大的提高了.既然Android都与时俱进了,那我们也不能落后,现在就

Android开发笔记(一百一十)使用http框架上传文件

HTTP上传 与文件下载相比,文件上传的场合不是很多,通常用于上传用户头像.朋友圈发布图片/视频动态等等,而且上传文件需要服务器配合,所以容易被app开发者忽略.就上传的形式来说,app一般采用http上传文件,很少用ftp上传文件. HttpURLConnection上传 很可惜Android没有提供专门的文件上传工具类,所以我们要自己写代码实现上传功能了.其实也不难,一样是按照普通网络访问的POST流程,只是要采用"multipart/form-data"方式来分段传输.另外文件上

Android开发笔记(一百一十八)自定义悬浮窗

WindowManager 在前面<Android开发笔记(六十六)自定义对话框>中,我们提到每个页面都是一个Window窗口,许多的Window对象需要一个管家来打理,这个管家我们称之为WindowManager窗口管理.在手机屏幕上新增或删除页面窗口,都可以归结为WindowManager的操作,下面是该管理类的常用方法说明: getDefaultDisplay : 获取默认的显示屏信息.通常用该方法获取屏幕分辨率,详情参见<Android开发笔记(三)屏幕分辨率>. addV

Android开发笔记(一百一十三)测试工具

单元测试TestCase Android的sdk提供了对项目进行单元测试的功能,开发包的android.test下面便是专门用来单元测试的类.单元测试的作用是通过模拟文本输入和手势输入(如点击操作),从而让app自动执行一系列的操作,这样就能够检查程序是否运行正常. 下面是搭建测试工程的具体步骤: 1.首先当然你得有一个待测试的app工程,最简单的如带有一个编辑框的Hello World工程: 2.其次在ADT中创建测试工程,操作步骤为依次选择菜单"File"--"New&q

Android开发笔记(一百一十一)聊天室中的Socket通信

Socket通信 基本概念 对于程序开发来说,网络通信的基础就是Socket,但因为是基础,所以用起来不容易,今天我们就来谈谈Socket通信.计算机网络有个大名鼎鼎的TCP/IP协议,普通用户在电脑上设置本地连接的ip时,便经常看到下图的弹窗,注意红框部分已经很好地描述了TCP/IP协议的作用. TCP/IP是个协议组,它分为三个层次:网络层.传输层和应用层: 网络层包括:IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议. 传输层包括:TCP协议.UDP协议. 应用层包括:HT

Android开发笔记(一百一十二)开发工具

Eclipse/ADT ADT是Google在Eclipse基础上封装了Android开发工具的环境,最新版本是2014年7月2日发布的adt-bundle-windows-x86_64-20140702,之后Google推出自己的开发环境Android Studio,就不再更新ADT了.不过基于Eclipse的广泛使用,当前还是有不少app使用ADT进行开发. 在Eclipse上安装插件的步骤如下:依次选择菜单"Help"--"Install New Software...