为了账号安全,请及时绑定邮箱和手机立即绑定

四大组件基础篇

标签:
Android

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档优先级

  1. 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 不同 吐司是系统级的显示控件 会显示在所有应用的最上层 不会被其他界面遮挡 所以 传递应用的上下文也可以显示吐司

原文链接:http://www.apkbus.com/blog-862846-62727.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消