Android 手机状态示例






4.78/5 (34投票s)
一个简单的 Android 应用程序,用于显示 Android 智能手机的手机详细信息、电池状态和数据连接状态。
介绍
本文将利用 Android SDK 中 android.telephony 和 android.os.batterymanager 包中的 API,创建一个简单而智能的应用程序,以显示 Android 智能手机的手机详细信息、电池状态和数据连接状态。
背景
Android 框架为您提供了构建一流应用体验所需的一切。其核心组件是 Android 软件开发工具包 (Android SDK),它提供了构建、测试和部署应用程序所需的所有工具。在本文中,我们将主要关注以下包/类,以显示与蜂窝电话相关的非常常见的一些信息。
服务提供商相关
- 服务状态
- 小区位置
- 呼叫状态
- 连接状态
- 信号强度
- 数据交换。
手机特定
- 设备 ID
- 电话号码
- 软件版本
- 运营商名称
- SIM 卡国家代码
- SIM 卡运营商
- SIM 卡序列号
- 用户 ID
- 网络类型
- 手机类型
- 电池相关信息
为了从 Android 系统检索上述信息,我们必须在 AndroidManifest.xml 文件中包含我们 Android 应用程序的权限。以下列表向用户展示了在 AndroidManifest.xml 文件中请求特定信息时需要包含的适当权限。
Android manifest 中的权限
否 | 信息 | 权限 |
1 | 小区位置 | ACCESS_COARSE_LOCATION ACCESS_FINE_LOCATION |
2 | 呼叫状态 | |
3 | 数据连接状态 | |
4 | 信号强度 | |
5 | 数据方向状态 | |
6 | 服务状态 | |
7 | 设备 ID | READ_PHONE_STATE |
8 | 电话号码 | READ_PHONE_STATE |
9 | 运营商名称 | READ_PHONE_STATE |
10 | SIM 卡运营商 | READ_PHONE_STATE |
11 | SIM 卡国家代码 | READ_PHONE_STATE |
12 | SIM 卡序列号。 | READ_PHONE_STATE |
13 | 用户 ID | READ_PHONE_STATE |
14 | 网络类型 | ACCESS_NETWORK_STATE |
手机类型 |
设计 Android 应用程序布局
下一步是设计一个布局文件,用于显示我们感兴趣的信息。为了清晰起见,我在这个示例中包含了一个标签式布局。第一个标签将显示电话相关信息,第二个标签将显示手机当前的电池状态。
main.xml
此布局将托管一个标签控件。根据选择,它将显示“手机状态”或“电池状态”信息。
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@android:id/tabhost">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</TabWidget>
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
</LinearLayout>
</TabHost>
phonestatus.xml
此布局将显示电话相关信息,如呼叫状态、小区位置、连接状态、信号级别、数据活动、电话号码、IMEI 码、设备软件版本等。
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:scrollbarStyle="insideOverlay"
android:scrollbarAlwaysDrawVerticalTrack="false">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<!--Service State-->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:text="@string/tvServiceState" style="@style/labelStyleRight"/>
<TextView android:id="@+id/serviceState_info" style="@style/textStyle"/>
</LinearLayout>
<!--cell location -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:text="@string/tvCellLocation" style="@style/labelStyleRight"/>
<TextView android:id="@+id/cellLocation_info" style="@style/textStyle"/>
</LinearLayout>
<!--Call State-->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:text="@string/tvCallState" style="@style/labelStyleRight"/>
<TextView android:id="@+id/callState_info" style="@style/textStyle"/>
</LinearLayout>
<!--Data Connection State-->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:text="@string/tvConnState" style="@style/labelStyleRight"/>
<TextView android:id="@+id/connectionState_info" style="@style/textStyle"/>
</LinearLayout>
<!--Signal level -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:text="@string/tvSignalLevel" style="@style/labelStyleRight"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:orientation="horizontal">
<ProgressBar android:id="@+id/signalLevel" style="@style/progressStyle"/>
<TextView android:id="@+id/signalLevelInfo" style="@style/textSmallStyle"/>
</LinearLayout>
</LinearLayout>
<!--Data Activity-->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:text="@string/strData" style="@style/labelStyleRight"/>
<ImageView android:id="@+id/dataDirection" style="@style/imageStyle"/>
</LinearLayout>
<TextView android:id="@+id/device_info" style="@style/labelStyleLeft"/>
</LinearLayout>
</ScrollView>
一旦数据已在(稍后描述的)PhoneStatusActivity 中分配。布局文件将生成如下屏幕:
battery.xml
该布局将用作显示手机电池特定信息的占位符。布局文件包含一个简单的 TextView。此 TextView 的数据源将在(稍后描述的)BatteryStatusActivity.java 中设置。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView android:id="@+id/batterylevel" style="@style/textStyleCenter"/>
</LinearLayout>
布局文件将输出如下内容:
Android 布局样式
Android 中的样式类似于网页中的层叠样式表 (CSS)
设计,即它们允许您将设计与
内容分离。为了在电池状态和手机状态布局中获得一致的外观和感觉,我们将在 res/values 文件夹中包含一个 styles.xml 文件,其中将指定高度、内边距、字体颜色、字体大小等属性,
背景颜色、文本对齐等。
要包含一个 style.xml 文件,请在 Android 项目中,让我们在 res/values 文件夹下创建一个新的 XML 文件,并将其命名为 styles.xml。
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="AppTheme" parent="android:Theme.Light" />
<color name="blue">#0000FF</color>
<color name="white">#FFFFFF</color>
<color name="red">#FF0000</color>
<color name="yellow">#FFF200</color>
<color name="green">#00FF00</color>
<color name="black">#000000</color>
<style name="labelStyleRight">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">0.5</item>
<item name="android:textSize">15dip</item>
<item name="android:textStyle">bold</item>
<item name="android:layout_margin">10dip</item>
<item name="android:gravity">center_vertical|right</item>
</style>
<style name="labelStyleLeft">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">0.5</item>
<item name="android:textSize">15dip</item>
<item name="android:textStyle">bold</item>
<item name="android:layout_margin">10dip</item>
<item name="android:gravity">center_vertical|left</item>
</style>
<style name="textStyle">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">0.5</item>
<item name="android:textSize">15dip</item>
<item name="android:textStyle">bold</item>
<item name="android:layout_margin">10dip</item>
<item name="android:gravity">center_vertical|left</item>
</style>
<style name="textStyleCenter">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">0.5</item>
<item name="android:textSize">15dip</item>
<item name="android:textStyle">bold</item>
<item name="android:layout_margin">10dip</item>
<item name="android:gravity">center</item>
</style>
<style name="textSmallStyle">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">fill_parent</item>
<item name="android:layout_weight">0.5</item>
<item name="android:textSize">10dip</item>
<item name="android:layout_margin">10dip</item>
<item name="android:gravity">center_vertical|left</item>
</style>
<style name="progressStyle">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_margin">10dip</item>
<item name="android:layout_weight">0.5</item>
<item name="android:indeterminateOnly">false</item>
<item name="android:minHeight">20dip</item>
<item name="android:maxHeight">20dip</item>
<item name="android:progress">15</item>
<item name="android:max">100</item>
<item name="android:gravity">center_vertical|left</item>
<item name="android:progressDrawable">@android:drawable/progress_horizontal</item>
<item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item>
</style>
<style name="imageStyle">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">0.5</item>
<item name="android:src">@drawable/nodata</item>
<item name="android:scaleType">fitStart</item>
<item name="android:layout_margin">10dip</item>
<item name="android:gravity">center_vertical|left</item>
</style>
</resources>
使用代码
1. AndroidManifest.xml 文件中的权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_UPDATES"/>
2. android.telephony.TelephonyManager
与设备上的电话服务相关的任何信息都必须通过 TelephonyManager 类进行访问。此类不能直接实例化,而是通过以下方式检索其实例的引用:
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
2.1 PhoneStateListener 对象
PhoneStateListener 类负责监视智能手机设备上特定电话状态的变化。
我们将覆盖我们希望从 PhoneStateListener 对象接收更新的状态的方法,并将 PhoneStateListener 实例与 LISTEN_ 标志的按位或一起传递给 TelephonyManager.listen()。应用程序现在已准备好根据电话事件更新任何视图。
int events = PhoneStateListener.LISTEN_SIGNAL_STRENGTH |
PhoneStateListener.LISTEN_DATA_ACTIVITY |
PhoneStateListener.LISTEN_CELL_LOCATION |
PhoneStateListener.LISTEN_CALL_STATE |
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
PhoneStateListener.LISTEN_SERVICE_STATE;
tm.listen(phoneListener, events);
2.1.1 监听电话状态
指定我们希望从 TelephonyManager 接收的更新后,PhoneStateListener 将处理每次电话状态更改的回调方法。我们将记录 PhoneStateListener 类提供的相应处理程序方法中的所有此类状态更改,并在视图上更新状态。
注意: 出于演示目的,我在本文中只讨论了少数 PhoneStateListener 回调方法。此外,为了便于理解,回调实现方法被过度简化了。这些方法除了更新 TextView 或在某些情况下更改缩略图图像之外,别无他用。
2.1.1.1. onCallStateChanged
设备呼叫状态更改时调用此回调。在此方法中,我们将根据状态记录手机是空闲、响铃还是空闲状态。
TelephonyManager.CALL_STATE_IDLE | 设备呼叫状态:无活动 |
TelephonyManager.CALL_STATE_RINGING | 设备呼叫状态:有新来电并且正在 响铃或等待 |
TelephonyManager.CALL_STATE_OFFHOOK | 至少存在一个呼叫 正在拨号、活动或保持中,并且没有呼叫正在响铃 或等待 |
/*
* Call State Changed
* */
public void onCallStateChanged(int state, String incomingNumber) {
String phoneState = "UNKNOWN";
switch(state){
case TelephonyManager.CALL_STATE_IDLE :
phoneState = "IDLE";
break;
case TelephonyManager.CALL_STATE_RINGING :
phoneState = "Ringing (" + incomingNumber + ") ";
break;
case TelephonyManager.CALL_STATE_OFFHOOK :
phoneState = "Offhook";
break;
}
//Sets the CallState on a textview
setTextViewText(info_ids[INFO_CALL_STATE_INDEX], phoneState);
super.onCallStateChanged(state, incomingNumber);
}
2.1.1.2. onDataConnectionStateChanged
数据连接状态发生变化时调用此回调方法。它可以返回以下 4 种可能性之一
TelephonyManager.DATA_CONNECTED | 表示 IP 流量应该可用 |
TelephonyManager.DATA_CONNECTING | 正在设置数据连接 |
TelephonyManager.DATA_DISCONNECTED | 表示 IP 流量不可用 |
TelephonyManager.DATA_SUSPENDED | 连接已建立,但 IP 流量暂时不可用。例如,在 2G 网络中, 当收到语音通话时,数据活动可能会暂停 |
/*
* Cellphone data connection status
* */
public void onDataConnectionStateChanged(int state) {
String phoneState = "UNKNOWN";
switch(state){
case TelephonyManager.DATA_CONNECTED :
phoneState = "Connected";
break;
case TelephonyManager.DATA_CONNECTING :
phoneState = "Connecting..";
break;
case TelephonyManager.DATA_DISCONNECTED :
phoneState = "Disconnected";
break;
case TelephonyManager.DATA_SUSPENDED :
phoneState = "Suspended";
break;
}
//Sets the data connection status on a textview
setTextViewText(info_ids[INFO_CONNECTION_STATE_INDEX], phoneState);
super.onDataConnectionStateChanged(state);
}
2.1.1.3. onDataActivity
数据活动状态改变时调用此回调。可能的活动状态如下所示。
- TelephonyManager.DATA_ACTIVITY_NONE - 无 IP 流量。
- TelephonyManager.DATA_ACTIVITY_IN - 正在接收 IP 流量
- TelephonyManager.DATA_ACTIVITY_OUT - 正在发送 IP 流量
- TelephonyManager.DATA_ACTIVITY_INOUT - 正在发送和接收 IP 流量
- TelephonyManager.DATA_ACTIVITY_DORMANT - 数据连接活动,但物理链路已断开
/*
* Data activity handler
* */
public void onDataActivity(int direction) {
String strDirection = "NONE";
switch(direction){
case TelephonyManager.DATA_ACTIVITY_IN :
strDirection = "IN";
break;
case TelephonyManager.DATA_ACTIVITY_INOUT:
strDirection = "IN-OUT";
break;
case TelephonyManager.DATA_ACTIVITY_DORMANT:
strDirection = "Dormant";
break;
case TelephonyManager.DATA_ACTIVITY_NONE:
strDirection="NONE";
break;
case TelephonyManager.DATA_ACTIVITY_OUT:
strDirection="OUT";
break;
}
//Updates the status Data Activity on a ImageView
setDataDirection(info_ids[INFO_DATA_DIRECTION_INDEX],direction);
super.onDataActivity(direction);
}
setDataDirection 是一个用户定义的方法,它根据 DataActivity 的状态更新 ImageView 上的相应缩略图图像。
private void setDataDirection(int id, int direction){
int resid = getDataDirectionRes(direction);
((ImageView)findViewById(id)).setImageResource(resid);
}
2.1.2 结束监听电话状态
为了让应用程序停止监听 TelephonyManager 类的更新,我们现在将 PhoneStateListener 实例与 LISTEN_NONE 标志一起传递给 TelephonyManager.listen() 方法。
tm.listen(phoneListener, PhoneStateListener.LISTEN_NONE);
2.2 查询基本手机信息
TelephonyManager 方便地提供了访问与手机相关基本信息的方法,例如用户 ID、IMEI 码、电话号码、网络类型等。与此手机状态信息相关的代码包含在 PhoneStatusActivity.java 中。
//Get the IMEI code
String deviceid = tm.getDeviceId();
//Get the phone number string for line 1,For ex: the MSISDN for a GSM phone
String phonenumber = tm.getLine1Number();
//Get the software version number for the device, For ex: the IMEI/SV for GSM phones
String softwareversion = tm.getDeviceSoftwareVersion();
//Get the alphabetic name of current registered operator.
String operatorname = tm.getNetworkOperatorName();
//Get the ISO country code equivalent for the SIM provider's country code.
String simcountrycode = tm.getSimCountryIso();
//Get the Service Provider Name (SPN).
String simoperator = tm.getSimOperatorName();
//Get the serial number of the SIM, if applicable. Return null if it is unavailable.
String simserialno = tm.getSimSerialNumber();
//Get the unique subscriber ID, for example, the IMSI for a GSM phone
String subscriberid = tm.getSubscriberId();
//Get the type indicating the radio technology (network type)
//currently in use on the device for data transmission.EDGE,GPRS,UMTS etc
String networktype = getNetworkTypeString(tm.getNetworkType());
//This indicates the type of radio used to transmit voice calls
//GSM,CDMA etc
String phonetype = getPhoneTypeString(tm.getPhoneType());
用户定义的实用方法返回等效字符串
private String getNetworkTypeString(int type){
String typeString = "Unknown";
switch(type)
{
case TelephonyManager.NETWORK_TYPE_EDGE:
typeString = "EDGE"; break;
case TelephonyManager.NETWORK_TYPE_GPRS:
typeString = "GPRS"; break;
case TelephonyManager.NETWORK_TYPE_UMTS:
typeString = "UMTS"; break;
default:
typeString = "UNKNOWN"; break;
}
return typeString;
}
private String getPhoneTypeString(int type){
String typeString = "Unknown";
switch(type)
{
case TelephonyManager.PHONE_TYPE_GSM: typeString = "GSM"; break;
case TelephonyManager.PHONE_TYPE_NONE: typeString = "UNKNOWN"; break;
default:
typeString = "UNKNOWN"; break;
}
return typeString;
}
在此示例中,一旦我们收集了所有必需的信息,我们将在 deviceInfo TextView 上显示它们。
deviceinfo += ("Device ID: " + deviceid + "\n");
deviceinfo += ("Phone Number: " + phonenumber + "\n");
deviceinfo += ("Software Version: " + softwareversion + "\n");
deviceinfo += ("Operator Name: " + operatorname + "\n");
deviceinfo += ("SIM Country Code: " + simcountrycode + "\n");
deviceinfo += ("SIM Operator: " + simoperator + "\n");
deviceinfo += ("SIM Serial No.: " + simserialno + "\n");
deviceinfo += ("Subscriber ID: " + subscriberid + "\n");
deviceinfo += ("Network Type: " + networktype + "\n");
deviceinfo += ("Phone Type: " + phonetype + "\n");
2.3 手机电池
android.os.BatteryManager 类提供有关
手机电池状态的信息,形式为字符串和常量。
例如:
我们还可以提取当前的充电状态,以及如果设备正在充电,则是否
通过 USB 或交流充电器充电,使用
- BatteryManager.BATTERY_PLUGGED_AC - 电池已连接到交流电源
- BatteryManager.BATTERY_PLUGGED_USB - 电池已连接到 USB 电源
- BatteryManager.BATTERY_STATUS_CHARGING - 电池已连接到电源并正在充电
- BatteryManager.BATTERY_STATUS_DISCHARGING - 电池正在放电
- BatteryManager.BATTERY_STATUS_FULL - 电池电量已充满
3. 智能手机状态应用程序的 Activity 类
3.1 StatusActivity 类
这是应用程序启动时调用的主 Activity。此 Activity 负责创建用于托管 PhoneStatusActivity 和 BatteryStatusActivity 的 TabHost。默认情况下,将激活 PhoneStatusActivity。
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TabHost host = getTabHost();
//Phone Status Activity
TabSpec statusspec = host.newTabSpec("Phone");
statusspec.setIndicator("Phone",getResources().getDrawable(R.drawable.nphone));
Intent phoneStatusIntent = new Intent(this, PhoneStatusActivity.class);
statusspec.setContent(phoneStatusIntent);
//Battery Status Activity
TabSpec batteryspec = host.newTabSpec("Battery");
batteryspec.setIndicator("Battery", getResources().getDrawable(R.drawable.nbattery));
Intent batteryIntent = new Intent(this, BatteryStatusActivity.class);
batteryspec.setContent(batteryIntent);
// Adding all TabSpec to TabHost
host.addTab(statusspec); //Default tab
host.addTab(batteryspec);
}
3.2 PhoneStatusActivity 类
PhoneStatusActivity 类负责显示从 TelephonyManager 收到的信息,如第 2.1 节和第 2.2 节所述。
3.3 BatteryStatusActivity 类
BatteryStatusActivity 类负责显示电池状态。BatteryManager
以粘性Intent
形式广播所有电池和充电详细信息
包括
充电状态。通过挂接到这些 Intent,我们可以持续
监视手机电池的状态。为了实现这一点,我们注册一个
BroadcastReceiver 在主 Activity 线程中运行
(BatteryStatusActivity)。
receiver 将在收到任何匹配的广播 Intent 时被调用
filter (Intent.ACTION_BATTERY_CHANGED),在主应用程序线程中。
/*
* Battery Related Broadcast event registration
* */
private void registerBatteryLevelReceiver() {
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(battery_receiver, filter);
}
注册后,每当 BroadcastReceiver 收到 Intent
广播(即来自 BatteryManager)时,我们将按如下方式更新状态。
@Override
public void onReceive(Context context, Intent intent) {
boolean isPresent = intent.getBooleanExtra("present", false);
//Battery Technology
String technology = intent.getStringExtra("technology");
//Battery Plugged Information
int plugged = intent.getIntExtra("plugged", -1);
//Battery Scale
int scale = intent.getIntExtra("scale", -1);
//Battery Health
int health = intent.getIntExtra("health", 0);
//Battery Charging Status
int status = intent.getIntExtra("status", 0);
//Battery charging level
int rawlevel = intent.getIntExtra("level", -1);
int level = 0;
Bundle bundle = intent.getExtras();
Log.i("BatteryLevel", bundle.toString());
if (isPresent) {
if (rawlevel >= 0 && scale > 0) {
level = (rawlevel * 100) / scale;
}
String info = "Battery Level: " + level + "%\n";
info += ("Technology: " + technology + "\n");
info += ("Plugged: " + getPlugTypeString(plugged) + "\n");
info += ("Health: " + getHealthString(health) + "\n");
info += ("Status: " + getStatusString(status) + "\n");
setBatteryLevelText(info);
} else {
setBatteryLevelText("Battery not present!!!");
}
}
有关监视电池状态的最佳实践的更多信息,请参阅此处。
我们完成了!好吧,差不多。在您的 Android 模拟器上运行应用程序,或最好将其部署到您的智能手机上,以查看手机的状态。您可以尝试查询不同的参数,也可以自定义 UI 更新方式,就像我在此示例中监控数据活动和信号强度一样。
编码愉快!!
历史
初始版本 - 2012 年 3 月 22 日
下载 SmartPhoneStatus.zip