新年第一篇总结与分享~
离19年新年就剩一个月左右了,整个公司的项目也不像以往一样紧张、忙碌,周末同事和朋友聊得最多的就是抢票和年终奖了(然而我们公司并没有),为自己心疼一分钟。。。
总结一下18年吧,主要负责了4个商业项目,还参与了公司 Android 新框架的封装,整体来说算是稳步提高吧。
先说一下写这篇文章的背景吧,主要就是不太忙了,再加上客户要求适配 Android 9.0 (第一个客户主动提出来的),在看完9.0的适配之后,也对之前的进行了一些整理,废话不多说,请看干货!
动态权限适配是 Android 6.0 最先开始的,也是 Android 系统对开发者影响最大的改动之一。
权限组 | 权限名称 |
---|---|
CALENDAR | android.permission.READ_CALENDAR |
android.permission.WRITE_CALENDAR | |
CAMERA | android.permission.CAMERA |
CONTACTS | android.permission.READ_CONTACTS |
android.permission.WRITE_CONTACTS | |
android.permission.GET_ACCOUNTS | |
LOCATION | android.permission.ACCESS_FINE_LOCATION |
android.permission.ACCESS_COARSE_LOCATION | |
MICROPHONE | android.permission.RECORD_AUDIO |
PHONE | android.permission.READ_PHONE_STATE |
android.permission.CALL_PHONE | |
android.permission.READ_CALL_LOG | |
android.permission.ADD_VOICEMAIL | |
android.permission.WRITE_CALL_LOG | |
android.permission.USE_SIP | |
android.permission.PROCESS_OUTGOING_CALLS | |
SENSORS | android.permission.BODY_SENSORS |
SMS | android.permission.SEND_SMS |
android.permission.RECEIVE_SMS | |
android.permission.READ_SMS | |
android.permission.RECEIVE_WAP_PUSH | |
android.permission.RECEIVE_MMS | |
STORAGE | android.permission.READ_EXTERNAL_STORAGE |
android.permission.WRITE_EXTERNAL_STORAGE |
对应在清单文件中的展示如下:
在 targetSdkVersion 大于等于的 24 的 app 中,但是我们没有去适配 Android 7.0。那么在调用安装页面,或修改用户头像操作时,就会失败。那么就需要你去适配 Android 7.0或是将 targetSdkVersion 改为 24 以下(不推荐)。适配的方法这里就不细讲,大家可以看鸿洋大神的 Android 7.0 行为变更 通过FileProvider在应用间共享文件这篇文章。
Android 7.0 引入一项新的应用签名方案 APK Signature Scheme v2,它能提供更快的应用安装时间和更多针对未授权 APK 文件更改的保护。在默认情况下,Android Studio 2.2 和 Android Plugin for Gradle 2.2 会使用 APK Signature Scheme v2 和传统签名方案来签署您的应用。
说明:
// build.gradle里面加上这句话
defaultConfig {
useLibrary 'org.apache.http.legacy'
}
// MODE_WORLD_READABLE:Android 7.0以后不能使用这个获取,会闪退
// 应修改成MODE_PRIVATE
SharedPreferences read = getSharedPreferences(RELEASE_POOL_DATA, MODE_WORLD_READABLE);
ANSWER_PHONE_CALLS:允许您的应用通过编程方式接听呼入电话。要在您的应用中处理呼入电话,您可以使用 acceptRingingCall() 函数。
READ_PHONE_NUMBERS:权限允许您的应用读取设备中存储的电话号码。
Android 8.0中,为了更好的管制通知的提醒,不想一些不重要的通知打扰用户,新增了通知渠道,用户可以根据渠道来屏蔽一些不想要的通知。
代码示例如下:
/**
* Description: Android 8.0通知的兼容类
* Author: Jack Zhang
* create on: 2019/1/2 3:16 PM
*/
public class MyNotification
{
public static final String CHANNEL_ID_GL = "com.jz.gailun";
public static final String CHANNEL_NAME_GL = "盖伦";
public static final String CHANNEL_ID_AX = "com.jz.aixi";
public static final String CHANNEL_NAME_AX = "艾希";
public static final String CHANNEL_ID_LL = "com.jz.liulang";
public static final String CHANNEL_NAME_LL = "流浪";
public static void setONotifyChannel(NotificationManager manager, String channeId, String channelName)
{
setONotifyChannel(manager, null, channeId, channelName);
}
public static void setONotifyChannel(NotificationManager manager, NotificationCompat.Builder builder, String channeId, String channelName)
{
if (TextUtils.isEmpty(channeId) || TextUtils.isEmpty(channelName))
Logger.e("Android 8.0 Notification的channeId与channelName不能为空");
if (Build.VERSION.SDK_INT >= 26)
{
// 第三个参数设置通知的优先级别
NotificationChannel channel = new NotificationChannel(channeId, channelName, NotificationManager.IMPORTANCE_DEFAULT);
// 是否可以绕过请勿打扰模式
channel.canBypassDnd();
// 是否可以显示icon角标
channel.canShowBadge();
// 是否显示通知闪灯
channel.enableLights(true);
// 收到消息时震动提示
channel.enableVibration(true);
// 设置绕过免打扰
channel.setBypassDnd(true);
channel.setLockscreenVisibility(NotificationCompat.VISIBILITY_SECRET);
// 设置闪光灯颜色
channel.setLightColor(Color.RED);
// 获取设置铃声设置
channel.getAudioAttributes();
// 设置震动模式
channel.setVibrationPattern(new long[]{100, 200, 100});
// 是否会闪光
channel.shouldShowLights();
if (manager != null)
manager.createNotificationChannel(channel);
if (builder != null)
builder.setChannelId(channeId);//这个id参数要与上面channel构建的第一个参数对应
}
}
public static Notification getNotification(Context context, String channelId)
{
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, channelId);
Notification notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.mipmap.ic_logo)
.setPriority(NotificationManager.IMPORTANCE_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
return notification;
}
}
/**
* Description: 通知管理类
* Author: Jack Zhang
* create on: 2019/1/2 3:23 PM
*/
public class NotifyManager
{
private volatile static NotifyManager INSTANCE;
private NotifyManager(Context context)
{
initNotifyManager(context);
}
public static NotifyManager getInstance(Context context)
{
if (INSTANCE == null)
synchronized (NotifyManager.class)
{
if (INSTANCE == null)
INSTANCE = new NotifyManager(context);
}
return INSTANCE;
}
private NotificationManager manager;
// NotificationManagerCompat
private NotificationCompat.Builder builder;
// 初始化通知栏配置
private void initNotifyManager(Context context)
{
context = context.getApplicationContext();
manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// // 如果存在则清除上一个消息
// manager.cancel(lastNotificationId);
builder = new NotificationCompat.Builder(context, MyNotification.CHANNEL_ID_GL);
MyNotification.setONotifyChannel(manager, builder, MyNotification.CHANNEL_ID_GL, MyNotification.CHANNEL_NAME_GL);
// 设置标题
builder.setContentTitle("Title");
// 状态栏的动画提醒语句
builder.setTicker("Ticker");
// 什么时候提醒
builder.setWhen(System.currentTimeMillis());
// 设置通知栏的优先级
builder.setPriority(Notification.PRIORITY_DEFAULT);
// 设置点击可消失
builder.setAutoCancel(true);
// 设置是否震动等
builder.setDefaults(Notification.DEFAULT_VIBRATE);
// 设置icon
builder.setSmallIcon(R.mipmap.ic_logo);
// 设置点击意图
Intent intent = new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
}
/**
* 显示盖伦通知栏
*
* @author Jack Zhang
* create at 2019/1/2 3:28 PM
*/
public void showGLNotify(Context context)
{
// 设置内容
builder.setContentText("盖伦");
manager.notify(1, builder.build());
}
/**
* 显示艾希通知栏
*
* @author Jack Zhang
* create at 2019/1/2 3:28 PM
*/
public void showAXNotify(Context context)
{
builder.setContentText("艾希");
manager.notify(2, builder.build());
}
/**
* 显示流浪通知栏
*
* @author Jack Zhang
* create at 2019/1/2 3:28 PM
*/
public void showLLNotify(Context context)
{
builder.setContentText("流浪");
manager.notify(3, builder.build());
}
}
问题原因:项目使用了ActiveAndroid,在 8.0 或 8.1 系统上使用 26 或以上的版本的 SDK 时,调用 ContentResolver 的 notifyChange 方法通知数据更新,或者调用 ContentResolver 的 registerContentObserver 方法监听数据变化时,会出现上述异常。
解决方案:
问题原因:Android 8.0 引入了新的广播接收器限制,因此您应该移除所有为隐式广播 Intent 注册的广播接收器。
解决方案:使用动态广播代替静态广播。
Caused by: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
问题原因:Android 8.0 非全屏透明页面不允许设置方向(后面8.1系统谷歌就去掉了这个限制,可能是真的没必要)
解决方案:
CLEARTEXT communication to life.115.com not permitted by network security polic
问题原因: Android P 限制了明文流量的网络请求,非加密的流量请求都会被系统禁止掉
解决方案:
java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed
if (Build.VERSION.SDK_INT >= 26)
canvas.clipPath(mPath);
else
canvas.clipPath(mPath, Region.Op.REPLACE);