首先做个小结:
在本章学习的时候,因为Android8的新特性,出现了两个与书上的不太相符的地方,都是在5.3发送自定义广播一节中出现,查询资料解决了一个发送标准广播的问题,Android8对广播协议做了限制
从清单文件中移除广播接收器 由于 Android 8.0 引入了新的广播接收器限制,因此您应该移除所有为隐式广播 Intent 注册的广播接收器。将它们留在原位并不会在构建时或运行时令应用失效,但当应用运行在 Android 8.0 上时它们不起任何作用。 显式广播 Intent(只有您的应用可以响应的 Intent)在 Android 8.0 上仍以相同方式工作。 这个新增限制有一些例外情况。如需查看在以 Android 8.0 为目标平台的应用中仍然有效的隐式广播的列表,请参阅隐式广播例外。 |
解决办法是添加setComponent参数,ComponetName(“自定义广播的包名”, “自定义广播的路径”);
通过这个方法我解决了发送标准广播的的实例,然而在发送有序广播室出现了问题,因为是要被两个接收器接到,所以在添加compone参数时如果只添加一个则可以显示一个,如果添加两个则都无显示,也没有出现书上所示的出现两次广播。这个令人费解,我没有找到相关的资料,希望各位大佬不吝赐教。
言归正传:开始对最佳实践time:
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();
}
}
activities.clear();
}
}
小结:无非是添加 移出 销毁三步走;
第儿步,创建BaseActivity类,顾名思义作为所有活动的父类。
第三步是创建一个登录活动LoginActivity;默认as生产相应的布局文件,默认的代码300多行,吓我一跳,看了界面的确挺接近我们平时接触的界面,但是用书上的一句话:这里我们不需要什么花哨的功能.。 所以就精简了哈;
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".LoginActivity">
<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:textSize="18sp"
android:text="Account:"/>
<EditText
android:id="@+id/account"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1" />
</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:inputType="textPassword" />
</LinearLayout>
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Login"
/>
</LinearLayout>
还是可视的布局来的直接,不多解释,上图:
接下来是LoginActivity的代码;
public class LoginActivity extends BaseActivity {
private EditText accountEdit;
private EditText passwordEdit;
private Button login;
private static final int REQUEST_READ_CONTACTS = 0;
@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.password);
login=(Button)findViewById(R.id.login);
login.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
String account=accountEdit.getText().toString();
String password=passwordEdit.getText().toString();
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();
}
}
});
}
}
当用户名与登录密码与预设的不一致时,弹出提示框,一致时这里使用显示intent转入MainActivity活动。
第四步,主活动干哈呢,说好的强制下线呢,所以在这里实现强制下线活动,给个按钮就行。修改布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/force_ofline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send force offline broadcast"/>
</LinearLayout>
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_ofline);
forceOffline.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent i=new Intent("come.example.yt.broadcastbestpractice.FORCE_OFFLINE");
sendBroadcast(i);
}
});
}
}
这里唯一的重点就是那个广播值,come.example.yt.broadcastbestpractice.FORCE_OFFLINE,发送强制下线的广播,也就是说MainActivity并不直接强制用户下线,而是通过广播的形式让其程序去执行强制下线功能。
那么问题来了,知道应该要创建一个Receiver 来接收广播,问题是在哪?显然静态注册是无法满足这个功能的,因为广播接收器里面需要弹出一个对话框来阻塞用户的正常操作,静态注册是无法在onReceive方法内弹出对话框这样的UI控件。
所以只能动态注册,在哪?书上说是BaseActivity,好吧,我也并不是特别理解,意向它是父亲啊,所以其他的都是继承它的,上代码
public class BaseActivity extends AppCompatActivity {
private ForceOffLineReceiver receiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityCollector.addActivity(this);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction("come.example.yt.broadcastbestpractice.FORCE_OFFLINE");
receiver=new ForceOffLineReceiver();
registerReceiver(receiver,intentFilter);
}
@Override
protected void onPause() {
super.onPause();
if(receiver!=null){
unregisterReceiver(receiver);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
class ForceOffLineReceiver extends BroadcastReceiver{
@Override
public void onReceive(final Context context, Intent intent) {
AlertDialog.Builder builder=new AlertDialog.Builder(context);//使用AlertDialog.Builder方法构建对话框。
builder.setTitle("Warnning");//设置对话框的标题
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 dialogInterface, int i) {
ActivityCollector.finishAll();//销毁所有活动
Intent o=new Intent(context,LoginActivity.class);
context.startActivity(o);//重新启动LoginActivity;
}
});
builder.show();
}
}
}
注意到在接收器接受广播后,新建了一个对话框,提出警告并设置确定按钮来实现强制下线,即先销毁当前所有活动,重新回到登录界面。
另外,之前都是在onCreate和onDestroy中实现广播接收器的注册和销毁,这里是重写onResume和onPause函数,在这两个函数中实现注册和销毁的,因为在这我们只需要保证只有处在栈顶的活动才能收到这条强制下线广播,非栈顶的活动不应爱也没必要去接受广播,当一个活动失去栈顶位置时就会自动取消广播接收器的注册。
最后,要将LoginActivity设置为程序的主界面,修改AndroidMainifest文件
<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=".BaseActivity"
android:label="@string/title_activity_base"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".LoginActivity"
android:label="@string/title_activity_login">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
最后的成果展示: