当前位置:网站首页>Activity交互问题,你确定都知道?
Activity交互问题,你确定都知道?
2022-07-29 05:21:00 【代码与思维】
对于AndroidDeveloper来说,activity就像初恋一样,启动要求低,响应快,准备得当的前提下能玩的花样也多,但关于activity交互方面的可能问题也有不少,本文将从常见Binder传递数据限制、多个Application对activity跳转的影响等方面进行逐步探讨。
Binder传递数据限制
数据传递限制
通过intent在Activity之间相互跳转时传递数据,已经是最常见的基本操作,但这种操作在特定情况下会引起崩溃,例如以下场景:
Intent intent = new Intent(MainActivity.this, NextPageActivity.class);
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
Bitmap bitmap = Bitmap.createScaledBitmap(icon, 1024, 1024, false); intent.putExtra("data",bitmap);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
在MainActivity中跳转NextPageActivity页面,并通过intent传递一个bitmap数据,执行上述代码后引起的报错如下:
可见报错原因:Intent传输数据过大,而Android系统对使用Binder进行数据传输的大小做了限制(相关限制定义在frameworks/native/libs/binder/processState.cpp类中,可能因为版本不同而代码有些许不同),通常情况下系统限制为1M,在Android 6和Android 7上单次传输数据大小限制为200KB,但是根据不同版本、不同厂商,这个值又会有不同的区别。
其实,考虑到Binder的设计初衷,有此异常抛出也能理解:其Binder本身就是为了进程间频繁且灵活的通信所设计的,并不是为了拷贝大数据而使用的,所以性能优先,数据大小自然有所限制。
解决措施
1、将对象转化成Json字符串
JVM 加载类时常会伴随额外的空间来保存类相关信息,将类中数据转化为 JSON 字符串可以减少数据大小。比如使用 Gson.toJson 方法。此方法几乎万能,当然,有时候将类转化成Json字符串后还是会超出Binder限制,说明这时候要传输的数据量较大,建议使用缓存等本地持久化方式、或全局观察者方式(EventBus、RxBus之类)来实现数据共享。
2、使用 transient 关键字修饰非必须字段,减少通过 Intent 传递的数据
transient只能修饰变量,而不能修饰方法、类、局部变量,而静态变量无论是否被transient修饰,都不能被序列化。可见,这种方式不适合所有场景,包括上述代码场景,例如下述传递一个bean类数据:
Intent intent = new Intent(MainActivity.this, NextPageActivity.class); intent.putExtra("data",new TestBean());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
static class TestBean implements Serializable {
private transient byte[] bean = new byte[1024*1024];
}
在该bean类中对byte[]进行 transient 关键词修饰,运行代码后会发现再无报错。
指定process造成多个Application
特定情况(需求)下,我们要对Activity指定别的进程名,如下:

由于Activity能在不同的进程中启动,且每一个进程都会默认创建一个Application,因此可能会使得Application的onCreate()多次调用。我们可在Application代码中打印对应进程日志:
public class MainApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Log.i(getClass().getName(),"process name : "+getProcessName(this, Process.myPid()));
}
public String getProcessName(Context cxt, int pid) {
ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return null;
}
for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName;
}
}
return null;
}
}
MainActivity代码逻辑较简单,仅是一个点击事件,点击后跳转至指定process为“andev.process”的NextPageActivity,这里就不贴出来了,运行代码后,可看到对应打印的进程名日志为:

不难看出,MainApplication的onCreate()被调用了两次,如果按照我们往常的习惯在onCreate中进行一些环境的初始化的话,则对应各种初始化方法会执行两次,会造成一些意想不到的报错和崩溃。
解决方法
1、在Application的onCreate()中进行初始化前进行进程判断,如果是当前主进程则进行对应操作,一般都是用此方法;
2、网上说的抽象出一个与 Application 生命周期同步的类,并根据不同的进程创建相应的 Application 实例。不太建议,一般情况下通过当前进程名判断即可。
后台启动Activity问题
从Android10(API29)开始,Android系统对从后台启动Activity做了一定限制,要用户同意“后台弹出界面”权限后,APP才能从后台服务或操作中弹出Activity,自从2019年5月份开始,小米开启了这项权限判断,从此各大厂商陆续添加此权限要求。其实此权限的设计初衷不难理解,为了避免当前前台用户的交互被打断,保证当前屏幕上展示的内容不受影响。想想看,风和日丽的一天,你吃着火锅唱着歌,玩着手里的农药,突然某个不知名APP在后台给你弹了个界面,你点了关闭重新回到农药,却发现只能看黑白电视了,还招来队友的问候,是不是顿时间就觉得火锅不香了。
其实这属于Android系统的优化,应尽可能的去适配厂商,给安卓生态圈带来更加用户体验,如果实在需要,可通过产品设计引导用户去开启对应权限,当然也有绕过此权限的方法,如下:
public void startWithNotify(Intent intent) {
String channelId = "xxxxxxx";
String nTag = "xxxxxxx";
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 2022712, intent, PendingIntent
.FLAG_UPDATE_CURRENT);
try {
pendingIntent.send();
notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null) {
return;
}
if (Build.VERSION.SDK_INT >= 26 && notificationManager.getNotificationChannel(channelId) == null) {
final NotificationChannel notificationChannel = new NotificationChannel(channelId, mContext.getString(R.string.app_name), NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setDescription(mContext.getString(R.string.app_name));
notificationChannel.setLockscreenVisibility(-1);
notificationChannel.enableLights(false);
notificationChannel.enableVibration(false);
notificationChannel.setShowBadge(false);
notificationChannel.setSound((Uri) null, (AudioAttributes) null);
notificationChannel.setBypassDnd(true);
notificationManager.createNotificationChannel(notificationChannel);
}
//不弹出来的空通知栏
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setFullScreenIntent(pendingIntent, true)
.setCustomHeadsUpContentView(new RemoteViews(mContext.getPackageName(), R.layout.layout_empty_notify));
if (Build.VERSION.SDK_INT >= 21) {
builder.setVisibility(Notification.VISIBILITY_PRIVATE);
}
notificationManager.notify(nTag, notificationId, builder.build());
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (notificationManager != null) {//需延迟取消。不然activity出不来
notificationManager.cancel(nTag, notificationId);
}
}
}, 1000);
} catch (Exception e) {
e.printStackTrace();
}
}
当然,此方法涉及到版本适配,不一定适配当前所有Room,此时可以采用另一套方案:
1、判断当前界面是否在前台,热后获取对应的ActivityManager;
2、利用系统的当前的task堆栈,遍历找到需要的task,将其调至强行切换到前台即可。
此方法相对耗时,有自己的弊端。但两种方式结合,能应对90%以上Room。而最好的方式,还是引导用户打开对应权限。
作者:左大星
链接:https://juejin.cn/post/7119465581232783397
边栏推荐
猜你喜欢

并发编程学习笔记 之 工具类Semaphore(信号量)

Huawei 2020 school recruitment written test programming questions read this article is enough (Part 2)

裸金属云FASS高性能弹性块存储解决方案

超简单集成HMS ML Kit 实现parental control

How to survive in the bear market of encryption market?

Refresh, swagger UI theme changes

量化开发必掌握的30个知识点【什么是Level-2数据】

“山东大学移动互联网开发技术教学网站建设”项目实训日志六

iSCSI vs iSER vs NVMe-TCP vs NVMe-RDMA

Android Studio 实现登录注册-源代码 (连接MySql数据库)
随机推荐
Realize the scheduled backup of MySQL database in Linux environment through simple script (mysqldump command backup)
Centos7 silently installs Oracle
通过简单的脚本在Linux环境实现Mysql数据库的定时备份(Mysqldump命令备份)
ssm整合
如何零代码制作深度学习的趣味app(适合新手)
The LAAS protocol of defi 2.0 is the key to revitalizing the development of defi track
Read and understand move2earn project - move
Performance comparison | FASS iSCSI vs nvme/tcp
datax安装
全闪分布式,如何深度性能POC?
day02 作业之文件权限
数组的基础使用--遍历循环数组求出数组最大值,最小值以及最大值下标,最小值下标
Gluster集群管理小分析
“山东大学移动互联网开发技术教学网站建设”项目实训日志二
并发编程学习笔记 之 原子操作类AtomicInteger详解
Thinkphp6 output QR code image format to solve the conflict with debug
C# 连接 SharepointOnline WebService
与张小姐的春夏秋冬(4)
初探fastJson的AutoType
与张小姐的春夏秋冬(2)