前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >android学习笔记----自定义广播和系统广播

android学习笔记----自定义广播和系统广播

作者头像
砖业洋__
发布于 2023-05-06 11:04:46
发布于 2023-05-06 11:04:46
1.7K00
代码可运行
举报
文章被收录于专栏:博客迁移同步博客迁移同步
运行总次数:0
代码可运行

自定义广播:

无序广播(标准广播):

是一种异步执行的广播,在广播发出之后,所有的广播接收器几乎在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播效率会比较高,同时也是无法被截断或修改数据的。这种广播是全局性的,其他的应用程序都可以接受到。

首先来看一个无序广播的例子,不管有没有人接收我都要广播出去,就像新闻联播有没有人看我都是准点开播

发送方:

MainActivity.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    // 点击按钮,发送一条无序广播
    public void onclick(View view) {
        Intent intent = new Intent();
        intent.setAction("mybroadcast");
        intent.putExtra("name", "新联联播,我来广播");
        sendBroadcast(intent);
    }
}

这里因为是自定义广播,这个action我们就可以自己设置,我在接收方设置成了"mybroadcast"

所以这里intent.setAction("mybroadcast");

activity_main.xml

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:orientation="vertical">
    <Button
        android:onClick="onclick"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发起无序广播"/>
</LinearLayout>

再来看接收方:

MainActivity.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

ReceiveCustomReceiver.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class ReceiveCustomReceiver extends BroadcastReceiver {
    // 当接收到发送的自定义广播时调用
    @Override
    public void onReceive(Context context, Intent intent) {
        String content = intent.getStringExtra("name");
        Toast.makeText(context, "在接受无序广播中收到了该内容:" + content, Toast.LENGTH_SHORT).show();
    }
}

AndroidManifest.xml

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.receive_unordered_broadcast">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        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=".ReceiveCustomReceiver">
            <intent-filter>
                <action android:name="mybroadcast" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

这里和发送方对应,action的name为"mybroadcast"

依次把接收方和发送方运行到模拟器,来看看运行结果:

在发送方的界面,点击发送无序广播,看到接收方的Toast显示出来了,接收到了广播

有序广播:

是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕之后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先接收到广播消息,并且前面的广播接收器还可以截断或者修改正在传递的广播,这样后面的广播无法接受到广播消息或者接收到不真实的广播消息。这种广播是全局性的,其他的应用程序都可以接受到。

接下来演示有序广播,用上级领导给下农民一级一级的发大米的例子

可能中途被某一级官员贪污,或者修改反馈大米的数量,下面一起来看看

领导发送方:

MainActivity.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    // 点击按钮发送有序广播
    public void onlick(View view) {
        Intent intent = new Intent();
        intent.setAction("myfadami");
        // 第3个参数为最后的广播,如果为null则不接收
        // 但是最后的广播也可能造假,可能中途被某一级修改并终止了
        sendOrderedBroadcast(intent, null, new FinalReceiver(), null,
                1, "上级领导给每个村民发1000斤大米", null);
    }
}

关于

public void sendOrderedBroadcast (Intent intent,

String receiverPermission,

BroadcastReceiver resultReceiver,

Handler scheduler, int initialCode,

String initialData,

Bundle initialExtras)

sendBroadcast(Intent) 的版本,允许您从广播中接收数据。这是通过在呼叫时提供自己的广播收发器来实现的,它将被视为广播结束时的最终接收器-ITSBroadcastReceiver.onReceive(Context, Intent)方法将使用从其他接收方收集的结果值调用。广播将以与调用相同的方式序列化。sendOrderedBroadcast(Intent, String).

喜欢sendBroadcast(Intent),此方法是异步的;它将在调用结果tReceiver.onRecept()之前返回。

看见BroadcastReceiver有关意图广播的更多信息。

参数

intent

Intent:广播的意图;所有符合这一意图的接收者都将收到广播。

receiverPermission

String:字符串命名接收方接收广播时必须持有的权限。如果为NULL,则不需要任何权限。

resultReceiver

BroadcastReceiver:您自己的广播收发器作为广播的最终接收方。

scheduler

Handler:一个自定义处理程序,用于调度结果收发器回调;如果为NULL,则将在上下文的主线程中调度它。

initialCode

int: 结果代码的初始值。通常是Activity.RESULT_OK。

initialData

String:结果数据的初始值。通常是空的。

initialExtras

Bundle:结果附加值的初始值。通常是空的。

FinalReceiver.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class FinalReceiver extends BroadcastReceiver { // 不用在清单文件配置
    @Override
    public void onReceive(Context context, Intent intent) {
        // 获取发送广播携带的数据
        String content = getResultData();
        // 显示结果
        Toast.makeText(context, "报告领导,接收到下级发送的反馈消息:\n" + content, Toast.LENGTH_SHORT).show();
    }
}

接收方官员和农民的过程:

MainActivity.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onDestroy() { // 广播在该应用程序销毁后仍能接收
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");
    }
}

部署运行只有可直接返回退出,看到onDestroy之后广播消息仍然能够接收

省领导:

ProvinceReceiver.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class ProvinceReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 获取发送广播携带的数据
        String content = getResultData();
        // 显示结果
        Toast.makeText(context, "省:" + content, Toast.LENGTH_SHORT).show();
        // 如果上级领导发的大米不想给下面的人,那么就直接终止广播
        // abortBroadcast();
        // 如果此时贪污300斤大米,再往下面发大米
        // 修改收到的广播内容
        setResultData("上级领导给每个村民发700斤大米");
    }
}

市领导:

CityReceiver.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class CityReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 获取发送广播携带的数据
        String content = getResultData();
        // 显示结果
        Toast.makeText(context, "市:" + content, Toast.LENGTH_SHORT).show();
        // 如果上级领导发的大米不想给下面的人,那么就直接终止广播
        // abortBroadcast();
        // 如果此时贪污200斤大米,再往下面发大米
        // 修改收到的广播内容
        setResultData("上级领导给每个村民发500斤大米");
    }
}

乡领导:

CountryReceiver.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class CountryReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
// 获取发送广播携带的数据
        String content = getResultData();
        // 显示结果
        Toast.makeText(context, "乡:" + content, Toast.LENGTH_SHORT).show();
        // 如果上级领导发的大米不想给下面的人,那么就直接终止广播
        // abortBroadcast();
        // 如果此时贪污200斤大米,再往下面发大米
        // 修改收到的广播内容
        setResultData("上级领导给每个村民发300斤大米");
    }
}

农民:

FarmerReceiver.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class FarmerReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 获取发送广播携带的数据
        String content = getResultData();
        // 显示结果
        Toast.makeText(context, "农民:" + content, Toast.LENGTH_SHORT).show();
    }
}

AndroidManifest.xml

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.receive_orderbroadcast">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        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=".ProvinceReceiver">
            <intent-filter android:priority="1000">
                <action android:name="myfadami" />
            </intent-filter>
        </receiver>
        <receiver android:name=".CityReceiver">
            <intent-filter android:priority="500">
                <action android:name="myfadami" />
            </intent-filter>
        </receiver>
        <receiver android:name=".CountryReceiver">
            <intent-filter android:priority="100">
                <action android:name="myfadami" />
            </intent-filter>
        </receiver>
        <receiver android:name=".FarmerReceiver">
            <intent-filter android:priority="-100">
                <action android:name="myfadami" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

运行结果如下:

如果此时省领导很大野心,想贪污1000斤大米,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class ProvinceReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 获取发送广播携带的数据
        String content = getResultData();
        // 显示结果
        Toast.makeText(context, "省:" + content, Toast.LENGTH_SHORT).show();
        // 如果上级领导发的大米不想给下面的人,那么就直接终止广播
        abortBroadcast();
        // 如果此时贪污1000斤大米
        // 修改收到的广播内容
        setResultData("上级领导给每个村民发1000斤大米");
    }
}

直接终止广播,并且修改最后广播的内容,那么向上级反馈的就是1000斤大米,这样就成功的欺骗的上级

运行结果图:

自己发送的自定义广播可以根据是sendOrderedBroadcast还是sendBroadcast来判断是有序广播还是无序广播,系统广播我们可以用abortBroadcast来阻止接收广播测试,如果能够终止广播,那么说明是有序广播,反之则为无序广播。

本地广播:

不管是有序广播无序广播还是系统的广播都是全局性的广播,即发出的广播可以被其他任何应用程序接收到,并且我们也可以接受来自于其他任何应用程序的广播,这样就很容易引起安全问题,比如我们发送一些携带关键性数据的广播有可能被其他应用程序截获,或者其他应用程序向我们的广播接收器里发送各种垃圾广播。

1、本地广播:发送的广播事件不被其他应用程序获取,也不能响应其他应用程序发送的广播事件。本地广播只能被动态注册,不能静态注册。动态注册或方法时需要用到LocalBroadcastManager。

2、全局广播:发送的广播事件可被其他应用程序获取,也能响应其他应用程序发送的广播事件(可以通过 exported–是否监听其他应用程序发送的广播 在清单文件中控制) 全局广播既可以动态注册,也可以静态注册。

MainActivity.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private LocalBroadcastManager localBroadcastManager;
    private IntentFilter intentFilter;
    private LocalReceiver localReceiver;

    @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 View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("mylocalbroadcast");
                localBroadcastManager.sendBroadcast(intent); // 发送本地广播
            }
        });
        intentFilter = new IntentFilter();
        intentFilter.addAction("mylocalbroadcast");
        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.发送本地广播会比发送系统全局广播更高效。

系统广播:

广播接收器可以自由的对自己感兴趣的广播进行注册,这样当有相应的广播发出时,广播接收器就能够接收到该广播,并在内部处理相应的逻辑。注册广播的方式一般有2种,在代码中注册和在AndroidManifest.xml中注册,前者被称为动态注册,后者被称为静态注册

动态注册一般用于操作特别频繁的广播事件,比如屏幕的锁屏与解锁,电池电量的变化。

动态注册:

体验一下动态注册,屏幕的锁屏与解锁:

ScreenReceiver.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class ScreenReceiver extends BroadcastReceiver {
    private static final String TAG = "ScreenReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if ("android.intent.action.SCREEN_OFF".equals(action)){
            Log.d(TAG, "屏幕锁屏了");
        } else if ("android.intent.action.SCREEN_ON".equals(action)){
            Log.d(TAG, "屏幕解锁了");
        }
    }
}

MainActivity.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private ScreenReceiver screenReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 动态的注册广播接收者
        screenReceiver = new ScreenReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("android.intent.action.SCREEN_ON");
        intentFilter.addAction("android.intent.action.SCREEN_OFF");
        registerReceiver(screenReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 当activity销毁的时候要取消注册广播接收者
        unregisterReceiver(screenReceiver);
    }
}

如果是静态注册,那么需要在清单文件如下操作:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        <receiver android:name=".ScreenReceiver">
            <intent-filter>
                <action android:name="android.intent.action.SCREEN_ON" />
                <action android:name="android.intent.action.SCREEN_OFF" />
            </intent-filter>
        </receiver>

所以对应到动态注册就是:

IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("android.intent.action.SCREEN_ON"); intentFilter.addAction("android.intent.action.SCREEN_OFF");

一定要记得,动态注册的广播接收器一定要取消注册才行,否则会报错

android.app.IntentReceiverLeaked: Activity com.example.dynamicregistrationbroadcastreceiver.MainActivity has leaked IntentReceiver com.example.dynamicregistrationbroadcastreceiver.ScreenReceiver@8bd6a7b that was originally registered here. Are you missing a call to unregisterReceiver()?

运行结果:

静态注册:

静态注册实现开机启动:

       动态注册的广播接收器可以自由的控制与注销,在灵活性方面有很大的优势,但是它也存在一个缺点,就是必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在onCreate()中的,那么有什么办法可以让程序未启动的情况下就能接收到广播呢,那就是静态注册了。

BootReceiver.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

public class BootReceiver extends BroadcastReceiver {
    private static final String TAG = "BootReceiver";

    // 当手机重新启动的时候调用,经测试6.0以后此方法行不通
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "=================onReceive: ");
        // 在这个方法里面开启activity
        Intent intent1 = new Intent(context, MainActivity.class);
        // ==========注意,不能在广播中直接开启activity,需要添加一个任务栈标记
        // intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        // 开启activity
        context.startActivity(intent1);
        Toast.makeText(context, "开机自动启动成功", Toast.LENGTH_LONG).show();
    }
}

 ==========注意,不能在广播中直接开启activity,需要添加一个任务栈标记 intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

其实这句话可以不加,但是需要早AndroidManifest中添加属性,继续往下看吧

MainActivity.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "==============onCreate: ");
    }
}

AndroidManifest.xml

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.startup">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        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=".BootReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

批注:

    Exported属性表示是否允许这个广播接收器接收本程序以外的广播,Enabled属性表示是否启用这个广播接收器。

    如果只有Exported属性而不加Enabled属性,则会报错,如下:E/MS_RegisterService: Exception getting GCM token     java.io.IOException: AUTHENTICATION_FAILED

如果只有Enabled属性而不加Exported属性,则会打开activity页面但是不会弹出Toast

两个属性都加上就没问题了,就不需要在广播接收器中添加intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

运行结果: 

模拟器可以成功,遗憾的是真机不一定成功。具体解决方案参考这里:

Android 8.0 的开机广播 和 IntentService:https://www.jianshu.com/p/378819c21bde

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

广播实践----实现强制下线功能:

demo地址:https://github.com/liuchenyang0515/BroadcastBestPractice

运行效果图:

ActivityCollector.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.app.Activity;

import java.util.ArrayList;
import java.util.List;

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 activty : activities) {
            if (!activty.isFinishing()){
                activty.finish();
            }
        }
    }
}

activity_login.xml

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:text="Account:"
            android:textSize="18sp" />

        <EditText
            android:id="@+id/account"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_weight="1"
            android:hint="请输入帐号" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:text="Password:"
            android:textSize="18sp" />

        <EditText
            android:id="@+id/password"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_weight="1"
            android:hint="请输入密码"
            android:inputType="textPassword" />
    </LinearLayout>

    <Button
        android:id="@+id/login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="login"
        android:textAllCaps="false" />
</LinearLayout>

LoginActivity.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class LoginActivity extends BaseActivity {

    private EditText passwordEdit;
    private EditText accountEdit;
    private Button login;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        accountEdit = (EditText) findViewById(R.id.account);
        passwordEdit = (EditText) findViewById(R.id.password);
        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();
                // 只演示广播功能,用固定帐号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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/force_offline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send force offline broadcast"
        android:textAllCaps="false" />
</LinearLayout>

MainActivity.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

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.broadcastbestpractice.FORCE_OFFLINE");
                sendBroadcast(intent);
            }
        });
    }
}

BaseActivity.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class BaseActivity extends AppCompatActivity {

    private ForceOfflineReceiver receiver;
    private static final String TAG = "BaseActivity";

    @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.broadcastbestpractice.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) {
            Log.d(TAG, "onReceive: ");
            AlertDialog.Builder builder = new AlertDialog.Builder(context);
            builder.setTitle("Warning");
            builder.setMessage("You are forced to be offline. Please try to login again");
            builder.setCancelable(false);
            builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    ActivityCollector.finishAll(); // 销毁所有活动
                    Intent intent1 = new Intent(context, LoginActivity.class);
                    context.startActivity(intent1); // 重新启动LoginActivity
                }
            });
            builder.show();
        }
    }
}

批注:

注册ForceOfflineReceiver这个广播接收器时,这里重写了onResume()和onPause()这两个生命周期方法,然后在这两个方法中注册和取消注册了ForceOfflineReceiver。为什么呢?之前不是都在onCreate()和onDestroy()方法里的来注册和取消广播接收器的吗?这是因为我们始终需要保证只有处于栈顶的活动才能接收到这条强制下线广播,非栈顶的活动不应该也没有必要去接收这条广播,所以写在onResume()和onPause()方法里就可以很好的解决这个问题,当一个活动失去栈顶位置时,就会自动取消广播接收器的注册。

AndroidManifest.xml

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.broadcastbestpractice">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        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>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
使用Typescript和ES模块发布Node模块
TypeScript已经成为一种非常流行的JavaScript语言,这是有原因的。它的类型系统和编译器能够在您的软件运行之前的编译时捕获各种bug,并且附加的代码编辑器功能使它成为一个非常适合开发人员的高效环境。
张张
2020/05/27
2.7K0
使用Typescript和ES模块发布Node模块
会写 TypeScript 但你真的会 TS 编译配置吗?
随着 TypeScript 的流行,越来越多的项目通过使用 TypeScript 来实现编写代码时候的类型提示和约束,从开发过程中减少 BUG 出现的概率,以此提升程序的健壮性和团队的研发效率。
小东同学
2022/07/29
4K1
会写 TypeScript 但你真的会 TS 编译配置吗?
TypeScript必知三部曲(一)TypeScript编译方案以及IDE对TS的类型检查
TypeScript代码的编译过程一直以来会给很多小伙伴造成困扰,typescript官方提供tsc对ts代码进行编译,babel也表示能够编译ts代码,它们二者的区别是什么?我们应该选择哪种方案?为什么IDE打开ts项目的时候,就能有这些ts代码的类型定义?为什么明明IDE对代码标红报错,但代码有能够编译出来?
w4ngzhen
2023/10/18
1K0
TypeScript必知三部曲(一)TypeScript编译方案以及IDE对TS的类型检查
【TypeScript 编程】001-002 第 1 章 导言 与 第 2 章 TypeScript 概述
https://blog.csdn.net/Aria_Miazzy/article/details/105430400
訾博ZiBo
2025/01/06
1020
【TypeScript 编程】001-002 第 1 章 导言 与 第 2 章 TypeScript 概述
去除typescript代码类型
在短时间内有一个需求,原项目代码是 js,而我手里头的功能代码是 ts 的,需要将其合并。
愧怍
2022/12/27
2.7K0
十分钟了解 TypeScript 是如怎样工作的
本文概述了 TypeScript 的工作原理:典型的 TypeScript 项目的结构是什么?什么被编译以及怎样编译?我们如何使用 IDE 编写 TypeScript?
疯狂的技术宅
2020/05/11
1.4K0
十分钟了解 TypeScript 是如怎样工作的
TypeScript与Babel、webpack的关系以及IDE对TS的类型检查
只要接触过ts的前端同学都能回答出ts是js超集,它具备静态类型分析,能够根据类型在静态代码的解析过程中对ts代码进行类型检查,从而在保证类型的一致性。那,现在让你对你的webpack项目(其实任意类型的项目都同理)加入ts,你知道怎么做吗?带着这个问题,我们由浅入深,逐步介绍TypeScript、Babel以及我们日常使用IDE进行ts文件类型检查的关系,让你今后面对基于ts的工程能够做到游刃有余。
w4ngzhen
2023/10/17
8020
TypeScript与Babel、webpack的关系以及IDE对TS的类型检查
TypeScript编译选项
TypeScript编译选项是用于配置TypeScript编译器(tsc)的选项,用于指定编译过程中的行为和输出结果。通过这些选项,我们可以自定义编译器的行为,以满足项目的特定需求。
堕落飞鸟
2023/05/22
7280
如何安装 TypeScript,并配置开发环境以便开始使用
TypeScript 是一种由微软开发的静态类型编程语言,它可以作为 JavaScript 的超集使用,并且可以在编译时进行类型检查。TypeScript 提供了更强大的工具和功能,使开发者能够更轻松地编写可维护、可扩展的代码。本文将详细介绍如何安装 TypeScript,并配置开发环境以便开始使用。
网络技术联盟站
2023/07/06
1.2K0
TypeScript
lib用于指定要包含在编译中的库文件 “lib”:[ “es6”, “dom” ],
jinghong
2020/05/12
1.4K0
《现代Typescript高级教程》解读TSConfig
TypeScript 配置文件(tsconfig.json)是用于配置 TypeScript 项目的重要文件。它允许开发者自定义 TypeScript 编译器的行为,指定编译选项、文件包含与排除规则、输出目录等。通过合理配置 tsconfig.json,我们可以根据项目需求进行灵活的 TypeScript 编译设置。
linwu
2023/07/27
6370
深入理解 TypeScript 模块
在程序设计中,为完成某一功能所需的一段程序或子程序,或指能由编译程序、装配程序等处理的独立程序单位;或指大型软件系统的一部分
网罗开发
2021/04/26
2.6K0
深入理解 TypeScript 模块
云函数 + TypeScript + Node.js 最佳实践探索
目的 最近 Serverless 愈来愈火,我刚好在培训,比较有时间去尝试一些新东西,所以趁这个时候去使用下 Serverless,尝试使用 Typescript 和 nodejs 开发,部署在腾讯云 SCF 上的一个小工具,探讨下 Typescript+ Node.js + SCF 的最好实践模式,并同时抛钻引玉,希望有同学提供更好的方案。 ---- 项目介绍 一、想法 本人有时候会追剧,但是剧集更新时,我一般都是不知道的。只有空闲的时候,才会特地去查看它们有没有更新。如果有这么一个工具,能够在剧集更
腾讯云serverless团队
2019/08/09
3K0
云函数 + TypeScript + Node.js 最佳实践探索
TypeScript学习笔记(三)—— 编译选项、声明文件
compilerOptions ⽀持很多选项,常⻅的有 baseUrl 、 target 、 moduleResolution 和 lib 等。 compilerOptions 每个选项的详细说明如下:
张果
2022/10/04
2.6K0
TypeScript学习笔记(三)—— 编译选项、声明文件
1、TypeScript初识及环境搭建
TypeScript最近很火,有很多开源的项目也是由其开发的,如果你是一名前端开发工程师或准备要成为一名前端开发工程师的话,现在如果再不学习TypeScript就真的太OUT了!那么现在请你系好安全带,和我一起踏上学习TypeScript之旅吧!
用户1272076
2019/03/26
4640
【腾讯云Serverless】腾讯云Serverless + Typescript实践
最近serverless愈来愈火,我刚好在培训,比较有时间去尝试一些新东西,所以趁这个时候去使用下serverless,尝试使用typescript和nodejs开发,部署在腾讯云scf上的一个小工具,探讨下typescript + nodejs + scf的最好实践模式,并同时抛钻引玉,希望有同学提供更好的方案。
Juli
2019/08/22
148.8K1
教你 30 秒发布一个 TypeScript 包到 NPM
文章读译自 The 30 second guide to publishing a typescript package to npm,部分内容有修改哈。
savokiss
2019/11/06
1.9K0
TypeScript 入门
通过 node -v 命令查看本机是否安装,如果没有安装,参考node.js安装指南 根据电脑系统环境进行安装
王秀龙
2021/08/23
1.7K0
TypeScript 入门
TypeScript 开发环境搭建
下载 node.js 下载地址:https://nodejs.org/en/ 安装 node.js 无其他特殊操作,选择好安装路径直接安装即可 安装完成后,使用win+R快捷键打开 cmd 命令行窗口
Emperor_LawD
2021/05/13
1.2K0
TypeScript 开发环境搭建
TypeScript增量编译
我们在使用Node.js开发服务端时通常会使用TypeScript来开发大型项目,但是使用ts-node进行全量编译时经常遇到编译速度慢的问题,通常是修改一行代码编译两分钟。这时我们需要使用增量编译来优化编译速度,及其他的文件在项目启动时进行全量编译,开发时修改了哪个TS文件就编译成对应的JS文件。
用户6256742
2022/07/06
1.5K0
推荐阅读
相关推荐
使用Typescript和ES模块发布Node模块
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档