您的位置:首页 > 移动开发 > Android开发

2016-05-08Android之广播+服务上

2016-05-08 06:04 603 查看
##1. 广播接收者概念
BroadCastReceiver,是Android四大组件之一。必须注册。
1. 注册方式:1)静态注册2)动态注册

##2. IP拨号器 有序广播

activity_main.xml
<EditText
android:id="@+id/et_ipnum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="请输入IP号码" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btn"
android:text="保存并退出"
android:layout_gravity="center"
android:textColor="#f00"
android:onClick="btn_onclick"/>

MainActivity

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_ipnum = (EditText) findViewById(R.id.et_ipnum);
/*将et_ipnum的数据存在sp上
* 获得sp对象getSharedPreferences("IPnum", MODE_PRIVATE);
* IPnum   系统自己加上.xml 后缀  MODE_PRIVATE 文件 权限为私有
*/
sp = getSharedPreferences("IPnum", MODE_PRIVATE);
/*
* 数据的回显
* sp.getString
*/
String ipnum = sp.getString("ipnum", "");
et_ipnum.setText(ipnum);

}
public void btn_onclick(View view) {
String ip_num = et_ipnum.getText().toString().trim();
/*
* 将数据保存在sp中
* sp.edit()先编辑一下
* putString存数据
* .commit()最后提交
*/
sp.edit().putString("ipnum", ip_num).commit();
Toast.makeText(this, "ip号码保存成功"+ip_num, Toast.LENGTH_LONG).show();
/*
* 退出当前activity
*/
finish();
}

注册清单
<receiver
android:name="com.guyulei.broadcast1.IpNumBroadCast">
<intent-filter >
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
</intent-filter>
</receiver>
加权限:
android.permission.PROCESS_OUTGOING_CALLS

IpNumBroadCast extends BroadcastReceiver

public class IpNumBroadCast extends BroadcastReceiver {
private static final String TAG = "guyulei";
private SharedPreferences sp;

/*
* 接收到广播时 系统回调
*/
@Override
public void onReceive(Context context, Intent intent) {
/*注册清单
*<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
*/
/*加权限android.permission.PROCESS_OUTGOING_CALLS
* 获取用户拨打的号码并没有封装在intent内
* 在函数内getResultData();
*/
String resultData = getResultData();
//获取保存在sp内的ipnum
sp = context.getSharedPreferences("IPnum",Context.MODE_PRIVATE);
String ip_num = sp.getString("ipnum", "");
//修改电话号码为ip+resultData
String newnum = ip_num+resultData;
//将号码打出去对应getResultData()为setResultData
setResultData(newnum);

Log.d(TAG, "电话号码被修改成了"+newnum);
}
}

##3. 案例-短信黑名单(***)

MainActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

et_blacknum = (EditText) findViewById(R.id.et_blacknum);
//将黑名单号码保存在sp
sp = getSharedPreferences("BlackNum", MODE_PRIVATE);
//回显
String blacknum = sp.getString("blacknum", "");
et_blacknum.setText(blacknum);

}
public void saveandexit(View view) {
String blacknum = et_blacknum.getText().toString().trim();
//又忘了提交 妈的 commit()
sp.edit().putString("blacknum", blacknum).commit();
Toast.makeText(this, "黑名单保存成功", Toast.LENGTH_LONG).show();
finish();
}
清单文件:  优先级  短信广播
<receiver
android:name="com.guyulei.broadcast.BlackNumBroadCast">
<intent-filter
android:priority="1000">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>

添加的权限
<uses-permission android:name="android.permission.RECEIVE_SMS"/>

BlackNumBroadCast extends BroadcastReceiver:
public void onReceive(Context context, Intent intent) {
//权限:android.permission.RECEIVE_SMS
/*
* 获取短信的内容
* pdus一种短信协议 里面封装数据
*/
Bundle bundle = intent.getExtras();
//数组中 的每个元素代表一条短信
Object[] pdus = (Object[]) bundle.get("pdus");
for (int i = 0; i < pdus.length; i++) {
byte[] pdu = (byte[]) pdus[i];
//将pdu字节数据转换为短信对象
SmsMessage smsMessage = SmsMessage.createFromPdu(pdu);
String address = smsMessage.getOriginatingAddress();

String body = smsMessage.getMessageBody();
//log
Log.d(TAG, "接收到来自"+address+"的短信的内容为:"+body);

//获取用户设置的黑名单
sp = context.getSharedPreferences("BlackNum", Context.MODE_PRIVATE);
String blacknum = sp.getString("blacknum", "");
//若是黑名单 则拉黑 否则显示
if(blacknum.equals(address)){
//拦截
Log.d(TAG, "这条短信被我拦截了!"+address+"..."+body);
//终止有序的广播
abortBroadcast();
}else{
//不拦截
}
}
}

###3.1 广播的分类
1. 无序广播(Normal BroadCast)
1. 特点:不能被拦截,理论上所有的广播接收者可以同时接收到。
2. 有序广播(Ordered Broadcast)
1. 特点:可以被拦截,哪个广播接收者的优先级高,哪个就优先接收

###3.2 广播接收者的优先级
1. 优先级的配置

<receiver android:name="com.example.blacknum.BalckNumReceiver">
<intent-filter android:priority="2000">
<action android:name="android.provider.Telephony.SMS_RECEIVED"></action>
</intent-filter>
</receiver>

2. 优先级的范围
系统默认应用广播接收者的优先级是0,Google推荐的优先级范围是[-1000,+1000]

###3.2 终止有序广播
1. abortBroadcast();

##4. 案例-监听手机网络状态(***)
###4.1 获取到Android手机的网络状态
1. 添加获取网络状态权限

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
2. 新的API

private void getNetState() {
//获取网络管理器的实例
ConnectivityManager connectivityManager = (ConnectivityManager)this.getSystemService(CONNECTIVITY_SERVICE);
//通过connectivityManager获取当前网络状态
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
//如果当前手机没有任何可用的网络,返回null
if (activeNetworkInfo==null) {
tv_state.setText("当期没有可用网络");
Toast.makeText(this, "当前没有可用网络", Toast.LENGTH_SHORT).show();
return;
}else {
//获取网络名称 WIFI MOBILE(LTE UTMS)
String typeName = activeNetworkInfo.getTypeName();
//如果是MOBILE,进一步区分是4G还是3G或者2G
String subtypeName = activeNetworkInfo.getSubtypeName();
tv_state.setText("当前网络:"+typeName+"/"+subtypeName);
Toast.makeText(this, typeName+"/"+subtypeName, Toast.LENGTH_LONG).show();
}
}
3. 忘记反注册时的异常

05-06 11:31:37.607: E/ActivityThread(26186): Activity com.example.listennetstate.MainActivity has leaked IntentReceiver com.example.listennetstate.MainActivity$NetStateReceiver@16c80d17 that was originally registered here. Are you missing a call to unregisterReceiver()?
4. 当Activity退出的时候,反注册广播接收者

if (netStateReceiver!=null) {
//反注册广播接收者
unregisterReceiver(netStateReceiver);
netStateReceiver = null;
}

###4.2 复习老API
1.获取布局填充器(打气筒)

LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);

###4.3 广播接收者的生效周期
1. 静态注册的
1. 特点:一旦注册成功了,永久生效,只要不把APP卸载。在静态文件中注册的广播,如果该应用一旦启动过一次,那么就会被注册到系统中。
2. 动态注册的
1. 特点:如果是在Activity中注册的,那么Activity销毁的时候,广播接收者也失效了

##5. 案例-监听开机启动(***)只能在清单文件中注册
开启启动的时候,系统会发出一个无序的广播,我们只需要监听该广播事件就可以实现开机启动。

1. 开机启动的Action
2. 开机启动需要添加的权限

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

###5.1 在广播中启动Activity的bug

05-06 03:47:54.440: E/AndroidRuntime(1432): Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
1. 解决方案
1. 在广播中启动Activity的时候,需要指定要创建一个任务栈

//添加flag,让系统为我们的Activity创建一个新的任务栈
intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

##6. 案例-监听屏幕的关闭和打开
1. 特别频繁的操作,只能动态的注册,比如:屏幕的开关,电量的改变。

##7. 发送自定义广播
###7.1 发送无序广播

//发送无序广播
sendBroadcast(intent);

###7.2 发送有序广播
1. 优先级
1. 优先级相同的情况下,谁先注册,谁先接受
2. 优先级相同的情况下,动态注册优先于静态注册接收到消息

2. 有序广播可以被拦截,但是最终广播接收者不能被拦截

##8. Service的基本概念(*)
1. 定义:

A Service is an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another application.

2. Service是Android四大组件之一,使用前必须在AndroidManifest.xml中注册。

3. 服务有两种形态
1. 启动Stated
2. 绑定Bound

4. 服务的注册
<service android:name="com.example.startservice.MyService"/>

##9. Service的启动和停止
1. startService
2. stopService

##10. startService的生命周期

1. 生命周期方法
1. onCreate
2. onStateCommand
3. onDestroy

##11. 案例-来电窃听器
清单文件
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

<receiver
android:name="com.guyulei.eavesdrop.BootStartReceiver">
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>

<service
android:name="com.guyulei.eavesdrop.EavesdropService"></service>
</application>

BootStartReceiver extends BroadcastReceiver:

private static final String TAG = "guyulei";

@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "开机服务启动了");
//启动service
context.startService(new Intent(context,EavesdropService.class));
}

EavesdropService extends Service:
private TelephonyManager telephonymanager;
private MediaRecorder recorder;
private boolean isStart;
private boolean isRinging;

public void onCreate() {
//获取系统电话管理器
telephonymanager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
/*
* 参数1:new PhoneStateListener() 监听器,有很多回调函数,使用哪个由第二个参数说了算
* 参数2:要监听事件的int常量
*/
telephonymanager.listen(new PhoneStateListener(){

/*
* 参数1:int state电话的当前状态
* 参数2:打进来的电话号码
* 电话状态改变时  回调该方法 并返回改变后的状态
*/
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch(state){
//空闲状态
case TelephonyManager.CALL_STATE_IDLE:
//释放资源
if(isStart){
recorder.release();
recorder.stop();
isStart = false;
}else if(isRinging){
recorder.reset();
recorder.release();
isRinging = false;
}
break;
//响铃状态
case TelephonyManager.CALL_STATE_RINGING:
//准备录音机
if(recorder == null){
recorder = new MediaRecorder();
}
//设置音频源 麦克风
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置文件输出的格式
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
//设置音频的编码
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

//设置输出保存的文件
recorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+incomingNumber+"-"+new Date().getTime()+".3gp");
isRinging = true;
break;
//接通状态
case TelephonyManager.CALL_STATE_OFFHOOK:
//录音
try {
//prepare()即创建文件
recorder.prepare();
recorder.start();
isStart = true;
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}

}, PhoneStateListener.LISTEN_CALL_STATE);
}
1. 步骤
1. 定义一个开机启动的广播,开机的的时候,启动Service
2. 在Service的onStart方法中监听用户的电话状态(新)
3. 当有新来电的时候,开始录音,当挂电话的时候结束录音(新)

2. 电话的三种状态
1. 空闲状态
2. 响铃状态
3. 接听状态
3. 注意添加权限、
1. 开机启动广播权限
2. 添加写sdcard权限
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: