写在最后
很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从哪里入手去学习,对此我整理了一些资料
如果你熟练掌握以下列出的知识点,相信将会大大增加你通过前两轮技术面试的几率!这些内容都供大家参考,互相学习。
①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包,最后觉得有帮助、有需要的朋友可以点个赞
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
//图片文件
val file = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
//还有以下那么多环境, 根据存储文件类型选择不同环境
var DIRECTORY_ALARMS = “Alarms”
var DIRECTORY_AUDIOBOOKS = “Audiobooks”
var DIRECTORY_DCIM = “DCIM”
var DIRECTORY_DOCUMENTS = “Documents”
var DIRECTORY_DOWNLOADS = “Download”
var DIRECTORY_MOVIES = “Movies”
var DIRECTORY_MUSIC = “Music”
var DIRECTORY_NOTIFICATIONS = “Notifications”
var DIRECTORY_PICTURES = “Pictures”
var DIRECTORY_PODCASTS = “Podcasts”
var DIRECTORY_RINGTONES = “Ringtones”
var DIRECTORY_SCREENSHOTS = “Screenshots”
java:
//图片文件
File file = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
//还有以下那么多环境, 根据存储文件类型选择不同环境
public static String DIRECTORY_ALARMS = “Alarms”;
public static String DIRECTORY_AUDIOBOOKS = “Audiobooks”;
public static String DIRECTORY_DCIM = “DCIM”;
public static String DIRECTORY_DOCUMENTS = “Documents”;
public static String DIRECTORY_DOWNLOADS = “Download”;
public static String DIRECTORY_MOVIES = “Movies”;
public static String DIRECTORY_MUSIC = “Music”;
public static String DIRECTORY_NOTIFICATIONS = “Notifications”;
public static String DIRECTORY_PICTURES = “Pictures”;
public static String DIRECTORY_PODCASTS = “Podcasts”;
public static String DIRECTORY_RINGTONES = “Ringtones”;
public static String DIRECTORY_SCREENSHOTS = “Screenshots”;
数据和标识符变更
说明: 从 Android Q 开始,应用必须具有 READ_PRIVILEGED_PHONE_STATE
特许权限才能访问设备的不可重置标识符(包含 IMEI 和序列号, 并且这个权限只有系统app才可以使用, 也就是说在Android Q上已经不能获取DeviceId了
替代方法:
- Android Id:
kotlin:
val androidId = Settings.Secure.getString(
context.contentResolver,
Settings.Secure.ANDROID_ID
)
java:
String androidId = Settings.Secure.getString(
context.contentResolver,
Settings.Secure.ANDROID_ID
);
- 但是在实际应用中发现, 有Android Id获取失败的情况, 所以就完善了上面的方法
kotlin:
var deviceId = Settings.Secure.getString(
getAppContext().contentResolver,
Settings.Secure.ANDROID_ID
)
if (androidId.isNullOrEmpty()) {
deviceId = getUniquePsuedoID()
}
fun getUniquePsuedoID(): String {
val devIDShort =
“35” + Build.BOARD.length % 10 + Build.BRAND.length % 10 + Build.CPU_ABI.length % 10 + Build.DEVICE.length % 10 + Build.MANUFACTURER.length % 10 + Build.MODEL.length % 10 + Build.PRODUCT.length % 10
// API >= 9 的设备才有 android.os.Build.SERIAL
// http://developer.android.com/reference/android/os/Build.html#SERIAL
// 如果用户更新了系统或 root 了他们的设备,该 API 将会产生重复记录
var serial: String?
try {
serial = android.os.Build::class.java.getField(“SERIAL”).get(null).toString()
return UUID(
devIDShort.hashCode().toLong(),
serial.hashCode().toLong()
).toString()
} catch (exception: Exception) {
serial = “serial”
}
// 最后,组合上述值并生成 UUID 作为唯一 ID
return UUID(devIDShort.hashCode().toLong(), serial!!.hashCode().toLong()).toString()
}
java:
String deviceId = Settings.Secure.getString(
context.contentResolver,
Settings.Secure.ANDROID_ID
);
if(TextUtils.isEmpty(deviceId)) {
deviceId = getUniquePsuedoID()
}
public String getUniquePsuedoID() {
String devIDShort =
“35” + Build.BOARD.length % 10 + Build.BRAND.length % 10 + Build.CPU_ABI.length % 10 + Build.DEVICE.length % 10 + Build.MANUFACTURER.length % 10 + Build.MODEL.length % 10 + Build.PRODUCT.length % 10;
// API >= 9 的设备才有 android.os.Build.SERIAL
// http://developer.android.com/reference/android/os/Build.html#SERIAL
// 如果用户更新了系统或 root 了他们的设备,该 API 将会产生重复记录
String serial;
try {
serial = android.os.Build::class.java.getField(“SERIAL”).get(null).toString()
return UUID(
devIDShort.hashCode().toLong(),
serial.hashCode().toLong()
).toString();
} catch (Exception e) {
serial = “serial”;
}
// 最后,组合上述值并生成 UUID 作为唯一 ID
return UUID((long)devIDShort.hashCode(), (long)serial.hashCode()).toString();
}
限制Activity后台启动
说明: 此项行为变更适用于在 Android Q 上运行的所有应用,甚至包括以 Android 9(API 级别 28)或更低版本为目标平台的应用。此外,即使您的应用以 Android 9 或更低版本为目标平台并且最初安装在运行 Android 9 或更低版本的设备上,该行为变更仍会在设备升级到 Android Q 后生效。
解决方法:
发送全屏通知会自动启动Activity
kotlin:
fun sendNotification(
title: String?,
body: String?,
data: PushMessageNode?,
bitmap: Bitmap?
) {
val intent = Intent(this, PushJumpActivity::class.java)
intent.putExtra(WhatConstants.Intent.FIRE_PUSH_MESSAGE, data)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
val pendingIntent = PendingIntent.getActivity(
this, requestCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
val notificationManager =
this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?
if (notificationManager != null) {
val notificationBuilder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val mNotificationChannel =
NotificationChannel(
NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH
)
notificationManager.createNotificationChannel(mNotificationChannel)
}
notificationBuilder
.setSmallIcon(R.mipmap.logo)
.setLargeIcon(
bitmap ?: BitmapFactory.decodeResource(
context,
R.mipmap.logo
)
)
.setContentTitle(title)
.setContentText(body)
.setShowWhen(true)
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
//设置为全屏通知, 此时若App处于前台, 会为悬挂通知, 无论前台后台, 都会自动启动Acitivity
.setFullScreenIntent(pendingIntent, true)
.setContentIntent(pendingIntent)
notificationManager.notify(
requestCode /* ID of notification */,
notificationBuilder.build()
)
bitmap?.recycle()
}
}
java:
private void sendNotification(String title, String body, PushMessageNode data, Bitmap bitmap) {
Intent intent = new Intent(this, PushJumpActivity.class);
intent.putExtra(WhatConstants.Intent.INSTANCE.getFIRE_PUSH_MESSAGE(), data);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
int requestCode = (int) (Math.random() * 1000) + 1;
PendingIntent pendingIntent = PendingIntent.getActivity(this, requestCode /* Request code */, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationManager notificationManager = null;
notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder notificationBuilder;
if (notificationManager != null) {
if (Build.VERSION.SDK_INT >= 26) {
NotificationChannel mNotificationChannel = new NotificationChannel(“1”, “Channel1”, NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(mNotificationChannel);
notificationBuilder = new Notification.Builder(this, “1”);
} else {
notificationBuilder = new Notification.Builder(this);
}
notificationBuilder = notificationBuilder
.setSmallIcon(R.mipmap.logo)
.setLargeIcon(bitmap != null ? bitmap : BitmapFactory.decodeResource(context.getResources(), R.mipmap.logo))
.setContentTitle(title)
.setContentText(body)
.setShowWhen(true)
.setPriority(Notification.PRIORITY_HIGH)
.setAutoCancel(true)
.setSound(defaultSoundUri)
//设置为全屏通知, 此时若App处于前台, 会为悬挂通知, 无论前台后台, 都会自动启动Acitivity
.setFullScreenIntent(pendingIntent, true);
.setContentIntent(pendingIntent);
notificationManager.notify(requestCode /* ID of notification */, notificationBuilder.build());
if (bitmap != null)
bitmap.recycle();
}
}
获取剪贴板数据
说明: 只有默认输入法(IME)或者是目前处于焦点的应用, 才能访问到剪贴板数据.
这也就是说应用已经不能在后台监听剪贴板数据了, 不过我对目前处于焦点的应用这句话不太了解 . 另外在适配过程中, 遇到了一个问题, 在Acitivity onCreate直接获取剪贴板数据是不能成功获取的, 而在按钮点击的时候是可以的:
class SimpleActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//直接获取剪切板数据
getTextFromClip()
//剪贴板有数据也return “”
//点击按钮获取剪切板数据
view.setOnClickListener {
getClipboardData()
//返回剪贴板的正常数据
}
}
private fun getTextFromClip(): String {
val clipboardManager =
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?
if (null == clipboardManager || !clipboardManager.hasPrimaryClip()) {
return “”
}
val clipData = clipboardManager.primaryClip
if (null == clipData || clipData.itemCount < 1) {
return “”
}
val clipText = clipData.getItemAt(0)?.text ?: “”
return clipText.toString()
}
}
后面又对目前处于焦点的应用思考了一下, 应该就是视图加载到窗口上才能获取焦点, 后面经过适配, 在view.post()之后获取剪贴板数据,又参考了这篇文章[Android源码解析]view.post()到底干了啥, 了解到view.post()是在view dispatchAttachedToWindow后执行的, 然后写出方法如下:
kotlin:
/**
- 获取剪贴板的内容
*/
fun getClipBoardText(@Nullable activity: Activity?, f: (String) -> Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && activity != null) {
getTextFromClipFroAndroidQ(activity, f)
} else {
f.invoke(getTextFromClip())
}
}
/**
- AndroidQ 获取剪贴板的内容
*/
@TargetApi(Build.VERSION_CODES.Q)
private fun getTextFromClipFroAndroidQ(@NonNull activity: Activity, f: (String) -> Unit) {
val runnable = Runnable {
try {
val clipboardManager =
activity.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?
if (null == clipboardManager || !clipboardManager.hasPrimaryClip()) {
f.invoke(“”)
return@Runnable
}
val clipData = clipboardManager.primaryClip
最后
文章所有资料全部已经打包整理好,另外小编手头上整理了大量Android架构师全套学习资料,Android核心高级技术PDF文档+全套高级学习资料+视频+2021 BAT 大厂面试真题解析
资料展示:
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
中…(img-Ry2qIMGB-1715338985692)]
[外链图片转存中…(img-3ENcIYBP-1715338985693)]
[外链图片转存中…(img-ZmmTJXNk-1715338985693)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!