1. ContentObserver
内容观察器ContentObserver给目标内容注册一个观察器,目标内容的数据一旦发生变化,观察器规定好的动作马山触发,从而执行开发者预先定义的代码。
代码示例(利用ContentObserver监听短信)
public class MonitorSmsActivity extends AppCompatActivity {
private SmsGetObserver mObserver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_monitor_sms);
//给指定uri注册内容观察器,一旦发生数据变化,就触发观察器的onChange方法
Uri uri = Uri.parse("content://sms");
// notifyForDescendents:
// false :表示精确匹配,即只匹配该Uri,true :表示可以同时匹配其派生的Uri
// 假设UriMatcher 里注册的Uri共有一下类型:
// 1.content://AUTHORITIES/table
// 2.content://AUTHORITIES/table/#
// 3.content://AUTHORITIES/table/subtable
// 假设我们当前需要观察的Uri为content://AUTHORITIES/student:
// 如果发生数据变化的 Uri 为 3。
// 当notifyForDescendents为false,那么该ContentObserver会监听不到,但是当notifyForDescendents 为ture,能捕捉该Uri的数据库变化。
mObserver = new SmsGetObserver(this);
getContentResolver().registerContentObserver(uri, true, mObserver);
}
@Override
protected void onDestroy() {
super.onDestroy();
getContentResolver().unregisterContentObserver(mObserver);
}
private static class SmsGetObserver extends ContentObserver{
private final Context mContext;
public SmsGetObserver(Context context) {
super(new Handler(Looper.getMainLooper()));
this.mContext = context;
}
@SuppressLint("Range")
@Override
public void onChange(boolean selfChange, @Nullable Uri uri) {
super.onChange(selfChange,uri);
// onChange会多次调用,收到一条短信会调用两次onChange
// mUri===content://sms/raw/20
// mUri===content://sms/inbox/20
// 安卓7.0以上系统,点击标记为已读,也会调用一次
// mUri===content://sms
// 收到一条短信都是uri后面都会有确定的一个数字,对应数据库的_id,比如上面的20
if (uri == null) {
return;
}
if (uri.toString().contains("content://sms/raw") ||
uri.toString().equals("content://sms")) {
return;
}
Cursor cursor = mContext.getContentResolver().query(uri, new String[]{"address", "body", "date"}, null, null, "date DESC");
if(cursor.moveToNext()){
String sender = cursor.getString(cursor.getColumnIndex("address"));
String body = cursor.getString(cursor.getColumnIndex("body"));
Log.d("lzk", "onChange: "+sender+" "+body);
}
cursor.close();
}
}
}
2. 使用图片发送彩信
public class SendMmsActivity extends AppCompatActivity implements View.OnClickListener {
private ImageView iv_appendix;
private ActivityResultLauncher<Intent> mResultLauncher;
private EditText et_phone;
private EditText et_title;
private EditText et_message;
private Uri picUri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_send_mms);
iv_appendix = findViewById(R.id.iv_appendix);
et_phone = findViewById(R.id.et_phone);
et_title = findViewById(R.id.et_title);
et_message = findViewById(R.id.et_message);
iv_appendix.setOnClickListener(this);
findViewById(R.id.btn_send_mms).setOnClickListener(this);
mResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
if(result.getResultCode()==RESULT_OK){
Intent intent = result.getData();
if(intent!=null){
//获取选择的图片
picUri= intent.getData();
if(picUri!=null){
iv_appendix.setImageURI(picUri);
Log.d("lzk", "onActivityResult: "+picUri);
}
}
}
}
});
}
@Override
public void onClick(View view) {
if(view.getId() == R.id.iv_appendix){
//跳转到系统相册,选择图片,并返回
Intent intent=new Intent(Intent.ACTION_GET_CONTENT);
//设置内容为图片
intent.setType("image/*");
mResultLauncher.launch(intent);
}else if(view.getId() == R.id.btn_send_mms){
sendMms(et_phone.getText().toString(),
et_title.getText().toString(),
et_message.getText().toString());
}
}
private void sendMms(String phone, String title, String message) {
Intent intent=new Intent(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// 彩信发送的目标号码
intent.putExtra("address", phone);
// 彩信的标题
intent.putExtra("subject", title);
// 彩信的内容
intent.putExtra("sms_body", message);
// 彩信的图片附件
intent.putExtra(Intent.EXTRA_STREAM, picUri);
// 彩信的附件为图片
intent.setType("image/*");
startActivity(intent);
ToastUtil.show(this,"请在弹窗中选择短信或者信息应用");
}
}
3.FileProvider
public class ProviderMmsActivity extends AppCompatActivity {
private static final String[] PERMISSIONS = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE
};
private EditText et_phone;
private EditText et_title;
private EditText et_message;
private static final int PERMISSION_REQUEST_CODE = 1;
private List<ImageInfo> mImageList = new ArrayList<>();
private GridLayout gl_appendix;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
et_phone = findViewById(R.id.et_phone);
et_title = findViewById(R.id.et_title);
et_message = findViewById(R.id.et_message);
setContentView(R.layout.activity_provider_mms);
gl_appendix = findViewById(R.id.gl_appendix);
//手动让MediaStore扫描入库
//手动让MediaStore扫描入库
MediaScannerConnection.scanFile(this,
new String[]{Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString()},
null, null);
if(PermissionUtil.checkPermission(this,PERMISSIONS,PERMISSION_REQUEST_CODE)){
//加载图片列表
loadImageList();
showImageGrid();
}
}
private void sendMms(String phone, String title, String message,String path) {
Uri uri=Uri.parse(path);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// 通过FileProvider获得文件的Uri访问方式
uri = FileProvider.getUriForFile(this, getString(R.string.file_provider), new File(path));
Log.d("ning", String.format("new uri:%s", uri.toString()));
}
Intent intent=new Intent(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// 彩信发送的目标号码
intent.putExtra("address", phone);
// 彩信的标题
intent.putExtra("subject", title);
// 彩信的内容
intent.putExtra("sms_body", message);
// 彩信的图片附件
intent.putExtra(Intent.EXTRA_STREAM,uri);
// 彩信的附件为图片
intent.setType("image/*");
startActivity(intent);
ToastUtil.show(this,"请在弹窗中选择短信或者信息应用");
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == PERMISSION_REQUEST_CODE&&
PermissionUtil.checkGrant(grantResults)){
loadImageList();
showImageGrid();
}
}
private void showImageGrid() {
gl_appendix.removeAllViews();
for (ImageInfo imageInfo : mImageList) {
ImageView imageView = new ImageView(this);
Bitmap bitmap = BitmapFactory.decodeFile(imageInfo.path);
imageView.setImageBitmap(bitmap);
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
int px = Utils.dip2px(this, 110);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(px, px);
imageView.setLayoutParams(params);
int padding=Utils.dip2px(this, 5);
imageView.setPadding(padding,padding,padding,padding);
imageView.setOnClickListener(v -> {
sendMms(et_phone.getText().toString(),
et_title.getText().toString(),
et_message.getText().toString(),
imageInfo.path);
});
gl_appendix.addView(imageView);
}
}
@SuppressLint("Range")
//加载图片列表
private void loadImageList() {
String[] columns = new String[]{
MediaStore.Images.Media._ID,
MediaStore.Images.Media.TITLE,
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.SIZE,
};
//300kb以内的图片
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
columns,
"_size<307200",
null,
"_size DESC"
);
int count = 0;
if (cursor != null) {
while (cursor.moveToNext() && count < 6) {
ImageInfo image = new ImageInfo();
image.id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID));
image.name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.TITLE));
image.path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
image.size = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.SIZE));
if(FileUtil.checkFileUri(this,image.path)){
count++;
mImageList.add(image);
Log.d("lzk", image.toString());
}
}
}
}
}
4. APK文件应用安装
public class ProviderApkActivity extends AppCompatActivity implements View.OnClickListener {
private static final String[] PERMISSIONS = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE
};
private static final int PERMISSION_REQUEST_CODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_provider_apk);
findViewById(R.id.btn_install).setOnClickListener(this);
}
@Override
public void onClick(View view) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
checkAndInstall();
}else{
if(PermissionUtil.checkPermission(this, PERMISSIONS, PERMISSION_REQUEST_CODE)){
installApk();
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode==PERMISSION_REQUEST_CODE&&PermissionUtil.checkGrant(grantResults)){
installApk();
}
}
@RequiresApi(api=Build.VERSION_CODES.R)
private void checkAndInstall() {
if(!Environment.isExternalStorageManager()){
Intent intent=new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.fromParts("package",getPackageName(),null));
startActivity(intent);
}else{
installApk();
}
}
private void installApk() {
String apkPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + "/chapter06-release.apk";
PackageManager pm = getPackageManager();
PackageInfo pi = pm.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES);
if(pi==null){
ToastUtil.show(this,"安装文件已经损坏");
return;
}
Uri uri = Uri.parse(apkPath);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// 通过FileProvider获得文件的Uri访问方式
uri = FileProvider.getUriForFile(this, getString(R.string.file_provider), new File(apkPath));
Log.d("ning", String.format("new uri:%s", uri.toString()));
}
Intent intent=new Intent(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
startActivity(intent);
}
}