android从放弃到坚持放弃第五课

来源:互联网 发布:vibe算法改进代码 编辑:程序博客网 时间:2024/06/07 16:50

第五课学习笔记

  • 第五课学习笔记
    • 广播机制简介
    • 接受系统广播
      • 动态注册监听网络变化
      • 静态注册实现开机自启
    • 发送自定义广播
      • 标准广播
      • 发送有序广播
    • 使用本地广播
    • 最佳实践强制下线功能
    • git

第四课暂时跳过。

学习资料:第一行代码

广播机制简介

安卓中的每一个应用程序都可以对自己感兴趣的广播注册,这样程序就只会接收到自己关心的广播内容,这些广播可能来自系统,亦或是其他APP.广播主要分为两种

  1. 标准广播

完全异步执行的广播,广播发出后,所有广播接收器都会同一时刻接收到。效率比较高,但也意味这他无法被截获。

  1. 有序广播

同步执行。同一时刻只会有一个广播接收器能够接收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。接收器有先后顺序。

接受系统广播

系统广播:比如手机电量发生变化后会发出一条广播。事件或区域发生改变也会发出一条广播。

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

动态注册监听网络变化

注册广播一般有两种方式:在代码中注册和在AndroidManifest.xml中注册。

public class MainActivity extends AppCompatActivity {    private IntentFilter intentFilter;    private NetworkChangeReceiver networkChangeReceiver;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        intentFilter = new IntentFilter();        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_LONG).show();        }    }}

每当网络变化时,onReceive()方法就会执行。。

onCreate()方法中首先创建了一个IntentFilter的实例,并给他添加了一个值为。。。的anction,网络发生变化时,系统就会将这个广播发出去,接下来创建的是NetworkChangeReceiver的实例。然后调用registerReceiver()方法进行注册,传进去两个实例,这样NetworkChangeReceiver就会收到所有android,net,conn.CONNECTIVITY_CHANGE的广播。最后一定要取消注册。

public final void addAction (String action)

Add a new Intent action to match against. If any actions are included in the filter, then an Intent’s action must be one of those values for it to match. If no actions are included, the Intent action is ignored.

Parameters

action Name of the action to match, i.e. Intent.ACTION_VIEW.

public abstract void onReceive (Context context, Intent intent)

This method is called when the BroadcastReceiver is receiving an Intent broadcast. During this time you can use the other methods on BroadcastReceiver to view/modify the current result values. This method is always called within the main thread of its process, unless you explicitly asked for it to be scheduled on a different thread using registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler). When it runs on the main thread you should never perform long-running operations in it (there is a timeout of 10 seconds that the system allows before considering the receiver to be blocked and a candidate to be killed). You cannot launch a popup dialog in your implementation of onReceive().

If this BroadcastReceiver was launched through a tag, then the object is no longer alive after returning from this function. This means you should not perform any operations that return a result to you asynchronously – in particular, for interacting with services, you should use startService(Intent) instead of bindService(Intent, ServiceConnection, int). If you wish to interact with a service that is already running, you can use peekService(Context, Intent).

The Intent filters used in registerReceiver(BroadcastReceiver, IntentFilter) and in application manifests are not guaranteed to be exclusive. They are hints to the operating system about how to find suitable recipients. It is possible for senders to force delivery to specific recipients, bypassing filter resolution. For this reason, onReceive() implementations should respond only to known actions, ignoring any unexpected Intents that they may receive.

Parameters

context The Context in which the receiver is running. intent The Intent being received.

进行优化onReceive:

        public void onReceive(Context context, Intent intent){            ConnectivityManager connectivityManager = (ConnectivityManager)                     getSystemService(Context.CONNECTIVITY_SERVICE);            NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();            if(networkInfo != null && networkInfo.isAvailable()){                Toast.makeText(context, "network is available", Toast.LENGTH_LONG).show();            }else {                Toast.makeText(context, "network is unavailable", Toast.LENGTH_LONG).show();            }        }

首先通过getSystemService()方法得到了ConnectivityManager的实例,这是一个系统服务类,专门用语管理网站连接的。然后调用他的getActiveNetworkInfo()方法得到NetworkInfo的实例,接着调用isAvailable()方法就可以判断出当前是否有网络了。

注意:程序进行一些对用户来说比较敏感的操作,就需要在配置文件中申明权限。否则程序直接崩溃。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.wrjjrw.broadcasttest" >    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>....

静态注册实现开机自启

为了让程序在未启动的情况下就能接收到广播,这就需要静态注册。

让程序接受一条开机广播,当收到这条广播后就可以在onReceive()方法里执行相应的逻辑,从而实现开机启动的功能。右击包new→broadcastReceiver。命名为BootCompleteReceiver。Exported属性表示是否允许这个广播接收器接受本程序以外的广播,Enabled属性表示是否启用这个广播接收器。勾选,Finish.

public class BootCompleteReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        Toast.makeText(context, "Boot complete", Toast.LENGTH_LONG).show();    }}

另外在AndroidManifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.wrjjrw.broadcasttest">    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <receiver            android:name=".BootCompleteReceiver"            android:enabled="true"            android:exported="true">            <intent-filter>                <action android:name="android.intent.action.BOOT_COMPLETED" />            </intent-filter>        </receiver>    </application></manifest>

我没有成果,但我同学成果了。。。。。。。但我还没找到哪里有问题????


发送自定义广播

标准广播

新建一个类。MyBroadcastReceiver,代码如下:

public class MyBroadcastReceiver extends BroadcastReceiver {    @Override    public  void  onReceive(Context context, Intent intent){        Toast.makeText(context, "received in MyBroadcastReceiver"                ,Toast.LENGTH_LONG).show();    }}

修改AndroidManifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.wrjjrw.broadcasttest">    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <receiver            android:name=".BootCompleteReceiver"            android:enabled="true"            android:exported="true">            <intent-filter>                <action android:name="android.intent.action.BOOT_COMPLETED" />            </intent-filter>        </receiver>        <receiver            android:name=".MyBroadcastReceiver"            android:enabled="true"            android:exported="true"            >            <intent-filter>                <action android:name="com.example.wrjjrw.broadcasttest.MY_BROADCAST"/>            </intent-filter>        </receiver>    </application></manifest>

修改MainActivity:

public class MainActivity extends AppCompatActivity {    private IntentFilter intentFilter;    private NetworkChangeReceiver networkChangeReceiver;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        intentFilter = new IntentFilter();        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, final Intent intent){            ConnectivityManager connectivityManager = (ConnectivityManager)                    getSystemService(Context.CONNECTIVITY_SERVICE);            NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();            if(networkInfo != null && networkInfo.isAvailable()){                Toast.makeText(context, "network is available", Toast.LENGTH_LONG).show();            }else {                Toast.makeText(context, "network is unavailable", Toast.LENGTH_LONG).show();            }            Button button = (Button) findViewById(R.id.Broadcast);            button.setOnClickListener(new View.OnClickListener(){                @Override                public void  onClick(View v){                    Intent intent1 = new Intent                            ("com.example.wrjjrw.broadcasttest.MY_BROADCAST");                    sendBroadcast(intent1);                }            });        }    }}

跑路。


发送有序广播

新建一个项目Broadcasttest2。新建AnotherBroadcastReceiver类

public class AnotherBroadcastReceiver2 extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        Toast.makeText(context, "received in Another Receiver" ,Toast.LENGTH_LONG).show();    }}

修改Manifest:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.wrjjrw.broadcasttest2">    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <receiver            android:name=".AnotherBroadcastReceiver2"            android:exported="true"            android:enabled="true"            >            <intent-filter>                <action android:name="com.example.wrjjrw.broadcasttest.MY_BROADCAST"></action>            </intent-filter>        </receiver>    </application></manifest>

跑路,你会发现,点之前的按钮,这个app也能接受到相应的广播。

来改成有序广播:

            Button button = (Button) findViewById(R.id.Broadcast);            button.setOnClickListener(new View.OnClickListener(){                @Override                public void  onClick(View v){                    Intent intent1 = new Intent                            ("com.example.wrjjrw.broadcasttest.MY_BROADCAST");                    sendOrderedBroadcast(intent1, null);                }            });

项目一的按钮改一下。可以发现,第一个参数还是原来的intent1,第二个参数是指的与权限相关的字符串,这里传入null就可以了。运行起来跟之前的差不多。

设置先后顺序:

            <intent-filter android:priority="100">                <action android:name="com.example.wrjjrw.broadcasttest.ANOTHER_BROADCAST"></action>            </intent-filter>

这样干就好了

当然还可以截断。

    public  void  onReceive(Context context, Intent intent){        Toast.makeText(context, "received in MyBroadcastReceiver"                ,Toast.LENGTH_LONG).show();        abortBroadcast();    }

abortBroadcast()方法截断这条广播。


使用本地广播

之前使用的都是属于系统广播,即可以被任何应用程序接受到,同时也可以被介绍到来自其他任何应用程序的广播。但容易引起安全问题

我修改了之前的MainActivity:

public class MainActivity extends AppCompatActivity {    private IntentFilter intentFilter;    private LocalReceiver localReceiver;    private LocalBroadcastManager localBroadcastManager;    private NetworkChangeReceiver networkChangeReceiver;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        localBroadcastManager = LocalBroadcastManager.getInstance(this);//get instance        intentFilter = new IntentFilter();        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");        networkChangeReceiver = new NetworkChangeReceiver();        registerReceiver(networkChangeReceiver, intentFilter);        Button button = (Button) findViewById(R.id.Broadcast);        button.setOnClickListener(new View.OnClickListener(){            @Override            public void  onClick(View v){                Intent intent1 = new Intent                        ("com.example.wrjjrw.broadcasttest.MY_BROADCAST");                //sendOrderedBroadcast(intent1, null);                Intent intent2 = new Intent                        ("com.example.wrjjrw.broadcasttest.LOCAL_BROADCAST");                localBroadcastManager.sendBroadcast(intent2);// send local broadcast            }        });        intentFilter = new IntentFilter();        intentFilter.addAction("com.example.wrjjrw.broadcasttest.LOCAL_BROADCAST");        localReceiver = new LocalReceiver();        localBroadcastManager.registerReceiver(localReceiver, intentFilter);//注册本地广播        Button button1 = (Button) findViewById(R.id.another_broadcast);        button1.setOnClickListener(new View.OnClickListener(){            @Override            public void  onClick(View v){                Intent intent1 = new Intent                        ("com.example.wrjjrw.broadcasttest.ANOTHER_BROADCAST");                sendBroadcast(intent1);            }        });    }    @Override    protected void onDestroy(){        super.onDestroy();        unregisterReceiver(networkChangeReceiver);        localBroadcastManager.unregisterReceiver(localReceiver);    }    class NetworkChangeReceiver extends BroadcastReceiver{        @Override        public void onReceive(Context context, final Intent intent){            ConnectivityManager connectivityManager = (ConnectivityManager)                    getSystemService(Context.CONNECTIVITY_SERVICE);            NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();            if(networkInfo != null && networkInfo.isAvailable()){                Toast.makeText(context, "network is available", Toast.LENGTH_LONG).show();            }else {                Toast.makeText(context, "network is unavailable", Toast.LENGTH_LONG).show();            }        }    }    class LocalReceiver extends BroadcastReceiver{        @Override        public void onReceive(Context context, Intent intent){            Toast.makeText(context, "received local broadcast", Toast.LENGTH_LONG).show();        }    }}

可以对比,发现除了开头是通过LocalBroadcastManager的getInstance()得到实例,然后调用了他的registerReceiver方法。其他的和刚刚的差不多。

注意:本地广播无法通过静态注册的方式来接受。因为静态注册就是为让程序在未启动的情况下也能接受到广播,而发送本地广播时,我们的程序已经启动,无序静态注册的功能。

总结本地广播的advantages:

  1. 明确知道正在发送的广播不会离开我们的程序,不必担心机密数据泄漏
  2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐患
  3. 发送本地广播比系统全局广播更加高效。

最佳实践:强制下线功能

当我们被通知强制下线时,不需在每一个界面上都编写一个弹出对话框的逻辑。可以利用广播。

重新新建一个project:(BroadcastBestPractise)

借用第二章的编写一个ActivityCollector类管理所有的活动:

public class ActivityCollector {    public static List<Activity> activities = new ArrayList<>();    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类作为所有活动的父类:

public class BaseActivity extends AppCompatActivity {    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        ActivityCollector.addActivity(this);    }    @Override    protected void onDestroy() {        super.onDestroy();        ActivityCollector.removeActivity(this);    }}

新建LoginActivity,先修改activity_login.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/activity_login"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context="com.example.wrjjrw.broadcastbestpractise.LoginActivity">    <LinearLayout        android:layout_width="match_parent"        android:layout_height="60dp"        android:orientation="horizontal"        >        <TextView            android:layout_width="90dp"            android:layout_height="match_parent"            android:layout_gravity="center_vertical"            android:layout_margin="10dp"            android:textSize="18sp"            android:text="账号:"            />        <EditText            android:id="@+id/account"            android:layout_width="0dp"            android:layout_height="match_parent"            android:layout_weight="1"            android:layout_gravity="center_vertical"            />    </LinearLayout>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="60dp"        android:orientation="horizontal"        >        <TextView            android:layout_width="90dp"            android:layout_height="match_parent"            android:layout_gravity="center_vertical"            android:layout_margin="10dp"            android:textSize="18sp"            android:text="密码:"            />        <EditText            android:id="@+id/passord"            android:layout_width="0dp"            android:layout_height="match_parent"            android:layout_weight="1"            android:inputType="textPassword"            />    </LinearLayout>    <Button        android:id="@+id/login"        android:layout_width="match_parent"        android:layout_height="80dp"        android:layout_margin="30dp"        android:layout_gravity="center"        android:text="登陆"        android:textSize="40dp"        android:background="@mipmap/ic_launcher"        />    <ImageView        android:layout_width="match_parent"        android:layout_height="match_parent"        android:src="@drawable/huchi"        /></LinearLayout>

再修改LoginActivity:

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.activity_login);        accountEdit = (EditText) findViewById(R.id.account);        passwordEdit = (EditText) findViewById(R.id.passord);        login = (Button) findViewById(R.id.login);        login.setOnClickListener(new View.OnClickListener(){            @Override            public void onClick(View v) {                String account = accountEdit.getText().toString();                String password = passwordEdit.getText().toString();                if(account.length() > 0 && password.length() > 0){                    Intent intent = new Intent(LoginActivity.this, MainActivity.class);                    startActivity(intent);                    finish();                }else{                    Toast.makeText(LoginActivity.this, "account or password is invalid"                            , Toast.LENGTH_LONG).show();                }            }        });    }}

这里模拟了一个登陆。

再修改MainActivity:

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 View.OnClickListener(){            @Override            public void onClick(View v) {                Intent intent = new Intent("com.example.wrjjrw." +                        "broadcastbestpractise.FORCE_OFFLINE");                sendBroadcast(intent);            }        });    }}

我们在这里发送了一条广播,这条广播就是用来强制用户下线的。但强制用户下线的逻辑不是写在MainActivity,我们需要写在他的父类BaseActivity中才可以对其他的也产生作用。

修改BaseActivity:

public class BaseActivity extends AppCompatActivity {    private ForceOfflineReceiver receiver;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        ActivityCollector.addActivity(this);    }    @Override    protected void onResume() {        super.onResume();        IntentFilter intentFilter = new IntentFilter();        intentFilter.addAction("com.example.wrjjrw." +                "broadcastbestpractise.FORCE_OFFLINE");        receiver = new ForceOfflineReceiver();        registerReceiver(receiver, intentFilter);    }    @Override    protected void onPause() {        super.onPause();        if(receiver != null){            unregisterReceiver(receiver);            receiver = null;        }    }    @Override    protected void onDestroy() {        super.onDestroy();        ActivityCollector.removeActivity(this);    }    class ForceOfflineReceiver extends BroadcastReceiver{        @Override        public void onReceive(final Context context, final Intent intent) {            AlertDialog.Builder builder = new AlertDialog.Builder(context);            builder.setTitle("Warning");            builder.setMessage("You are being loved!");            builder.setCancelable(false);            builder.setPositiveButton("Me,too", new DialogInterface.OnClickListener() {                @Override                public void onClick(DialogInterface dialog, int which) {                    ActivityCollector.finishAll();                    Intent intent1 = new Intent(context, LoginActivity.class);                    context.startActivity(intent1);//return login activity                }            });            builder.show();        }    }}

顺带复习了第三章的UI知识,这里不作解释。

这里重写了两个生命周期函数,分别在这里注册和取消注册了ForceOfflineReceiver。

放在这两个函数是因为我需要始终保持处于栈顶的活动才能接收到这条强制下线广播,非栈顶的活动不应该接受这个广播。

最后修改AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.wrjjrw.broadcastbestpractise">    <application        android:allowBackup="true"        android:icon="@drawable/huchi"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">        </activity>        <activity android:name=".LoginActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>    </application></manifest>

我顺便改了一个图标,啊哈哈哈哈.finish.

git

git部分请看我另外一个学习笔记(虽然我知道没人看的​:smile:​)

CLICK ME

1 0
原创粉丝点击