节选自:Android Jetpack
Jetpack 是一套库、工具和指南,可帮助开发者更轻松地编写优质应用。这些组件可帮助您遵循最佳做法、让您摆脱编写样板代码的工作并简化复杂任务,以便您将精力集中放在所需的代码上。
Jetpack 包含与平台 API 解除捆绑的 androidx.* 软件包库。这意味着,它可以提供向后兼容性,且比 Android 平台的更新频率更高,以此确保您始终可以获取最新且最好的 Jetpack 组件版本。
节选自:Android Jetpack
Jetpack 是一套库、工具和指南,可帮助开发者更轻松地编写优质应用。这些组件可帮助您遵循最佳做法、让您摆脱编写样板代码的工作并简化复杂任务,以便您将精力集中放在所需的代码上。
Jetpack 包含与平台 API 解除捆绑的 androidx.* 软件包库。这意味着,它可以提供向后兼容性,且比 Android 平台的更新频率更高,以此确保您始终可以获取最新且最好的 Jetpack 组件版本。
节选自:我为Dexposed续一秒——论ART上运行时 Method AOP实现
两年前阿里开源了Dexposed 项目,它能够在Dalvik上无侵入地实现运行时方法拦截,正如其介绍「enable ‘god’ mode for single android application」所言,能在非root情况下掌控自己进程空间内的任意Java方法调用,给我们带来了很大的想象空间。比如能实现运行时AOP,在线热修复,做性能分析工具(拦截线程、IO等资源的创建和销毁)等等。然而,随着ART取代Dalvik成为Android的运行时,一切都似乎戛然而止。
今天,我在ART上重新实现了Dexposed,在它能支持的平台(Android 5.0 ~ 7.1 Thumb2/ARM64)上,有着与Dexposed完全相同的能力和API;项目地址在这里 epic,感兴趣的可以先试用下:) 然后我们聊一聊ART上运行时Method AOP的故事。
有史以来,Android源码编译环境的搭建始终是一件麻烦事儿。网上有数不清的文章介绍如何编译Android源代码,但是他们要么方法复杂、步骤太多;要么自称解决了一些编译问题(需要修改头文件,系统配置等),让人对其可信度产生质疑。有的童鞋硬着头皮照做了,但是由于伟大的GFW,大部分都死在了第一步——repo脚本都下载不下来,就算下载过了过不了gerrit那一关。另外,就算你具备科学上网的能力,下载时间又成为了拦路虎;普通的VPN通常需要下载七八个小时,简直就是痛不欲生。久而久之,很多人对下载编译Android源码望而却步。
今天,我给大家提供一个极其简单、稳定的方案,来解决Android源码的下载编译问题。
首先,下载问题可以通过镜像解决;清华镜像 和 科大镜像 都是非常不错的选择,正常情况下一到两个小时即可下载完一个Android源码分支。
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
// 申请权限
private final int REQUEST_PERMISSION = 666;
private boolean gotoAppDetail = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 判断系统版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// 申请权限
requestPermission();
} else {
// 初始化
onInit();
}
}
/**
* 申请权限
*/
private void requestPermission() {
// 检查权限
String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_EXTERNAL_STORAGE};
List<String> checkPermissions = new ArrayList<>();
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
checkPermissions.add(permission);
}
}
if (checkPermissions.size() > 0) {
// 申请权限
ActivityCompat.requestPermissions(this, checkPermissions.toArray(new String[0]), REQUEST_PERMISSION);
} else {
// 初始化
onInit();
}
}
/**
* 申请权限回调
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == REQUEST_PERMISSION) {
int flag = 0;
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
flag = 1;
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])) {
flag = 2;
}
break;
}
}
if (flag == 0) {
// 初始化
onInit();
} else if (flag == 1) {
new AlertDialog.Builder(this).setTitle("提示").setMessage("请允许所有权限!").setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 申请权限
requestPermission();
}
}).show();
} else {
new AlertDialog.Builder(this).setTitle("提示").setMessage("请手动允许所有权限!").setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
gotoAppDetail = true;
// 跳转到应用详情界面
gotoAppDetailIntent(MainActivity.this);
}
}).show();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
/**
* 跳转到应用详情界面
*/
private void gotoAppDetailIntent(Activity activity) {
Intent intent = new Intent();
intent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + activity.getPackageName()));
activity.startActivity(intent);
}
@Override
protected void onRestart() {
super.onRestart();
// 跳转到应用详情界面返回
if (gotoAppDetail) {
gotoAppDetail = false;
// 申请权限
requestPermission();
}
}
/**
* 初始化
*/
private void onInit() {
}
}
参考链接:
1.唤醒PN532:
PC->PN532: 55 55 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff 03 fd d4 14 01 17 00 PN532->PC: 00 00 FF 00 FF 00 00 00 FF 02 FE D5 15 16 00
2.获取卡UID,也就是卡的唯一ID号,全球就这一个(理论上)
PC->PN532: 00 00 FF 04 FC D4 4A 02 00 E0 00
import android.content.Context;
import android.util.AttributeSet;
import android.widget.VideoView;
public class MyVideoView extends VideoView {
public MyVideoView(Context context) {
super(context);
}
public MyVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = getDefaultSize(0, widthMeasureSpec);
int height = getDefaultSize(0, heightMeasureSpec);
setMeasuredDimension(width, height);
}
}