04Broadcast Receiver

来源:互联网 发布:sql创建会员信息表 编辑:程序博客网 时间:2024/06/16 19:57

​本节内容:广播机制简介、接收系统广播(动态注册&静态注册)、发送自定义广播、使用本地广播、广播的最佳实践——实现强制下线功能、Git 时间,初识版本控制工具


广播机制简介:

    在一个IP网络范围中最大的IP 地址是被保留作为广播地址来使用的。比如某个网络的IP 范围是192.168.0.XXX,子网掩码是255.255.255.0,那么这个网络的广播地址就是192.168.0.255。

    Android 中的广播机制更加灵活,因为Android 中的每个应用程序都可以对自己感兴趣的广播进行注册

    发送广播借助Intent;接收广播需要广播接收器(Broadcast Receiver)。

    Android 中的广播主要可以分为两种类型,标准广播(完全异步执行、无序、无法截断)和有序广播。


接收系统广播:

    动态注册(代码中)监听网络变化:

    新建一个类,让它继承自BroadcastReceiver,并重写父类的onReceive()方法。

    MainActivity 中的代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MainActivity extends Activity {
    private IntentFilter intentFilter;
    private NetworkChangeReceiver networkChangeReceiver;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intentFilter = new IntentFilter();
 
        //广播接收器想要监听什么广播,就在这里添加相应的action
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        networkChangeReceiver = new NetworkChangeReceiver();
        registerReceiver(networkChangeReceiver, intentFilter);
    }
 
   @Override
    protected void onDestroy() {
        super.onDestroy();
        //动态注册的广播接收器一定都要取消注册
        unregisterReceiver(networkChangeReceiver);
    }
 
     class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "network changes",Toast.LENGTH_SHORT).show();
        }
    }
}

    进一步的优化。修改MainActivity 中的代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MainActivity extends Activity {
    ……
 
    class NetworkChangeReceiver extends BroadcastReceiver {
 
         @Override
        public void onReceive(Context context, Intent intent) {
 
             //getSystemService()方法得到了ConnectivityManager 的实例,这是一个系统服务类,专门用于管理网络连接
            ConnectivityManager connectionManager = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
 
            if (networkInfo != null && networkInfo.isAvailable()) {
                Toast.makeText(context, "network is available",
                Toast.LENGTH_SHORT).show();
            else {
                Toast.makeText(context, "network is unavailable",Toast.LENGTH_SHORT).show();
            }
        }
    }
}

    AndroidManifest.xml 文件里面加入如下权限就可以查询系统网络状态了:

1
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    访问http://developer.android.com/reference/android/Manifest.permission.html 可以查看Android系统所有可声明的权限。


    静态注册AndroidManifest.xml 中)实现开机启动:

    动态注册的缺点,必须要在程序启动之后才能接收到广播。让程序在未启动的情况下就能接收到广播就需要使用静态注册的方式。

    新建一个BootCompleteReceiver 继承自BroadcastReceiver,代码如下所示:

1
2
3
4
5
6
public class BootCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
    Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
    }
}

    然后修改AndroidManifest.xml 文件,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 
<application
    ……
    <receiver android:name=".BootCompleteReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
</application>

    注意:不要在onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当onReceive()方法运行了较长时间而没有结束时,程序就会报错。因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知,或者启动一个服务等


发送自定义广播:

    发送标准广播:

    新建一个MyBroadcastReceiver 继承自BroadcastReceiver,代码如下所示:

1
2
3
4
5
6
public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "received in MyBroadcastReceiver",Toast.LENGTH_SHORT).show();
    }
}

    然后在AndroidManifest.xml 中注册:

1
2
3
4
5
6
7
8
9
10
……
<application
    ……
    <receiver android:name=".MyBroadcastReceiver">
        <intent-filter>
            <action android:name="com.example.broadcasttest. MY_BROADCAST"/>
        </intent-filter>
        </receiver>
</application>
......

    接下来修改activity_main.xml 在布局文件中定义一个按钮,用于作为发送广播的触发点。

    然后修改MainActivity中的代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MainActivity extends Activity {
    ……
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
                sendBroadcast(intent);
            }
        });
        ……
    }
    ……
}

    由于广播是使用Intent 进行传递的,因此你还可以在Intent 中携带一些数据传递给广播接收器。


    发送有序广播:

    将sendBroadcast() 方法改成sendOrderedBroadcast()方法。第一个参数仍然是Intent,第二个参数是一个与权限相关的字符串,这里传入null 就行了。

   广播接收器的先后顺序在注册的时候进行设定,修改AndroidManifest.xml 中的代码,如下所示:

1
2
3
4
5
6
7
8
9
10
……
<application
    ......
    <receiver android:name=".MyBroadcastReceiver">
        <intent-filter android:priority="100" >
            <action android:name="com.example.broadcasttest.MY_BROADCAST"/>
        </intent-filter>
    </receiver>
</application>
......

   

    如果在onReceive()方法中调用了abortBroadcast()方法,就表示将这条广播截断,后面的广播接收器将无法再接收到这条广播。


使用本地广播:

    为了能够简单地解决广播的安全性问题,Android 引入了一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。

    本地广播主要就是使用了一个LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。

    修改MainActivity 中的代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MainActivity extends Activity {
    private IntentFilter intentFilter;
    private LocalReceiver localReceiver;
    private LocalBroadcastManager localBroadcastManager;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        // 获取实例
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.broadcasttest.LOCAL_BROADCAST");
                localBroadcastManager.sendBroadcast(intent); // 发送本地广播
            }
        });
        intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
        localReceiver = new LocalReceiver();
        localBroadcastManager.registerReceiver(localReceiver, intentFilter);// 注册本地广播监听器
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(localReceiver);
    }
 
    class LocalReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "received local broadcast",Toast.LENGTH_SHORT).show();
        }
    }
}

    本地广播是无法通过静态注册的方式来接收的。因为静态注册主要就是为了让程序在未启动的情况下也能收到广播,而发送本地广播时,我们的程序肯定是已经启动了,因此也完全不需要使用静态注册的功能。


    使用本地广播的几点优势:

    1. 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄的问题。

    2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐患。

    3. 发送本地广播比起发送系统全局广播将会更加高效


广播的最佳实践——实现强制下线功能

    先创建一个ActivityCollector 类用于管理所有的活动,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ActivityCollector {
    public static List<Activity> activities = new ArrayList<Activity>();
 
    public static void addActivity(Activity activity) {
        activities.add(activity);
    }
    public static void removeActivity(Activity activity) {
        activities.remove(activity);
    }
    public static void finishAll() {
        for (Activity activity : activities) {
            if (!activity.isFinishing()) {
            activity.finish();
            }
        }
    }
}

    然后创建BaseActivity 类作为所有活动的父类,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
public class BaseActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityCollector.addActivity(this);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
}

    登陆界面直接用上节TableLayout中的例子;

    新建LoginActivity 继承自BaseActivity,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class LoginActivity extends BaseActivity {
    private EditText accountEdit;
    private EditText passwordEdit;
    private Button login;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);
        accountEdit = (EditText) findViewById(R.id.account);
        passwordEdit = (EditText) findViewById(R.id.password);
        login = (Button) findViewById(R.id.login);
        login.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                String account = accountEdit.getText().toString();
                String password = passwordEdit.getText().toString();
                // 如果账号是admin且密码是123456,就认为登录成功
                if (account.equals("admin") && password.equals("123456")) {
                    Intent intent = new Intent(LoginActivity.this,
                    MainActivity.class);
                    startActivity(intent);
                    finish();
                else {
                    Toast.makeText(LoginActivity.this"account or password is invalid",
                    Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
}

    activity_main.xml 中只有一个按钮而已;

    然后修改MainActivity 中的代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainActivity extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button forceOffline = (Button) findViewById(R.id.force_offline);
        forceOffline.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.broadcastbestpractice.FORCE_OFFLINE ");
                sendBroadcast(intent);
            }
        });
    }
}

    强制用户下线的逻辑并不是写在MainActivity 里的,而是应该写在接收这条广播的广播接收器里面,这样强制下线的功能就不会依附于任何的界面,不管是在

程序的任何地方,只需要发出这样一条广播,就可以完成强制下线的操作了。

    新建ForceOfflineReceiver继承自BroadcastReceiver,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ForceOfflineReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(final Context context, Intent intent) {
        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
        dialogBuilder.setTitle("Warning");
        dialogBuilder.setMessage("You are forced to be offline. Please tryto login again.");
        dialogBuilder.setCancelable(false);
        dialogBuilder.setPositiveButton("OK",new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                ActivityCollector.finishAll(); // 销毁所有活动
                Intent intent = new Intent(context,LoginActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(intent); // 重新启动LoginActivity
            }
        });
        AlertDialog alertDialog = dialogBuilder.create();
        // 需要设置AlertDialog的类型,保证在广播接收器中可以正常弹出
        alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
        alertDialog.show();
    }
}

    由于我们是在广播接收器里启动活动的,因此一定要给Intent 加入FLAG_ACTIVITY_NEW_TASK 这个标志。

    对AndroidManifest.xml文件进行配置,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
。。。。。。
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application
。。。。。。
<receiver android:name=".ForceOfflineReceiver" >
    <intent-filter>
        <action android:name="com.example.broadcastbestpractice.
    FORCE_OFFLINE" />
    </intent-filter>
</receiver>
</application>
。。。。。。

    注意:由于我们在ForceOfflineReceiver 里弹出了一个系统级别的对话框,因此必须要声明android.permission.SYSTEM_ALERT_WINDOW 权限。


Git 时间,初识版本控制工具:

    访问网址http://msysgit.github.io/,下载完成后双击安装包进行安装,之后一直点击下一步就可以完成安装了。

    创建代码仓库:

    Windows 系统,就从开始里找到Git Bash 并打开。

    首先应该配置一下你的身份

    git config --global user.name "Tony"

    git config --global user.email "tony@gmail.com"

    配置完成后你还可以使用同样的命令来查看是否配置成功,只需要将最后的名字和邮箱地址去掉即可;


    我们尝试着给BroadcastBestPractice 项目建立一个代码仓库。先进入到BroadcastBestPractice 项目的目录下面,然后在这个目录下面输入如下命令:

    git init

    仓库创建完成后,会在BroadcastBestPractice项目的根目录下生成一个隐藏的.git文件夹,这个文件夹就是用来记录本地所有的Git 操作的,可以通过ls -al 命令来查看一下;

    如果你想要删除本地仓库,只需要删除这个文件夹就行了。


    提交本地代码:

    add 是用于把想要提交的代码先添加进来,而commit 则是真正地去执行提交操作。

    比如我们想添加AndroidManifest.xml 文件,就可以输入如下命令:

    git add AndroidManifest.xml

    添加某个目录只需要在add 后面加上目录名就可以了。

    比如将整个src 目录下的所有文件都进行添加,就可以输入如下命令:

    git add src

    在add 的后面加上一个点,就表示添加所有的文件了:

    git add .

    现在BroadcastBestPractice 项目下所有的文件都已经添加好了,我们可以来提交一下了:

    git commit -m "First commit."

    注意在commit 命令的后面我们一定要通过-m 参数来加上提交的描述信息,没有描述信息的提交被认为是不合法的。


原创粉丝点击