Android 你需要掌握的知识(二)

目录

Service.png

Broadcast Receiver.png

一.Service详解

一.Service基础

1.什么是Service

Service是Android中实现程序后台运行的解决方案,是一个可以在后台执行长时间运行操作而没有用户界面的应用组件
Service可由其他应用(如:Activity,Broadcast)组件启动,服务在启动后会一直在后台运行。即使启动它的Activity或Broadcast已经被销毁也不会受影响,另外也可以把Service绑定到Activity,可以让Service和Activity之间进行数据交互,甚至Service和Activity有可能在不同的进程中也可以用进程间通信来进行数据传输。
注意:Service(服务)和Broadcast(广播)有一个共同点。它们都是运行在主线程当中,不能耗时操作。

2.Service和Thread的区别

1.Thread是程序执行的最小单元,线程,可以用它来执行一些异步操作
Service是安卓的一种机制,当它运行的时候,如果是本地的Service,那么它对应的Service是运行在主线程上的。

2.Thread 运行是相对独立的,而Service运行是依赖于创建服务时所在的应用程序进程。

服务和后台是两个概念,android 的后台指的是它的运行完全不依赖于UI线程,即使Activity被销毁了,或者程序被关闭了,这个服务进程仍然存在,会在后台进行一些如:计算,数据统计等。Service仍然可以继续运作,如果要Service做耗时操作,依旧要在Service中创建子线程。在里面做耗时逻辑。

Activity中创建子线程和Service中创建子线程的不同就是,Activity中很难对子线程控制,Activity在被销毁后就无法获取之前的子线程实例。
如果在Service中就可以很放心的销毁Activity,不用担心无法控制的情况。

二.开启Service的两种方式和区别

1.startService

1.定义一个类继承Service

public class MyService extends Service 

2.在AndroidManifest.xml中配置该service

<application...
        <service android:name=".MyService"></service>
    </application>

3.使用Context的startService(startIntent)方法启动该Service

Intent startIntent = new Intent(this,MyService.class);
startService(startIntent);

4.不在使用时调用stopService(stopIntent)方法停止该服务。

Intent stopIntent = new Intent(this,MyService.class);
stopService(stopIntent);

MyService 继承 Service
服务启动后,会先调用MyService中的onCreate()的方法。

  /**
     * 首次创建服务时,系统会调用此方法来执行一次性设置程序(在调用onStartCommand()或onBind()之前)
     * 如果服务已在运行,则不会调用此方法,该方法只被调用一次
     */
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate() executed");
    }

在用startService启动这个服务时,就会调用onStartCommand方法,一旦调用这个方法,这个服务就会被正式开启。可以在这个方法中做自己的操作。

  /**
   * 每次通过startService方法启动Service时都会被回调
   */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand() executed");
        return super.onStartCommand(intent, flags, startId);
    }

在onStartCommand方法中有一个返回值是int,源码中是这样:

public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
        onStart(intent, startId);
        return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
    }

如果onStartCommand方法返回的是START_STICKY,意味着当整个Service因为内存不足而被系统杀掉的之后,一段时间内存再度空闲的时候,系统会尝试重新创建这个Service,创建成功后,又会调用onStartCommand方法,但其中的intent是空,这个值适合循环播放,天气预报这种服务。

当服务不在被使用的时候调用onDestroy()方法,Service可以在这个方法里面进行一些资源的清理(线程,注册的监听器,Content Provider),会进行回收。

/**
     * 服务销毁时的回调
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy() executed");
    }
2.bindService

bindService意味着在绑定服务的时候,服务和Activity已经处于绑定状态,绑定服务提供了一个客户端和服务端的接口,相当于Activity和Service交互的接口,它允许Activity和Service进行数据交互,发出请求获取结果等等。甚至如果Activity和Service在不同进程当中的时候,可以进行进程间通信来传输数据,这仅仅是在Activity和Service绑定之后才能运行,同时多个Activity可以绑定一个Service,但是绑定取消后,这个服务就会自动被销毁,并不像startService一样必须要调用stopService才能被销毁

bindService启动的方式:
1.创建Service,继承自Service并在类中,创建一个实现IBinder接口的实例对象并提供公共方法给Activity调用
2.从 onBind()回调方法返回此Binder实例。
3.Activity,从onServiceConnected()回调方法接收Binder,并使用提供的方法调用绑定服务。

和startService不同的是,它在MyService中实现了一个Binder的内部类

  /**
     * 创建Binder对象,返回给Activity使用,提供数据交换的接口
     */
    class DownloadBinder extends Binder {
        //声明一个方法 getService (提供给Activity调用)
        MyService getService() {
            //返回当前对象,这样就可以在Activice 中调用Service的公共方法
            return MyService.this;
        }

    }

值得注意的是和startService不同的是,在onBind方法中它返回了一个Binder,这个downloadBinder是我们自己创建的

private DownloadBinder downloadBinder = new DownloadBinder();
    @Override
    public IBinder onBind(Intent intent) {
        return downloadBinder;
    }

在Activity中我们要创建一个ServiceConnection的类,它代表的是Activity和Service的连接。

 private MyService.DownloadBinder downloadBinder;
    /**
     * ServiceConnection代表与服务的链接,它只有两个方法。
     * onServiceConnected和onServiceDisconnected
     * 前者是在操作者在连接一个服务成功时被调用,而后者是在服务崩溃或者杀死导致的连接中断时被调用
     */
    private ServiceConnection connection = new ServiceConnection() {
        /**
         *  与服务端交互的接口方法,绑定服务的时候被回调,在这个方法获取绑定Service传递过来的IBinder对象
         *  通过这个IBinder对象,实现Activity和Service交互
         */
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadBinder = (MyService.DownloadBinder) service;
            downloadBinder.getService().getCount();
        }
        /**
         * 取消绑定的时候被回调,但正常情况下是不被调用的,它的调用时机是当Service服务被意外摧毁时
         * 如内存资源不足时这个方法被自动调用
         */

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

通过bindService(Intent service, ServiceConnection conn, int flags)方法,来启动Service

Intent startIntent = new Intent(this, MyService.class);
bindService(startIntent,connection,BIND_AUTO_CREATE);

通过unbindService(ServiceConnection conn) 方法来停止Service

unbindService(connection);

二.Broadcast Receiver详解

一.广播

1.广播的定义

广播是一种广泛运用的在应用程序之间传输信息的机制,广播使用了设计模式中的观察者模式,当被观察者数据变换时会通知观察者做相应的数据处理,android中要发送的广播内容是一个Intent。在这个Intent里可以携带一些我们要传送的数据。
要记住这两点
1.广播实现了不同程序之间的数据传输与共享,只要和发送广播的Action相同的接收者都能接收这个广播,就是发送一个广播可以被很多广播Receive所接受,典型的就是系统应用(短信,电话等通知)。只要我们实现了他们Action的Broadcast Receiver,就能接收他们的数据。
2.广播接收者还有一个通知的作用,比如在服务中可以发送程序给Activity,让它更新UI,我们知道Service是没有界面的,它是在后台运行的一个组件,所以这时候我们就可以通过广播接收者专门接收从Service发过来的数据和通知,而不要让Service直接和Activity进行交互了。

2.广播的场景

1.同意app内在具有多个进程的不同组件之间的消息通信(在一个app内会开启多个进程,比如app中会有定位进程,主进程,两个进程之间的通信就可以用到广播)
2.不同app之间的组件之间的消息通信(两个app间通过广播来传递对方的消息,比如一个公司的多个app进行推广,就会用到通知)

3.广播的种类

1)Normal Broadcast(普通广播) : 会调用Content.sendBroadcast这个方法进行处理
2)System Broadcast(系统广播,有序广播):会调用Content.sendOrderedBroadcast这个方法进行处理
3)Local Broadcast(本地广播):只在APP内传播

二.实现广播-receiver

1.静态注册:注册完成就一直运行。

直接把广播接收者写在AndroidManifest.xml文件中。特点,它所依赖的Activity销毁了,但是仍然能够接收广播,甚至把进程杀死,仍然能接收广播,但是和动态注册相比,还是不是太灵活。

2.动态注册:跟随activity生命周期。

在代码中通过调用Context的registerReceiver()方法进行动态注册BroadcastReceiver,要记住动态广播最好在Activity的onResume()注册、onPause()注销,否则会导致内存泄露。同时动态注册还有这个特点,它的生命周期是跟随activity生命周期,所以当activity生命周期销毁时,这个动态注册的广播接收者也就失效了。

三.内部实现机制

1.自定义广播接收者BroadcastReceiver,并复写onRecvice()方法;

2.通过Binder机制向AMS(Activity Manager Service)进行注册;
AMS(Activity Manager Service):是贯穿android系统组件的一个核心服务,负责启动四大组件的启动,切换,调度以及应用程序的调度和管理工作。所以AMS是负责BroadcastReceiver的创建。
Biner机制:是android进程间通信的一个核心,它的整体设计架构是客户端/服务端结构,C/S结构,客户端进程可以获取到服务端进程的代理,并通过这个代理接口,通过方法来读取数据,来完成进程间的数据通信。

3.广播发送者通过Binder机制向ASM发送广播

4.ASM查找符合相应条件(IntentFilter / Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中

5.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。

四.LocalBroadcastManager详解(参考文章

1.使用它发送广播将只在自身App内传播,因此不必担心泄露隐私数据
2.其他App无法对你的App发送该广播,因为你的App根本就不可能接受到非自身应用发送的该广播,因此你不必担心有安全漏洞可以利用。
3.比系统的全局广播更加高效。

还要强调,安全漏洞隐私数据

1.如果不是本地广播,代码被反编译后,知道了你的Action,那么第三方应用就能发送与该Action匹配的广播,而你的应用也可以接收到,这样第三方应用就可以用你的广播做很多事情,(分享链接,植入广告)。
2.如果被别人知道了你应用的Action,当你使用广播来传递数据时,别的应用也能接收到你的广播,通过Action匹配,就能获取你的私密数据(用户数据,发送恶意信息等)

看一下LocalBroadcastManager的源码
先看一下它的构造方法:

  private LocalBroadcastManager(Context context) {
        mAppContext = context;
        mHandler = new Handler(context.getMainLooper()) {

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_EXEC_PENDING_BROADCASTS:
                        executePendingBroadcasts();
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        };
    }

它内部是由单例实现的而私有化构造函数,它内部是一个Handler对象,对象传入的是getMainLooper(),它是主线程,它代表它是在主线程运作的Looper。
再看一下它里边3个比较重要的集合类

    private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
            = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
    private final HashMap<String, ArrayList<ReceiverRecord>> mActions
            = new HashMap<String, ArrayList<ReceiverRecord>>();

    private final ArrayList<BroadcastRecord> mPendingBroadcasts
            = new ArrayList<BroadcastRecord>();

mReceivers可以看到是一个HashMap类,它里面的key是BroadcastReceiver,value是ArrayList,ArrayList里面的数据是IntentFilter,IntentFilter是一个过滤类,它会过滤我们的广播,为什么是一个集合呢,因为广播接收器可以接收到不同Action的广播,所以它这里就一定要维护一张表,一个BroadcastReceiver和一个Action是一一对应的。
mActions也是一个HashMap类,它的key 就是一个Action,它的value是ArrayList,ArrayList里面的数据是ReceiverRecord,它这个集合维护着Action所对应的ReceiverRecord,一一对应的一个HashMap表。
mPendingBroadcasts是一个ArrayList集合,它存储的就是BroadcastRecord对象。

    private static class BroadcastRecord {
        final Intent intent;
        final ArrayList<ReceiverRecord> receivers;

        BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
            intent = _intent;
            receivers = _receivers;
        }
    }

而BroadcastRecord对象它存储的是一个intent和receivers,所以说mPendingBroadcasts这个集合的作用就是存储和发送广播的Action匹配的ReceiverRecord集合,在执行处理广播的时候,会将遍历这个集合里面的广播接收器,说白了它是一个存储了广播接收器的存储器。

它是一个广播,肯定要注册,看一下registerReceiver这个方法

    public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mReceivers) {
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            ArrayList<IntentFilter> filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<IntentFilter>(1);
                mReceivers.put(receiver, filters);
            }
           //为广播添加器添加指定的过滤规则
            filters.add(filter);
          //。。。。。。。。。
            for (int i=0; i<filter.countActions(); i++) {
                String action = filter.getAction(i);
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList<ReceiverRecord>(1);
                    mActions.put(action, entries);
                }
               //把IntentFilter里面的所有Action分别建立对ArrayList<ReceiverRecord>的映射,也就是为相应的Action添加广播接收器,表示这个广播接收器可以接收此Action的广播
                entries.add(entry);
              //。。。。。。。
            }
        }
    }

看它所对应的unregisterReceiver

    public void unregisterReceiver(BroadcastReceiver receiver) {
        synchronized (mReceivers) {
            ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
            if (filters == null) {
                return;
            }
            for (int i=0; i<filters.size(); i++) {
                IntentFilter filter = filters.get(i);
                for (int j=0; j<filter.countActions(); j++) {
                    String action = filter.getAction(j);
                    ArrayList<ReceiverRecord> receivers = mActions.get(action);
                    if (receivers != null) {
                        for (int k=0; k<receivers.size(); k++) {
                            if (receivers.get(k).receiver == receiver) {
                                 //移除mReceivers表中广播接收器
                                receivers.remove(k);
                                k--;
                            }
                        }
                        if (receivers.size() <= 0) {
                            //移除mActions表中的广播接收器
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }

它做了两件事

1、移除mReceivers表中广播接收器
2、移除mActions表中的广播接收器

再看一下最重要的sendBroadcast

 public boolean sendBroadcast(Intent intent) {
        synchronized (mReceivers) {
            final String action = intent.getAction();
            final String type = intent.resolveTypeIfNeeded(
                    mAppContext.getContentResolver());
            final Uri data = intent.getData();
            final String scheme = intent.getScheme();
            final Set<String> categories = intent.getCategories();

            final boolean debug = DEBUG ||
                    ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
            if (debug) Log.v(
                    TAG, "Resolving type " + type + " scheme " + scheme
                    + " of intent " + intent);
            //通过得到我们Intent的Action来获得该Action对应的所有的广播接收器的集合
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            if (entries != null) {
                if (debug) Log.v(TAG, "Action list: " + entries);

                ArrayList<ReceiverRecord> receivers = null;
                //然后遍历该集合,进行匹配
                for (int i=0; i<entries.size(); i++) {
                    ReceiverRecord receiver = entries.get(i);
                    if (debug) Log.v(TAG, "Matching against filter " + receiver.filter);

                    if (receiver.broadcasting) {
                        if (debug) {
                            Log.v(TAG, "  Filter's target already added");
                        }
                        continue;
                    }
                    //当action,type,scheme,data,categories都完全相同时,这时匹配成功
                    int match = receiver.filter.match(action, type, scheme, data,
                            categories, "LocalBroadcastManager");
                    if (match >= 0) {
                        //匹配成功
                        if (debug) Log.v(TAG, "  Filter matched!  match=0x" +
                                Integer.toHexString(match));
                        if (receivers == null) {
                            receivers = new ArrayList<ReceiverRecord>();
                        }
                        //然后把匹配成功的ReceiverRecord对象添加到一个集合中去
                        receivers.add(receiver);
                        receiver.broadcasting = true;
                    } else {
                        if (debug) {
                            String reason;
                            switch (match) {
                                case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
                                case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
                                case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
                                case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
                                default: reason = "unknown reason"; break;
                            }
                            Log.v(TAG, "  Filter did not match: " + reason);
                        }
                    }
                }

                if (receivers != null) {
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    //最后再把存储匹配成功的ReceiverRecord对象集合添加到mPendingBroadcasts中去,而最终我们的广播接收是通过遍历mPendingBroadcasts这个集合来一一对这个集合里面的广播接收器进行广播的接收
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));

                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
//这个非常重要,因为一开始我们就知道了在构造函数中会创建一个handler,而这个方法的名为sendBroadcast()其实不是真正的发送一个广播,而是通过handler来发送一个Message,然后在handlerMessage()回调方法中进行消息的处理,所以这也证实了这是一个本地广播,其它应用根本无法获取到,因为LocalBroadcastManager内部是通过Handler实现广播的发送的  
                    mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }

这个sendBroadcast()方法看起来比较长,其实一半的代码都是在做匹配,这段代码也做了三件事:

1、把我们发送的Intent的规则和mActions中对应action的ArrayList<ReceiverRecord>集合里面的广播接收器的规则进行匹配,匹配成功则加入一个匹配成功的集合中
2、把匹配成功的集合加入到mPendingBroadcasts集合中
3、最重要的一点,它其实是通过handler发送一个Message来实现的

总结 :LocalBroadcastManager高效的原因主要是因为它内部是通过Handler实现的,它的sendBroadcast()方法含义并非和我们平时所用的一样,它的sendBroadcast()方法其实是通过handler发送一个Message实现的(当然这个方法还有规则匹配等作用)
既然是它内部是通过Handler来实现广播的发送的,那么相比与系统广播通过Binder实现那肯定是更高效了,同时使用Handler来实现,别的应用无法向我们的应用发送该广播,而我们应用内发送的广播也不会离开我们的应用
LocalBroadcastManager内部协作主要是靠这两个Map集合:mReceivers和mActions,当然还有一个List集合mPendingBroadcasts,这个主要就是存储待接收的广播对象
假如我们需要让一个广播接收器能接收多个不同Action的广播,我们可以在注册时候通过IntentFilter为该广播添加多条Action规则,这样只要符合其中一条该广播接收器就可以接收到了,如下:

 //注册
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.sunzxy.demo.BROADCAST_ACTION_1");
        filter.addAction("com.sunzxy.demo.BROADCAST_ACTION_2");
        filter.addAction("com.sunzxy.demo.BROADCAST_ACTION_3");
        LocalBroadcastManager.getInstance(this).registerReceiver(new MyMyBroadcastReceiver(),filter);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容