四大组件:
activity 显示。
contentProvider 对外暴露自己的数据给其他的应用程序。
BroadcastReceiver 广播接收者,必须指定要接收的广播类型。必须明确的指定action
service 服务,是运行后台,它是没有界面的。对某件事情进行监听。
一、广播:事件。
普通广播: 是异步的。会广播接收者同时接收,不能被中断
sendBroadcast()
有序广播: 是同步的。会根据广播接收的优先级进行接收,是可以中断 短信到来广播
sendOrderBroadcast()
-1000 ~ 1000
如果有序广播明确的指定了广播接收者,他是无法被中断的。
广播接收者的订阅:
1 在清单文件里面指定
<receiver android:name=".SmsReceiver"> <intent-filter android:priority="1000"> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter> </receiver>
2 在代码里面指定
IntentFilter filter = new IntentFilter(); filter.setPriority(1000); filter.addAction("android.provider.Telephony.SMS_RECEIVED"); registerReceiver(new SmsReceiver(), filter);
当广播接收者一旦接收到广播,它会执行里面的onReceive()方法,但是里面是不能执行耗时的操作,这个方式如果在10s之内没有执行完毕,就会出现anr异常,也就是应用无响应异常
练习1:利用广播拦截短信、拦截外拨电话、增加IP拨号功能
package com.shellway.mysmsreceiver; import android.support.v7.app.ActionBarActivity; import android.content.IntentFilter; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //代码注册广播接收者 IntentFilter filter = new IntentFilter(); filter.setPriority(1000); filter.addAction("android.provider.Telephony.SMS_RECEIVED"); registerReceiver(new SmsReceiver(), filter); } }
MainActivity.java
package com.shellway.mysmsreceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.text.BoringLayout; import android.util.Log; public class PhoneReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //得到外拨电话 String number = getResultData(); Log.i("i", number); // abortBroadcast();//因为底层明确了广播接收者,所以这种中断打电话的效果不行 // setResultData(null);//应该用这种方式来中断打电话 // 给用户设置IP拨号 setResultData("17951"+number); } }
PhoneReceiver.java
package com.shellway.mysmsreceiver; import java.text.SimpleDateFormat; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.telephony.SmsMessage; import android.telephony.gsm.SmsManager; import android.util.Log; public class SmsReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub Log.i("i", "已经接收到短信"); Bundle bundle = intent.getExtras(); Object[] objects = (Object[]) bundle.get("pdus"); for (Object object : objects) { SmsMessage smsMessage = SmsMessage.createFromPdu((byte[])object); //获取短信内容 String body = smsMessage.getDisplayMessageBody(); //获取来短信的电话号码 String addr = smsMessage.getDisplayOriginatingAddress(); //获取短信到来的时间 Long time = smsMessage.getTimestampMillis(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date = sdf.format(time); Log.i("i", "电话号码:"+addr); Log.i("i", "短信内容: "+body); Log.i("i","时间: "+ date); if(addr.equals("5558")){ //中断广播,注意在配置文件里要设置好这个接收者的优先级为最高 abortBroadcast(); //拦截消息转发给第三方 android.telephony.SmsManager smsManager = android.telephony.SmsManager.getDefault(); smsManager.sendTextMessage("5554", null, addr + ","+ body +","+ date, null, null); } } } }
SmsReceiver.java
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.shellway.mysmsreceiver" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" /> <uses-permission android:name="android.permission.RECEIVE_SMS"/> <uses-permission android:name="android.permission.SEND_SMS"/> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 这里使用了代码来实现广播接收者的注册来代替 <receiver android:name=".SmsReceiver"> <intent-filter android:priority="1000"> <action android:name="android.provider.Telephony.SMS_RECEIVED"></action> </intent-filter> </receiver> --> <receiver android:name=".PhoneReceiver"> <intent-filter android:priority="1000"> <action android:name="android.intent.action.NEW_OUTGOING_CALL"/> </intent-filter> </receiver> </application> </manifest>
AndroidManifest.xml
运行结果截图:
练习2:自定义广播(这里演示自己接收自己发出的广播)
package com.shellway.customreceiver; import android.support.v7.app.ActionBarActivity; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.EditText; public class MainActivity extends ActionBarActivity { private EditText et_data; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获取用户输入的数据 et_data = (EditText) findViewById(R.id.et_data); } public void send(View view){ String data = et_data.getText().toString(); //创建一个意图 Intent intent = new Intent(this,CustomReceiver.class); //给我这个广播自定义一个动作名称 intent.setAction("com.shellway.CustomReceiver"); //往意图里封装数据 intent.putExtra("data", data); //发送的是普通广播,不是有序广播 sendBroadcast(intent); } }
MainActivity.java
package com.shellway.customreceiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class CustomReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //接收广播过来的数据 String data = intent.getStringExtra("data"); Log.i("i", data); } }
CustomReceiver.java
<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:layout_width="fill_parent" android:layout_height="wrap_content" android:text="要广播的数据:" /> <EditText android:id="@+id/et_data" android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="请输入要广播发送的数据" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="send" android:text="开始广播" /> </LinearLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.shellway.customreceiver" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".CustomReceiver"> <intent-filter> <action android:name="com.shellway.CustomReceiver"></action> </intent-filter> </receiver> </application> </manifest>
AndroidManifest.xml
运行结果截图:
二、service:服务
运行于后台,没有界面,对某件事情进行监听。它不能自己运行,必须手动激活。
当服务被创建的时候会调用一个onCreate()方法。
练习3:通过服务监听电话的呼叫状态
package com.shellway.myservice; import android.support.v7.app.ActionBarActivity; import android.telephony.TelephonyManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void startService(View view){ //创建一个意图 Intent intent = new Intent(this,MyService.class); //启动一个服务 startService(intent); } }
MainActivity.java
package com.shellway.myservice; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Log; public class MyService extends Service { //定义一个系统默认的电话服务管理者 private TelephonyManager tm; private MyPhoneStateListener listener; //当服务被创建的时候调用该方法 @Override public void onCreate() { super.onCreate(); listener = new MyPhoneStateListener(); tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); //监听电话的呼叫状态 tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE); } private final class MyPhoneStateListener extends PhoneStateListener{ @Override public void onCallStateChanged(int state, String incomingNumber) { // TODO Auto-generated method stub super.onCallStateChanged(state, incomingNumber); switch (state) { case TelephonyManager.CALL_STATE_IDLE://闲置状态 Log.i("i", "闲置状态"); Log.i("i", incomingNumber); break; case TelephonyManager.CALL_STATE_OFFHOOK://接听状态 Log.i("i", "接听状态"); break; case TelephonyManager.CALL_STATE_RINGING://响铃状态 Log.i("i", "响铃状态"); break; default: break; } } } @Override public IBinder onBind(Intent intent) { return null; } }
MyService.java
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="startService" android:text="启动一个监听电话服务" /> </LinearLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.shellway.myservice" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" /> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".MyService"></service> </application> </manifest>
AndroidManifest.xml
运行结果截图:
startService()去启动服务。只能通过stopService()的方式去停止
onCreate() 只会被调用一次
onStart() 只要启动就会多次调用
onDestory() 只会被调用一次
bindService().
09-01 04:10:53.014: E/ActivityThread(5415):
Activity cn.itcast.service.MainActivity has leaked ServiceConnection [email protected] that was
originally bound here
onCreate()只会被调用一次
onBind()只会被调用一次
onUnbind()只会被调用一次
onDestroy()只会被调用一次
如果访问者退出,需要把连接释放。
可以多次绑定,但是解绑服务只能执行一次,多次解绑就会出错。
服务里面能否执行耗时的操作?
服务里面是不能执行耗时的操作。如果需要执行耗时的操作:可以开启子线程来执行。
在开发的时候往往都需要和服务进行通信,就需要使用bindService()来启动服务。
调用服务里面的方法:
1 设计一个接口:IStudentQueryService Student queryStudent(int no);
2 在activity里面进行绑定操作:
bindService(intent,conn,flag);
3 写一个连接实现类:
private final class MyServiceConnection implements ServiceConnection{ public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub ibinder = (IStudnetQueryService) service; } public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub conn = null; ibinder = null; } }
4 在activity里面用IStudentQueryService 来接收onServiceConnected(ComponentName name, IBinder service)
返回过来的IBinder对象。
5 写一个StudentService extends Service ,重写onBind()
编译一个内部类StudentBinder extends Binder implements IStudnetQueryService
6 创建StudentBinder的对象,通过onBind()方法返回。
7 在activity通过onBind()返回的对象调用服务里面的方法。
上面的操作是一个本地的服务。在开发的时候有可能还会去调用别人应用里面提供好的服务。
远程绑定服务,调用服务里面的方法。
1 编写一个接口,再把接口文件修改为aidl,不能有修饰符。
如果我们使用了自定义对象需要实现Parcelable接口,还需要定义个一个aidl文件对这个类进行一个描述(Student.aidl).
编译器就会在gen目录下面生成对应的xx.java文件
2 在服务里面创建一个内部类:extends Stub 实现未实现的方法。
生成这个类的对象通过onBind()方法返回。
3 在客户端绑定服务。
注意:
public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub ibinder = Stub.asInterface(service); }
注意啦:本章还没完善,后面会继续更新的。。。