activity的启动模式跟activity的任务栈
长按home会出现近期的任务,
当我们打开一个应用程序的时候,系统会为这个应用程序默认分配一个任务栈.
任务栈作用:
1.维护用户名的操作体验,
2.记录每一个应用程序操作的行为
默认情况下activity的任务栈是一个后进先出的链表,同一个activity界面存在链表里是不同的对象,默认的activity的启动模式是standard的标准的启动模式,适用于绝大多数的android应用程序的开发,
为了避免一个activity被多次重复的创建,只有用户按多次的后退键才能退出这个应用程序,可以指定activity的启动模式为singleTop的启动模式,可以避免糟糕的用户体验
singleTop的启动模式:
在<activity>标签下配置android:launchMode=singleTop
Singletop
singleTop的启动模式特点:
1.如果栈顶存在activity是要被激活的activity,他就不会再创建新的activity
2.只在栈顶存在的activity与要激活的singletop的activity不相同的时候才会创建新的activty,放在任务栈的栈项
3.如果栈顶的activity是当前点击的activity,那么activity会执行onResoume(获取焦点),还会调用onNewIntent(当一个新的意图去启活他的时候),onNewIntent一般是跟singleTop一起配置使用的
Singletask启动模式特点:
1.在开启activity的时候,会检查任务栈里面是否已经有要激活的activity的实例存在,如果存在的话清空这个存在的Activity上面的任务,复用这个存在的activity
应用场景:
浏览器是google封闭了webkit内核
打开网页
1.初始化webkit内核(耗时耗空间)如果我们把浏览启动方式做这一般的启动方式,那么我们每打开一个页面就要初始化内核,再放到任务栈里,如果用singletask只有创建一个浏览器的activity,为了避免多次创建borwseractivity的实例,保证在当前应用程序的任务栈里面只有一个浏览器activity存在避免了申请过多的系统空间,提高用户执行的效率
生命周期:
如果栈项已经是当前的activity那么只会执行onNewIntent跟onResoume,一般要重写onNewIntent方法获取到新的要访问的地址,然后把新的地址加载进来
singleInstance启动模式特点:
这种模式非常极端,他不会把创建的activity的实例放入到当前应用的任务栈里面,他会自己创建一个任务栈,并且在任务栈里面存放自己的一个实例
如果activity02配置了singleInstance,那么执行以下操作打开01-->打开02-->打开01-->打开01,执行后退的效果是01-->01-->01-->02,因为02在一个单独的任务栈中,记录了谁引用了他,当关到最后一个01,系统发现他还引用了02,他就把02关闭.
在整个手机的操作系统里面只有一个单独的任务栈,并且任务栈里面只有一个单独的实例存在
观察任务栈:
1.查看当前activity在那个任务栈里面
getTaskId()这个是返回一个id的标识符,当前activity在任务栈ID号
Inttaskid=getTaskId();
应用场景:
1.有道词典,快速取词
2.保证某一个activity只在手机里面存在一个实例,类似java单态的模式
3.紧急呼叫界面还有正在呼叫的界面
如果研究,获取当前任务栈的信息:
1.getTaskID获取当前的activity运行的任务栈的id
2.ActivityManager负责管理系统的activity实例跟任务栈的,拿到ActivityMananger服务
ActivityMangeram=(ActivityManger)getSystemService(ACTIVITY_SERVICE);
3.得到手机任务栈列表信息
List<RunningTaskInfo>runningtaskinfos=An.getRunningTasks(intmaxNum)//获取当前手机正在运行的所有任务栈列表信息,最近的排在最前面,里面的maxNum是指定你要获取任务的个数
4.循环列表得到里面的任务信息
Info.baseActivity//获取栈底的activity
Info.topActivity//获取栈顶的activity
Info.Id//任务栈的id
Info.numActivities//得到任务栈中有多少个activity
系统文件中查找相应的说明(任务栈)
DevGuide--->Tasksandbackstack
注意:两个应用程序不要包名一致,否则会程序出错
广播接收者:broadcastreceiver
如果使用收音机收呼广播条件:
1.你要有一个收音机
2.频道设置正确
3.前提要有一个电台
短信窃听器:
1.创建一个类继承系统提供的标准的收音机(创建收音机)
ExtendsBroadcastReceiver//在这个类的说明会让我们到清单文件下配置一个结点
2.清单结点添加这个类(调频道)
<receiverandroid:name=".类名">
<intent-filer>
<actionandroid:name="意图动作">
1.android.intent.action.CAMERA_BUTTON//照相机按钮被按下
2.android.intent.action.BATTERY_LOW//电池低电量
3.android.intent.actionBATTERY_CHANGED//电池电量改变
4.android.intent.action.MEDIA_BAD_REMOVAL//sd卡被异常卸载
5.android:intent.action.MEDIA_REMOVED//SD卡被卸载
6.android.intent.action.PACKAGE_INSTALL应用程序被安装
7.android.intent.action.screen_OFF屏幕被锁屏了
8.android.intent.action.REBOOT系统重启了
9android.provider.Telephony.SMS_RECEIVED一条短信到来的时候对应的事件
<intent-filter>
3.加入接收短信的权限
Android.permission.RECEIVE_SMS(接收短信权限)
4.onReceiver这个方法是当一个广播接收者接收到一个广播事件时所触发的方法(参数上下文,Intent意图)
5.在这个onReceiver方法操作我们想要的动作
1.Intent.getExtras().get("pdus");//pdus是短信的一个规范,上面语句是获取短信所代表的二进制数组的数据获取出来
Object[]pdus=(Object[])intent.getExtras().get("pdus");
2.循环遍历上面的数组
For(Objectpud:pdus){
//把pdu转化成一个短信消息对象
SmsMessagesmsMessage=SmsMessage.createFromPdu((byte[])pdu);
smsMessage.getMessageBody()得到短信正文
smsMessage.getOriginationAddress()获取发件人的地址
}
如果不希望希望的短信应用接收到,可以设置广播接收者的优先级
广播接收者分为两种
1,无序广播:异步的操作,所有的注册了广播接受者的对象都会接受到这个广播(sendBroadcast)
2.有充广播:广播会按照优先级,依次的传递给所有的广播接收者,高优先级的广播接受者会先接收到广播事件(sendOrderedBroadcast)
广播优先级配置,在<receiver>的<intent-filter>标签中配置android:priority="优先级"//优先级从-1000到1000,最高的是1000,最高优先级的广播接收者可以第一个接收广播信息并可以对信息进行处理,在onReceive()方法中进行处理
abortBroadcast();//这是终止当前的广播事件
只有在管理应用程序中才能看到广播接收的程序,在界面中是找不到的,因为不有界面
采用清单文件注册广播接收者不需要开机启动,一旦布置到手机系统上,广播接受者就开始生效了,不管当前应用程序是否是运行状态
如果两个应用都配置1000的优先级,那么先安装的那个程序会先接受广播事件
IP拔号:
获取拔打电话事件,前面加一个17951,再拔打电话
1.用sharedPreferencess存存电话号码数据,在拔打电话时
2.定义广播接收者
3.清单中配置
优先级设置1000,设置动作(外拨电话的事件<actionandroid:name="android.intent.action.NEW_OUTGOING_CALL"/>
4.外拨打电话权限
Android.permission.PROCESS_OUTGOING_CALLS
5.onReceive方法中进行事件处理
6.电话拨打的时候会向intent存放一些数据
getResultData()//这个可以获取intent里面的外拨打数据即电话号码
7.上下文中获取配置文件夹里的信息
SharedPreferencessp=context.getSharedPreferences("config".Context.MODE_PRIVATER)
8.获取设置的值
Sp.getString("ipnumber","");//前面设置的数据
9.修改结果集中的数据
setResultData(新缀加上电话号码);//其实这里是要判断的如果他的电话已经加了前缀就直接返回,否则加上前缀号码
自定义的广播
应用A发送一个自定义广播
应用B接受一个自定义的广播
应用A:
1.定义广播发送者
2.产生一个点击事件用于发送自定义的广播事件
1>.产生一个广播意图
Intentintent=newIntent();
2>自定义一个广播类型
Intent.setAction("cn.itcast.mybroadcast");
3>放一些额外的数据
intent.putExtra(name,value);//设置数据
4>发送一条广播
sendBroadcast(intent);//会发给所有人,注册了对这一短信感信息的接受者,这是一个异布的事件
sendOrderedBroadcast(intent,receiverPermission)
receiverPermission:声明的权限(作用跟加密差不多)
上面的传递有一定的顺序,一次传给一个接收者,接收者可以取消这个广播
sendOrderedBroadcast(intent,null,resultReceiver,scheduler,initialCode,initialData,initialExtras);
显示的指定了resultReceiver,这个resultreceiver是无论如何都会接受到这个广播事件.
不能通过abortbroadcast()把广播事件给终止掉.
sendStickyBroadcast(intent);//粘性广播,发送广播时intent会一直保留,直到广播事件结束,这一般是有些事件需要花费的时候比较长,比如系统中的扫描网络的操作
应用B:
1.创建一个继承BroadcastReceiver
2.清单文件里配置
配置关心的动作,前面自定义配置的动作
<actionandroid:name="cn.itcast.mybroadcast">
3.onReceiver方法中进行处理广播数据
4.onReceiver中的参数intent传过来是有数据的,我们得到数据(前面intent.putExtra中设置的name值)
Stringvalue=intent.getStringExtra("name");
广播接收者生命周期:
广播接收者一般要求在十秒钟内完成,没有完成系统就会认为这是一个应该被回收到掉的候选对象,里面的线程也有可能会被回收(内存不足的情况下),可以把线程放在一个后台的服务中
进程线程应用程序任务栈他们之间的关系?
进程是操作系统分配内存的单位,一般来说每一个应用程序都会对应一个单独进程
线程:在一个进程里面可以有多个线程执行
应用程序:理解成与用用户交互的一组界面(activity),一组activity的集合,可能打开多个进程,比如(自己写一个程序打开浏览器)
任务栈:存放开启的activity的引用的,开启一个别的应用的activity,激活别的应用的进程,activity运行在别的应用进程里面的
进程的优先级:
文档中说明:devGuide---->processesandThreads
1.前台进程(如果内存严重不足也不会试图杀死这些前台进程,保证与用户的交互)
有一个获取到焦点的activity在运行
广播接受者正在执行onReceive方法
2.可见进程
如果里面的activity只是失去焦点,但用户仍然可见
3.服务进程
进程里面有一个服务在后台运行(service)
4.后台进程
当用户进程的activity执行了onstop的方法用户不可见了
5.空进程
一个进程里面淌有任务系统组件(activity,service,receiver)存在了
内容不足是按从优先级低到高开始回收
电话窃听器:
在后台开启一个服务,监听电话服务,如果打电话就开始录音,结束后上传录间到服务器
1.创建一个类继承Service服务系统
2.清单文件中申明
在</activity>结束标记后加一个标签<serviceandroid:name=".SystemService"></service>
3.实现service的onCreate()方法.服务可以理解成一个长期在后台运行的没有界面的activity,OnCreate方法是在服务第一次被创建的时候调用的方法
4.在onCreate方法中希望监听当前手机的通话状态
1>系统系统服务:(电话服务)
Telephonymanagertm=(TelephonyManager)getSystemService(TElEPHONY_SERVICE):
2>得到整个手机电话的包装类以后,就有一些获取相关系统属性的方法
1).tm.getDeviceId()//获取当前设备的id
2)getLine1Number()//获取当前手机的号码,需要运营商的支持的,好像现在只有cdma的手机可以实现
3).getNetworkOperator()//获取运营商
4)getNetworkOperatorName()//获取运营商名称
5)getNetworkType()//获取当前网络名称
6)getPhoneType()//获取当前手机类型
7)listen(PhoneStateListener,events)//用于接收当前手机变化的通知
这里listen满足我们的要求
Tm.listen(listener,events);
Listener:监听当前手机变量的对象
Events:要监听事件
PhoneStateListener.LISTEN_CALL_STATE监听电话呼叫状态更改的事件
Listen_data_activity监听网络数据变化的状态,这个用于查看网络是否中断
LISTEN_SIGNAL_STRENGTH:手机信号的强度
3>我们new一个phoneStateListener的内部类
4>复写onCallStateChanged(intstate,StringincomingNumber);
State:当前电话状态
incomingNumber:当前来电号码
5>通过state可以用switch判断当前是什么状态,并相应的进行处理
1.TelephonyManger.CALL_STATE_IDLE//手机处于空闲状态
2.CALL_STATE_RINGING//手机处于铃响状态
3.CALL_STATE_OFFHOOK://手机处于接听状态(勾起状态),这是通话的操作
4在手机处于铃响状态打开一个录间机
1).获取实例
MediaRecorderrecorder=newMediaRecorder();
2).设置声音采集源
Recorder.setAudioSource(MediaRecorder.AudioSource.MIC):
3).设置文件输出的格式
Recorder.setOutputFormat(MediaRecorder.OutputFormat.THRER_GPP)//这是里3pg的,mp3格式是要收费的开源都是免费的所以不采用
4).设置文件的路径:
Recorder.setOupputFile("/sdcard/info.3pg");
5.设置声音的编码方法
Recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
6).开始准备录音
recorder.prepare();
5手机处理接听状态开始录音
7)开始录音
Recorder.start();
6>手机处于空闲状态停止录间,上传文件到服务器
8)停止录音
Recoder.stop();
上传文件到服务器操作
7.因为写外部设备要加权限
android.permission.WRITE_EXTERNAL_STORAGE
监听电话状态权限
android.permission.READ_PHONE_STATE
使用手机麦克风权限
android.permission.RECORD_AUDIO
5.开启监听服务
1>创建一个意图
Intentintent=newIntent(this,SystemService.class);这里显示意图,指定要启动的组件
2>开启服务
startService(intent)
监听服务一般是采用注册一个开机启动的广播接受者,在开机的时候启动服务
系统中多媒体的api介绍:
DevGuide--->Multimedianadcamera-->audiocapture这个是录音的api介绍
我们接触到三大组件activity,service,广播接收者都是运行在主线程里的,文件上传一般都要开启一个线程用于上传数据
在onCreate()方法中可以设置setForeground(true)这个是把服务设置成前台服务,那么系统会认为这是一个前台,那就优先级就高了
录音这个动作是调用系统的一个录音的进程,所以不用再把他放在另一个服务中了,可以直接写主线程里
服务的应用场景:
1.保证当前应用程序的进程长期的存活,在服务里面执行一些耗时的操作
服务的开启方式:
开启服务:
1.实现一个类继承service
2.在清单文件是配置一个<service>标签
3.定义服务的意图
IntentserviceIntent=newIntent(this,MyService.class);
4.服务类中实现onCreate方法
5.调用服务类中开启服务
startService(serviceIntent)
绑定服务:
1.实现一个类继承service
2.在清单文件是配置一个<service>标签
3.定义服务的意图
IntentserviceIntent=newIntent(this,MyService.class);
4.服务类中实现onCreate方法
5.绑定服务
bindService(service,conn,flags);
service:开启的意思serviceIntent
conn:ServiceConnection接口,实现这个接口
flags:标识可以是0或者BIND_AUTO_CREATE,如果指定BIND_AUTO_CREATE那么如果服务不存在他会自动创建出来
如果使用开启服务那么调用者退出(后退键),服务还会长期在后台运行,服务和调用者之间生命周期没有什么关系
如果使用绑定服务,一旦调用者挂了,服务也会跟着挂掉.
service生命周期因为没有界面所以没有关系焦点的方法,有几下方法
onBind()//当服务被绑定的时候调用的方法
onStart()当服务被开启了
onDestroy()当服务被销毁了
onUnbind()当服务被解除绑定了
//停止一个服务
stopService(serviceIntent);
//解除绑定
unbindService(serviceConnection接口)
开启服务特点:
1.刚开始开启服务会调用onCreate()跟onStart()方法,如果一旦服务开启了就只会调用onStart()方法,服务是不会重复创建
2.按后退服务不会被停止销毁,如果在程序应用中手工停止服务或者写程序代码停止服务才会执行ondestroy()方法
绑定服务特点:
1.通过bindservice的方法开启服务,如果服务没有被创建那么会执行oncreate()方法跟onbind方法,服务只会被成功的绑定一次,多次的调用binservice也只会执行一次bind的方法.以后多次绑定的点击不会触发任何事件
2.如果用后退键退出这个服务,那么会报activity的serviceConnection泄漏了,然后执行Unbind()方法跟ondestroy方法
3.如果程序代码中执行解除绑定服务,那么会调用用Unbind()跟ondestroy()方法,而且log日志中不会报错,跟据上面的操作,我们可以把解除绑定的代码写在service的onDestroy()方法中,服务只能被解除绑定一次,多次的解除绑定服务,程序会抛异常
如果开启服务跟绑定服务同时使用,那么只能先解除绑定,才能响应停止服务的事件
为什么有了startservice还要开发一个bindservice的方法:
绑定:产生关联关系,如果采用绑定的方式开启服务,开启服务的activity就可以调用服务里面的方法了
实例代码:
1.service中定义一个测试方法,用于测试数据
2.可以通过绑定服务的通道间接的得到service对象
在绑定服务时用bindService方法,里面有一个serviceconnection的对象,里面有两个方法
//当服务被成功的绑定的时候执行的方法,方法返回一个IBinder的对象
onServiceConnected(ComponentNamename,Ibinderservice);
3.service在绑定时调用onBind方法返回Ibinder接口
4.关联上面两者,现在返回是一个null;
5.定义一个类实现IBinder接口,因为接口太多了我们使用系统提供的Binder类就可以了,这个类继承了IBinder接口
6.定义一个调用测试方法的方法
7.让onBind方法返回我们定义的Binder对象
8.然后onServiceconnected这个方法的IBinder参数就代表返回的IBinder对象
9.然后可以进行方法调用了,修改服务的数据跟参数了
如果直接newservice服务调方法也可以,不过这样就是直接得到对象了不是服务了,产生的问题,直接自己的new出来的服务对象,那么系统不会为这么对象赋上下文的,这样代码就会有问题
对外暴露方法:把内部类的方法通过接口暴露方法
1.创建一个接口
2.接口中创建一个方法定义,这个就是用于获取测试类方法的方法
3.在服务的代码里让Binder的继承类同时去实现刚才定义的接口
4.然后实现接口里定义的方法
5.调用者(调用服务的activity)就可以把得到的IBinder对象直接转成我们定义的接口类型
6.然后调用定义的方法就可以了
IPC:interprocesscommunication进程间的通讯
AIDL:androidinterfacedefinitionlanguageandroid接口定义语言
绑定本地服务:服务和调用者在同一个应用程序里
远程服务:服务和调用者在不同的应用程序里面,即不同的进程里面(ipc操作)
aidl远程服务调用过程:
1.创建一个远程服务
1>.如果一个远程的服务,想把服务里的方法暴露出来,那么就要符合google定义的规范,就是把接口的java文件后缀改成aidl
2>.生成的aidl文件中把接口的修饰符(public)删除,因为这个远程服务要暴露的接口一定的public的,所以不用写public修饰符(在定义接口跟方法都不用写,删除即可)
3>在gen的目录下自动生成了一个aidl文件名相同的java文件,这个类自动生成了一个IPC的实现类,这个实现类继承了android.os.Binder类并实现了我们定义的接口
4>我们就不用定义一个类实现IBinder接口,或者继承Binder类了直接继承android为我们实现生成的IPC的实现类(接口类名.Stub)
5.>然后直接实现这个类的未实现的方法(即我们接口中定义的方法,为要暴露的方法)
6>远程应用服务是希望别人把这个服务开启来的,配置一个隐匿的意图
<serviceandroid:name=".MyService">
<intent-filter>
<actionandroid:name="cn.itcast.myservice"/>//自定义服务
</intent-filter>
</service>
2.定义一个应用激活这个远程服务
1>定义远程服务的意图
IntentservieIntent=newIntent();
servieIntent.setAction("cn.itcast.myservice");//远程服务中定义的服务
2>绑定服务bindService(servieIntent,conn,BIND_AUTO_CREATE);
3>定义conn(ServiceConnection接口)并实现未实现的方法
4>在onServiceConnected()方法中的IBinder对象其实是远程服务里创建的继承了Iservice.stub(接口名.stub)的MyBinder对象
5>保存调用远程端跟远程端的接口是相同的,那么就把远程服务器的aidl文件复制过来,还要建立跟远程服务器aidl文件一样的包,并把这个aidl放在同样的包下,复制过来以后调用服务的程序端这边也会在gen文件下产生一个Iservice.java的文件,在文件中一个asInterface的一个静态方法,这个方法返回的是cn.itcast.remoteservice.IService接口(包名.接口名,即我们定义的接口)类型的对象,就是远程对象返回的内容了
6>在onServiceConnected()方法中得到远程返回的对象付给我们定义的全局变量
IserviceiService=null;//定义全局变量
iService=Iservice.Stub.asInterface(service);
7>得到远程服务器的IService接口对象后就可以调用里面定义的方法了
调用本地服务里面的方法跟调用远程服务的区别:
相同点:
1.都要去绑定服务
2.都要重写ServiceConnection的方法
3.都是得到服务端返回的Ibinder的对象
4.通过IBinder对象调用服务器里面的内容
不同点:
1.本地服务:直接定义一个IService接口方法就行了
2.远程服务:如果要暴露数据就需要IService的aidl(删除访问修饰符)
3.本地服务在写返回IBinder时要继承Binder实现Iservice(要暴露方法的接口)
4.远程服务实现的是Iservice.Stub
5.本地服务返回的IBinder要强制转换
6.远程服务调用Iservice.Stub.asInterface(service)
本地服务也是可以用aidl调用的