Activity
步骤 ① 写一个类继承 Activity(在AS下继承的是)
② 从写onCreate方法 注意super.onCreate不能删掉 放到onCreate中的第一行
③ 不要忘记 onCreate中 要写setContentView()方法 把布局文件加载到界面
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//super.onCreate不要删掉 让这一行先执行
setContentView(R.layout.activity_second);
}
}
④ 在清单文件中application节点下 声明Activity 节点
<activity
android:name="cn.dong.newactivity.SecondActivity"
android:icon="@drawable/head2"
android:label="我是第二个activity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
name 指定当前activity的路径 (要使用全类名) 注意可以 alt+/提示补全 能补全不要手敲
icon 可以给当前activity指定一个图标(只有当前activity是启动的activity才会在桌面上显示)
lable 给当前activity指定一个标签
如果一个activity节点下声明了intent-filter那么这个activity 就会在桌面上生成一个图标
这个action MAIN 和 category LAUNCHER在一个应用中只能在一个Acitivity下配置 这个Activity就是整个应用运行的第一个Activity
也可看做程序的入口
如果配置多个 可能会出现多个图标在桌面上
打开activity的两种方式
显示意图 : 直接通过字节码 .class打开对应activity 在打开自己应用activity的时候 一般使用显示意图
隐式意图: 如果应用需要有一些activity被其他应用访问 就需要使用隐式意图 还需要在清单文件对应的activity节点下配置 intent-filter
或者当前应用要访问其他应用的activity 需要使用隐式意图
activity的生命周期
onCreate 当activity第一次创建的时候走 oncreate
onStart
onResume 当一个新的页面运行起的时候执行的顺序 onCreate->onStart->onResume
当onResume 执行之后 页面处于可以被看见 也可以被操作的状态 (运行状态) (加载数据 刷新界面)
onPause(); 运行了onPause();之后 activity处于 暂停状态(可见但不可操作) 如果处于这个状态 在回到可以被操作的状态 只走onResume方法 不会调用onRestart
onStop(); 运行了onStop()之后 activity处于停止状态(不可见 也不能被操作 但是状态会被保存)(保存状态)
onDestory(); 运行了onDestory()之后 activity会被销毁(资源释放 死循环 退出 关闭线程)
onRestart(); 当页面重新来到最前面(从stop状态回到可以被操作的状态会走 onRestart)
如果a页面开启了B 页面 a 页面会运行 onPause()-> onstop
如果a页面调用了finish方法 onPause()-> onstop->onDestory();
如果a->B bfinish a的生命周期方法 onRestart->onStart->onResume 不会再次走oncreate
activity任务栈
每个android应用都会有多个activity , activity启动之后 会放到一个任务栈(activity栈)当中管理起来
当前可见的activity就是栈顶的activity
activity有四种启动模式(launch mode) 启动模式决定了activity在栈中存在多少个实例
配置activity启动模式在清单文件 activity节点下 添加一个属性 launchMode
<activity
android:name="cn.dong.activitystartmode.MainActivity"
android:label="@string/app_name"
android:launchMode="standard" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
standard 默认启动模式 只要调用了startActivity方法 就会在任务栈中创建一个该activity的实例
singleTop 栈顶只有一个实例 a->b->a->b 任务栈中b在栈顶 如果b配置了启动模式为singletop 这时b不能再创建新的实例
singleTask 在当前任务栈中只能有一个实例 a->b->a->c->a-> 如果此时 b 配置了singleTask启动模式 a 想开启b 由于栈中已经有了b的实例 此时 会把b上面的所有activity杀死 把b暴露出来 让b在栈顶 可以操作
singleInstance 在应用中只能有一个实例 而且这个实例会占用单独的任务栈
除了singleInstance 比较少用到其余三种启动模式都可能会用到,如果没什么特殊要求的activity 不需要指定特殊的启动模式 用默认的就可以了
应用的MainActivity(主界面 ) 一般使用singleTask,有一些界面 比如 新闻的详情 可能会被多次开启 这个时候可以使用singleTop
栈 先进后出 a->b->c
队列 先进先出
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.dong.activitystartmode"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="cn.dong.activitystartmode.MainActivity"
android:label="@string/app_name"
android:launchMode="standard" ><!--通过这个参数配置启动模式-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="cn.dong.activitystartmode.SecondActivity"
android:launchMode="singleInstance"></activity>
</application>
</manifest>
屏幕旋转时的生命周期
如果不特殊处理 系统默认操作: 销毁activity 重新创建一个新的
如果不想让屏幕旋转时activity销毁
① 写死屏幕的方向 在清单文件activity节点下配置
android:screenOrientation="portrait"(竖直的) landscape(水平的)
② 在清单文件activity节点配置一个configchanges属性 "screensize|orietation"
当屏幕旋转时 activity会调用onConfigrationchanged方法 可以在这个方法中处理屏幕旋转的逻辑
broadcastreceiver
步骤:①写一个类 继承broadcastreceiver
②重写onReceive方法
public class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//当收到广播之后 就会调用onReceive方法
}
}
③清单文件注册广播接收者
<receiver android:name="cn.dong.ipdailer.MyReceiver">
<intent-filter >
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
</intent-filter>
</receiver>
④ (可选)有的广播需要权限 还需要在清单文件中 添加权限
不同版本广播的特点
4.0之后 关于广播 有如下更新
① 通过force stop停止的应用 不能收到广播
②没有运行过的应用不能收到广播(想要收到广播 必须在有界面的情况下运行一次 才能收到广播)
有序广播和无序广播
有序广播指定优先级 通过给intent-filter节点 指定一个priority 优先级 可以确定有序广播的接收顺序 priority最大值就是int类型的最大值
中断一个有序广播调用的方法 abortBroadcast();
如何判断一个广播是有序还是无序 就是看调用abortBroadcast() 是否会报错
如果是有序广播 可以调用abortBroadcast()
如果是无序的调用abortBroadcast()会报错
<receiver android:name="cn.dong.receiveorderbroadcast.FarmerReceiver">
<intent-filter android:priority="100">
<action android:name="cn.dong.sendrice"/>
</intent-filter>
</receiver>
发送广播的方法 接收广播的顺序 广播是否可以被中断
有序广播 sendOrderedBroadcast 可以指定优先级 按优先级接收 可以中断
无序广播 sendBroadcast(intent) 没有顺序 同时收到 不可以中断
广播的动态注册和静态注册
动态注册 通过代码的方式调用 registerReceiver 注册广播接收者 这个广播跟注册它的组件声明周期相关 只有注册这个广播的组件活着才能够收到广播,动态注册的广播接收者 在activity退出的时候要注销掉
静态注册 在清单文件中声明receiver节点 注册广播接收者 静态注册的广播即使应用没有启动 一样也可以收到广播
Service
进程的概念&进程优先级
① 大部分android应用 都跑在一个linux进程中(也可以跑在多个进程) 所有的组件都运行在一个线程里(主线程) 4大组件(activity service broadcastreceiver contentprovider ) 都运行在主线程 四大组件做耗时操作都要开子线程
② android 试图保持所有的应用进程都存活在手机中 只有当手机内存不够用的时候才会杀死进程
android系统 通过进程中组件运行的情况 决定那个进程先被杀死 一共有5档优先级
-
Foreground process(前台进程)
1.1 有一个activity正在运行跟用户交互(activity的onResume方法被调用)1.2 广播接收者正在执行onreceive 方法1.3 service正在执行生命周期方法
2.Visible process(可视进程)
2.1 有一个activity 处于onPause状态(可见但不能被操作)
3.Service process(服务进程)
3.1 后台运行着一个用startservice开启的服务 一般这个服务虽说不能被用户看到 但是可能运行用户关心的操作(比如播放音乐)系统会尽可能保留服务进程不被杀死
4.Background process(后台进程)
4.1后台进程是 只有activity处于onStop状态没有其他组件在运行, 后台进程可以被系统随时杀死, 后台会存在多个处于后台进程状态的应用,哪个先挂掉是按照LRU(最近使用的最后杀死,最少使用的最先杀死)的顺序来决定优先级的.
5.Empty process(空进程)
5.1 空进程 没有任何组件活着的进程, 保持这个进程存活的目的是为了下次开启组件的时候速度更快一些,系统会随时杀死这些进程为了回收资源
startservice方式开启服务
步骤:
①写一个类继承Service
public class MyService extends Service {
②重写方法
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
super.onCreate();
//当service第一次创建的时候系统会调用这个方法
System.out.println("service onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//调用startservice方法的时候系统就会调用这个onStartCommand
System.out.println("service onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("service onDestroy");
//当service销毁的时候 会调用这个onDestroy
}
}
③清单文件注册
<service android:name="cn.dong.servicedemo.MyService"></service>
startservice开启服务的特点
①调用startservice之后 service走的生命周期方法 onCreate->onstartCommend
②多次调用startservice onCreate只会走一次 onStartCommend会调用多次(调用几次startservice onStartCommend方法就会调用几次)
③ 通过startservice开启service的activity退出之后 service不会销毁
④ 只有手动调用stopservice方法 才会销毁服务(或者在程序管理器页面停止服务)
⑤ startservice方式开启的服务可以提升应用的进程优先级
使用服务注册特殊广播接收者
可以把必须动态注册的广播接收者放到service中注册 service可以长期在后台存活, 保证可以随时收到广播
Service代码
public class ScreenService extends Service {
private ScreenReceivcer receiver;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
receiver = new ScreenReceivcer();
//创建一个意图过滤器
IntentFilter filter = new IntentFilter();
//给广播添加需要监听的action
filter.addAction("android.intent.action.SCREEN_OFF");
filter.addAction("android.intent.action.SCREEN_ON");
//动态注册一个广播接收者
registerReceiver(receiver, filter);
}
@Override
public void onDestroy() {
super.onDestroy();
//注销receiver
unregisterReceiver(receiver);
}
}
广播接收者代码
public class ScreenReceivcer extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if("android.intent.action.SCREEN_OFF".equals(action)){
System.out.println("屏幕熄灭 执行上传任务");
}else{
System.out.println("屏幕点亮,暂停上传任务");
}
}
}
清单文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.dong.serviceregisterreceiver"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="cn.dong.serviceregisterreceiver.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="cn.dong.serviceregisterreceiver.ScreenService"></service>
</application>
</manifest>
bindservice开启服务特点
① 通过bindservice开启服务要传递三个参数
//创建服务的意图对象
Intent service = new Intent(this,BindService.class);
conn = new Myconn();
//最后一个参数 flag标志位 一般传BIND_AUTO_CREATE 当连接之后 会创建service
bindService(service, conn, BIND_AUTO_CREATE);
② 通过bindservice开启服务 走生命周期方法: onCreate()->onBind
多次调用bindservice onCreate()->onBind都只会执行一次
③ 如果service是在activity中通过bindservice方法打开的 service的生命周期就跟activity的生命周期关联起来(不求同生,但求同死)
④ 在activity退出的时候 如果不手动调用unbindservice方法会抛异常 提示serviceconnection泄露
⑤ unbindservice只能调用一次 多次调用会抛异常
⑥ 在service中的onBind方法 如果有返回值 那么 ServiceConnection中的onServiceConnected方法在连接创建之后就会被调用 其中第二个参数 IBinder就是service中的onBind方法的返回值
比较bindservice 和startservice之间区别
走的生命周期方法 多次调用开启方法 service跟调用者的生命周期 关闭service的方法
bindservice onCreate->onbind oncreate onbind只走一次 跟创建它的activity 不求同生,但求同死 unbindservice 只能执行一次(调用多次会有异常)
startservice onCreate->onstartcommand oncreate调用一次 onstartcommand 调用一次 执行一次 没关系 stopservice 可以调用多次
通过bindservice调用服务中的方法
①需要注意 四大组件 都不能直接使用new 构造 的方式创建, 四大组件要运行在框架中 需要通过系统的api创建对应的实例,自己new的四大组件 脱离了android的框架 只是普通的java类
② 通过bindservice调用步骤
2.1 在bindservice 方法传递的第二个参数 ServiceConnection 中 onServiceConnected方法 把传进来的 Ibinder对象保存起来
2.2 在 service里 创建内部类 继承BInder对象 在内部类中 暴露出方法 供activity调用
2.3 把内部类对象 作为onBind方法的返回值 返回去 在activity中可以获取到
2.4 通过获取到的service的内部类对象访问相关方法
混合方式开启服务
startService bindservice 先后调用 顺序无所谓 但是都要调用
如果想销毁service 先后调用stopservice 和 unbindService
通过混合方式开启的服务 unbind之后 再次执行bindservice onbind 不会被调用 但是 ServiceConnection 中的 onServiceConnected每次都会被调用 也就是说 只要service没死 每次执行 bindservice之后 都会获取到service中的内部类对象 也就可以调用service中的方法
startservice 和bindservice虽然开了两次service 但是 只开启了一个servce对象
想销毁service 只要调用 stopservice 和unbindService 只要都调用了就可以关闭 跟顺序没关系
contentProvider
内容提供者作用: 在不同应用之间 共享数据库中的数据
访问内容提供者提供的数据 要使用contentresovler 内容解析者
步骤:
① 创建一个类 继承ContentProvider 重写里面方法
② 在清单文件中注册相应provider 必须指定authorities 属性 还要添加一个属性 exported = true
<provider android:name="cn.dong.contentproviderdemo.MyProvider"
android:authorities="cn.itcast.provider"
android:exported="true"></provider>
③ 在provider中处理uri匹配相关内容
创建URI匹配器 在provider 中搞静态代码块 在static代码块中添加 uri匹配的规则
private MyOpenHelper openHelper;
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int MATCH_UPDATE = 0;
private static final int MATCH_QUERY = 1;
static{
//通过uri匹配器 添加匹配的路径规则 cn.itcast.provider/query 如果调用match方法匹配上了这个规则 那么就会返回MATCH_UPDATE
//如果传入的uri没有匹配任何预先加入的uri 就会返回NO_MATCH
sURIMatcher.addURI("cn.dong.provider", "query", MATCH_QUERY);
}
根据业务逻辑 在不同的数据库操作方法中 处理uri匹配的流程
//第一个参数 就是用来匹配具体路径 决定是否让对方访问相应方法
int result = sURIMatcher.match(uri);
if(result == MATCH_QUERY){
SQLiteDatabase db = openHelper.getReadableDatabase();
Cursor cursor = db.query("info", projection, selection, selectionArgs, null, null, sortOrder);
return cursor;
}else{
throw new IllegalStateException("口令不正确");
}
④ 其他应用访问contentprovider方法
获得contentresolver对象
ContentResolver resolver = getContentResolver();
通过contentresolver调用相关方法访问contentprovider
需要注意 uri 要以content://开头 具体的路径 要跟contentprovider中 定义的uri匹配规则匹配上
Uri uri = Uri.parse("content://cn.dong.provider/query");
Cursor cursor = resolver.query(uri, null, null, null, null);
内容观察者 contentobserver
要在数据库数据发生变化的时候 通过内容解析者 发送一个notifychanged消息
public Uri insert(Uri uri, ContentValues values) {
int result = sURIMatcher.match(uri);
if(result==MATCH_INSERT){
SQLiteDatabase db = openHelper.getReadableDatabase();
long insert = db.insert("info", null, values);
//通过内容解析者 发送通知 告知内容发生变化 第一个参数 uri 通过这个uri确定是哪个内容提供者对应数据发生了改变
//第二个 参数 ContentObserver 如果传入了一个内容观察者对象 那么 只有这个内容观察者能收到变化的消息
//如果传了null 只要观察着第一个参数传入的uri的内容观察者都能收到变化的消息
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(String.valueOf(insert));
}else{
return null;
}
}
在要观察数据变化的地方 通过内容解析者注册一个内容观察者
contentobserver 是抽象类 要继承并重写方法onChange()
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Uri uri = Uri.parse("content://cn.dong.provider");
MyObserver observer = new MyObserver(new Handler());
//获取内容解析者 ,通过内容解析者注册内容观察者
//第一个参数 uri 就是要接收变化消息的uri 要跟notifyChange(uri,null) 第一个参数对应
//第二个参数 路径的匹配方式 如果是true 只要前面的authoritese部分匹配上了就可以收到变化消息
//如果是false 只有整个路径都匹配了才能收到消息
// uri是 content://cn.dong.provider 第二个参数穿了false 那么 它不能跟 content://cn.dong.provider/insert匹配
//第三个参数 就是要注册的内容观察者
getContentResolver().registerContentObserver(uri, true, observer);
}
private class MyObserver extends ContentObserver{
public MyObserver(Handler handler) {
super(handler);
// TODO Auto-generated constructor stub
}
@Override
public void onChange(boolean selfChange, Uri uri) {
System.out.println("uri = "+uri+"变化了");
//收到数据库内容变化的通知 在界面上刷新 展示最新的数据
}
}
}
内容观察者应用场景
比如 聊天应用 收到服务端传来的消息 先把消息保存到收据库 当数据库内容发生改变的时候就可以先通过内容解析者发送消息
在需要展示聊天界面的activity里注册内容观察者 当内容观察者收到数据库发生改变的消息时 可以重新查询消息记录的数据库,刷新整个界面,展示最新的聊天记录
上下文---getApplicationContext 和 activity context
getApplicationContext 获取到的是应用的上下文 生命周期跟应用相同 使用getApplicationContext 获取到的上下文不会造成内存泄露
只有在创建alertdialog的时候 必须使用activity作为上下文 因为 alertdialog要显示在activity的上面 必须传一个activity的上下文给它才能够正确显示
吐司 跟alertdialog 不同 吐司是系统级的显示控件 会显示在所有应用的最上层 不会被其他界面遮挡 所以 传递应用的上下文也可以显示吐司
本人具有8年android开发经验,android技术支持、培训和小项目开发,联系qq:1157464105,价格保证最优惠