为App存取内部数据提供外部接口,让不同应用共享数据。
①在配置里AndroidManifest.xml
android:name=".UserInfoProvider"
android:authorities="com.example.chapter07_server.provider.UserInfoProvider"
android:enabled="true"
android:exported="true"/>
危险权限表:
运行时动态申请权限Lazy模式:
①检查是否开启指定权限:
public class PermissionUtil {
//检查权限,返回true则已完全启用权限,返回false表示未完全启用权限
public static boolean checkPermission(Activity act,String[] permissions,int requestCode)
{
int check = PackageManager.PERMISSION_GRANTED;
//逐一将各个权限取出判断
for(String permission: permissions){
ContextCompat.checkSelfPermission(act,permission);
check=ContextCompat.checkSelfPermission(act,permission);
if(check!=PackageManager.PERMISSION_GRANTED){break;}
}
//②若有未开启的权限,则请求系统弹窗
if (check!=PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(act,permissions,requestCode);
return false;
}
return true; } }
③判断用户的权限选择结果:
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//判断用户是否授权
switch (requestCode){
case REQUEST_CODE_CONTACTS:
if(PermissionUtil.checkGRant(grantResults)){
Log.d("ning","通讯录权限获取成功");
}else
{
Log.d("ning","获取通讯录读写权限失败");
}
break;
case REQUEST_CODE_SMS:
if (PermissionUtil.checkGRant(grantResults)){
Log.d("ning","收发短信权限获取成功");
}else
{
Log.d("ning","收发短信权限获取失败");
jumpToSetting();
}
break; } }
//跳转带应用设置界面
private void jumpToSetting(){
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package",getPackageName(),null));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
运行时动态申请权限Hunger模式:直接在onCreate中申请所有权限。
ContentResovler基本用法
添加联系人:
public void onClick(View view) {
int id = view.getId();
if (id == R.id.add) {//创建一个联系人对象
Contact contact = new Contact();
contact.name= et_name.getText().toString().trim();
contact.email=et_email.getText().toString().trim();
contact.phone=et_phone.getText().toString().trim();
//方法一,使用ContentResolver多次写入.
addContacts(getContentResolver(),contact);
} else if (id == R.id.find) {
}
}
//往手机通讯录里添加一个联系人
private void addContacts(ContentResolver resolver, Contact contact) {
ContentValues values = new ContentValues();
Uri uri= resolver.insert(ContactsContract.RawContacts.CONTENT_URI,values);
long rawContactId = ContentUris.parseId(uri);
ContentValues name = new ContentValues();
//关联联系人编号
name.put(Contacts.Data.RAW_CONTACT_ID,rawContactId);
//"姓名"数据类型
name.put(Contacts.Data.MIMETYPE,CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
//联系人的姓名
name.put(Contacts.Data.DATA2,contact.name);
//往提供器添加联系人姓名
resolver.insert(ContactsContract.Data.CONTENT_URI,name);
ContentValues phone = new ContentValues();
//关联联系人编号
phone.put(Contacts.Data.RAW_CONTACT_ID,rawContactId);
//"电话号码"数据类型
phone.put(Contacts.Data.MIMETYPE,CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
//联系人的电话号码
phone.put(Contacts.Data.DATA2,contact.phone);
phone.put(Contacts.Data.DATA2,CommonDataKinds.phone.TYPE_MOBILE);
//往提供器添加联系人姓名电话号码
resolver.insert(ContactsContract.Data.CONTENT_URI,phone);
ContentValues email = new ContentValues();
//关联联系人编号
email.put(Contacts.Data.RAW_CONTACT_ID,rawContactId);
//"邮箱"数据类型
email.put(Contacts.Data.MIMETYPE,CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
//联系人的邮箱
email.put(Contacts.Data.DATA2,contact.email);
//往提供器添加联系人邮箱
resolver.insert(ContactsContract.Data.CONTENT_URI,email);
}
批量处理添加联系人:
private void addFullContacts(ContentResolver resolver , Contact contact){
//构建一个插入联系人主记录的内容操作器
ContentProviderOperation op_main=ContentProviderOperation
.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME,null)
.build();
//构建对于姓名的操作
ContentProviderOperation op_name= ContentProviderOperation
.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValueBackReference(Contacts.Data.RAW_CONTACT_ID,0)
.withValue(Contacts.Data.MIMETYPE,CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(Contacts.Data.DATA2,contact.name)
.build();
//构建对于电话号码操作
ContentProviderOperation phone= ContentProviderOperation
.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValueBackReference(Contacts.Data.RAW_CONTACT_ID,0)
.withValue(Contacts.Data.MIMETYPE,CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(Contacts.Data.DATA2,contact.phone)
.build();
//构建对于邮箱的操作
ContentProviderOperation email= ContentProviderOperation
.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValueBackReference(Contacts.Data.RAW_CONTACT_ID,0)
.withValue(Contacts.Data.MIMETYPE,CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(Contacts.Data.DATA2,contact.email)
.build();
//构建列表将三个操作放入
ArrayList
operations.add(op_main);
operations.add(op_name);
operations.add(phone);
operations.add(email);
//批处理提交四个操作
try {
resolver.applyBatch(ContactsContract.AUTHORITY,operations);
} catch (OperationApplicationException e) {
throw new RuntimeException(e);
} catch (RemoteException e) {
throw new RuntimeException(e); } }
批量查询联系人:
//查询通讯录信息
@SuppressLint("Range")
private void readPhoneContacts(ContentResolver resolver ) {
Cursor cursor= resolver.query(ContactsContract.RawContacts.CONTENT_URI,new String[]{ContactsContract.RawContacts._ID},null,null,null,null);
while (cursor.moveToNext()){
int rawContactId = cursor.getInt(0);
Uri uri = Uri.parse("content://com.android.contacts/contacts"+rawContactId+"/data");
Cursor dataCursor=resolver.query(uri,new String[]{Contacts.Data.MIMETYPE,Contacts.Data.DATA1,Contacts.Data.DATA2},null,null,null);
Contact contact = new Contact();
while (dataCursor.moveToNext()){
String data1= dataCursor.getString(dataCursor.getColumnIndex(Contacts.Data.DATA1));
String mimeType=dataCursor.getString(dataCursor.getColumnIndex(Contacts.Data.MIMETYPE));
switch (mimeType){
case CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE:
contact.name=data1; break;
case CommonDataKinds.Email.CONTENT_ITEM_TYPE:
contact.Email=data1; break;
case CommonDataKinds.Phone.CONTENT_ITEM_TYPE:
contact.Phone=data1; break;
}
}
dataCursor.close();
}
cursor.close(); }
ContentObserver监听短信:
public class MonitorSmsActivity extends AppCompatActivity {
private SmsGetObserver mObserver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_monitor_sms);
//指定一个注册内容观察器,一单数据发生变化,就触发OnChange方法
Uri uri = Uri.parse("content://sms");
mObserver = new SmsGetObserver(this);
getContentResolver().registerContentObserver(uri,true,mObserver);
}
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; }
public void onChange(boolean selfChange, @Nullable Uri uri){
super.onChange(selfChange,uri);
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()){
//短信的发送号码
@SuppressLint("Range") String sender=cursor.getString(cursor.getColumnIndex("address"));
@SuppressLint("Range") String content=cursor.getString(cursor.getColumnIndex("body"));
Log.d("ning",String.format("sender:%s,content:%s",sender,content));
}
cursor.close();
}
} }
跳转选择图片:
①创建意图
Intent intent = new Intent(Intent.ACTION_GET_CONTENT );
②设置图片内容类型与跳转
Intent.setType(“image/*”);
mResultLauncher.launch(intent );
FileProvider:
继承于ContentProvider,对第三方应用暴露文件,并授权读写操作的权限。
①首先在AndroidManifest.xml文件中配置
android:authorities="@string/file_provider"
android:name="androidx.core.content.FileProvider"
android:grantUriPermissions="true">
android:resource="@xml/file_paths"/>
②其次在对应路径xml的文件夹中的file_paths中写配置文件。
③在代码中对应实现。
访问其他程序中的数据:
①使用现有的内容提供器来读取和操作相应的程序数据
②创建自己的内容提供器给我们程序的数据提供外部访问接口。
Notification通知栏的使用:
Notification notification = new Notification.Builder(this)
.setContentTitle("this is content title") //指定通知栏标题内容
.setContentText("this is content text")//指定通知栏正文内容
.setWhen(System.currentTimeMillis())//创建时间
.setSmallIcon(R.mipmap.ic_launcher) //通知栏小图标
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.build(); //通知栏大图标
manager.notify(NOTIFICATION_ID, notification); //通道号要提前创建
参数1:通道号 参数2:notification对象
PendingIntent延迟意图:
方法1:getActivity()
方法2:getBroadcast()
方法3:getService()
以上均为三个参数:参1:Context 。参2:一般不用传0。参数3:意图
参数4:确定行为FLAG_ONESHOT,FLAG_NO_CREATE,FLAG_CANCEL,FLAG_UPDATE_CURRENT通常可以填0.
点击后图标消失:
①在后部分调用.setAutoCancel( true )方法
②在onCreate中
NotificationManager manager = (Notif icat ionManager) getSystemService
(NOTIFICATION_ SERVICE) ; //注册管理器
manager . cancel((NOTIFICATION_ID); . //注册取消。
调用摄像头:
①创建File对象,用于存储。
②调用getExternalCacheDir( )方法获取目录。
③做版本适配
④用intent去启动摄像头。
注意调用都有权限注册。
播放音频:
一般使用MediaPlayer类实现,常用方法如下
调用播放视频:
一般使用VideoView类的方法。
共享参数-SharedPreferences:轻量级存储工具,Key-Value形式。
使用场景:
①简单且孤立的数据。
②文本形式数据。
③需要持久化存储的数据。
④App个性化配置信息,用户使用App行为信息。
⑤临时片段信息。
getSharedPreferences("config", Context.MODE_PRIVATE);
①然后先ALT+ENTER声明成变量,再Ctrl+ALT+F变全局
private SharedPreferences preferences;
preferences = getSharedPreferences("config", Context.MODE_PRIVATE);
//在下面监听
public void onClick(View view) {
String name = et_name.getText().toString();
String age = et_age.getText().toString();
String height = et_height.getText().toString();
String weight = et_weight.getText().toString();
//获取编辑器
SharedPreferences.Editor editor = preferences.edit();
editor.putString("name",name);
editor.putInt("age", Integer.parseInt(age));
editor.putFloat("height",Float.parseFloat(height));
editor.putFloat("weight",Float.parseFloat(weight));
editor.putBoolean("married",gr_married.isChecked());
editor.commit();
}
②重新再取数据:(通过键值对取)
String name= preferences.getString("name","");
SQLite
(1) 数据定义语言:
CREATE TABLE IF NOT EXISTS 表名(
字段A PRIMARY KEY
字段B,
字段C
)
支持类型:
NULL:表示空值。
INTEGER:表示整数,可以存储整数值。
REAL:表示浮点数,可以存储浮点数值。
TEXT:表示文本,可以存储字符串。
BLOB:表示二进制数据,可以存储任意二进制数据。
(2)删除表格:
DROP TABLE IF EXISTS user_info
(3)修改表格:
ALTER TABLE user_info ADD COLUMN phone VARCHAR;
(只支持增加字段,不支持修改,删除字段)添加多列就分多次。
(4)数据操作语言与其他数据库相似
1,管理类:
OpenDatabase():打开指定路径数据库。
isOpen():判断数据库是否已经打开。
Close():关闭数据库。
getVersion():获取版本。
SetVersion():设置数据库版本。
2,事务类:
beginTransaction:开始事物。
SetTransactionSuccessful:设置事务的成功标志.
endTransaction:结束事务。
创建数据库,删除数据库:
①首先在全局生命这个数据库名
private String mDataBaseName;
mDataBaseName = getFilesDir() + "/test.db";
public void onClick(View view) {
int id = view.getId();
if (id == R.id.btn_database_create) {
//打开数据库,参数1:数据库名地址+名字,参数2:打开模式,参数3:游标
SQLiteDatabase db = openOrCreateDatabase(mDataBaseName, Context.MODE_PRIVATE, null);
//输出化语言:
String desc = String.format("数据库%s创建%s", db.getPath(), (db != null) ? "成功" : "失败");
tv_database.setText(desc);}
else if (id == R.id.btn_database_delete) {
//删除数据库
boolean result = deleteDatabase(mDataBaseName);
String desc_fail = String.format("数据库%s删除%s", mDataBaseName, result ? "成功" : "失败");
tv_database.setText(desc_fail); } }
数据库所在路径:data->data->包名->file
页面与数据库交互:SQLiteOpenhelper
两个抽象方法: onCreate ( ), onUpgrade ( )
两个重要实例方法getReadableDatabase( ), getWritableDatabase( )。均可创建或打开数据库。
(6)事务管理:(一致性,原子性)
beginTransaction:开始事务.
setTransactionSuccessful:设置事务的成功标志.
endTransaction:结束事务.
若事务失败泽会回滚操作,保证原子性。
定义部分
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.Buffer;
public class FileUtil {
//把字符串保存到指定路径
public static void saveText(String path,String txt){
BufferedWriter os =null; //创建一个os
try {os = new BufferedWriter(new FileWriter(path)); //new一个对象,其中包含指定路径
os.write(txt); //将txt 写入该路径
}catch (Exception e){
e.printStackTrace();
}finally {
if (os!=null)
{
try {
os.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
//从指定路径读取内容字符串
public static String openText(String path){
BufferedReader is=null;
StringBuilder sb =new StringBuilder();
try {
is =new BufferedReader(new FileReader(path));
String line = null;
while ((line=is.readLine())!=null){
sb.append(line);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (is!=null)
{
try {
is.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
return sb.toString();
}
}
引用部分:
//外部存储的私有空间写入
String directory = null; //创建一个字符串
String fileName = System.currentTimeMillis()+".txt"; //创建一个当前时间的文件名
directory = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString(); //获取路径
path =directory + File.separatorChar+fileName; //构建完整路径
FileUtil.saveText(path,sb.toString()); //进行保存
ToastUtil.show(this,"保存成功");
//外部存储的私有空间读出
tv_txt.setText(FileUtil.openText(path))
外部存储公有空间:还要手机获取权限
directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString();
(7)存储卡上读写图片文件
BitmapFactory工具用于读取各种来源的图片:相关方法如下:
decodeResource:该方法可从资源文件中读取图片信息。
DecodeFile:该方法可将指定路径的图片读取到Bitmap对象。
DecodeStream:该方法从输入流中读取位图数据。
①定义触发保存事件
public void onClick(View view) {
int id = view.getId();
if (id == R.id.btn_save) {
String fileName = System.currentTimeMillis() + ".jpeg";
//获取当前App的私有下载目录
path = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + File.separatorChar + fileName;
//从指定资源文件获取位图像对象
Bitmap b1 = BitmapFactory.decodeResource(getResources(), R.drawable.ting1);
//将位图对象保存下来
FileUtil.saveImage(path, b1);
ToastUtil.show(this, "保存成功");
} else if (id == R.id.btn_read) {
Bitmap b2 = FileUtil.openImage(path);
iv_content.setImageBitmap(b2);
} }
②对应保存
//把位图数据保存到指定路径的图片文件
public static void saveImage(String path, Bitmap b1) {
FileOutputStream fos = null;
try{
fos=new FileOutputStream(path);
//把位图数据压缩到文件流中
b1.compress(Bitmap.CompressFormat.JPEG,100,fos);}
catch (Exception e){ e.printStackTrace(); }finally {
//关闭输入输出流
if(fos!=null){
try { fos.close(); }catch (IOException e){
e.printStackTrace(); } } } }
③读取相应位图
//从指定路径读取位图数据
public static Bitmap openImage(String path) {
Bitmap bitmap=null;
FileInputStream fis=null;
try {
fis =new FileInputStream(path);
bitmap = BitmapFactory.decodeStream(fis);
}catch (Exception e){
e.printStackTrace();
}finally {
if (fis!=null){
try {
fis.close();
}catch (IOException e)
{
e.printStackTrace();
} } } return bitmap; }
(8)Application生命周期
在App运行过程中有且仅有一个Application对象,贯穿生命周期。
public class MyApplication extends Application {
//在APP启动时调用
@Override
public void onCreate() {
super.onCreate();
Log.d("ning","onCreate");
}
//APP终止(在真实的产品不会回调)
@Override
public void onTerminate() {
super.onTerminate();
Log.d("ning","onTerminate");
}
//配置改变时调用
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
}
适用于会频繁读取的信息,如用户名,手机号。
不方便由意图传输的数据,例如位图对象,非字符串类型。
容易因频繁分配内存导致的内存泄漏的对象,Handler等。可以采用单例模式。
操作:
super.onCreate(); mApp = this; }//在oncreate里实例化,将this指针地址给mApp
④在外部再调用MyApplication.getInstance();
Room框架通过注解技术简化数据库操作。
①在build.gradle的dependencies中配置
implementation 'androidx.room:room-runtime:2.2.5'// 导入 Room 依赖库
annotationProcessor 'androidx.room:room-compiler:2.2.5'// 导入注解处理器 ( Java )
②编写一个表对应的实体类Bookinfo
package com.example.myapplication.enity;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity
public class Bookinfo {
@PrimaryKey(autoGenerate = true) //自动增长
private int id;
private String name;
private String author;
private String press;
private double price;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getPress() {
return press;
}
public void setPress(String press) {
this.press = press;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Bookinfo{" +
"id=" + id +
", name='" + name + '\'' +
", author='" + author + '\'' +
", press='" + press + '\'' +
", price=" + price +
'}';
}
}
③编写一个表对应的持久化类BookDao的接口。会自动生产类。
package com.example.myapplication.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import com.example.myapplication.enity.Bookinfo;
import java.util.List;
@Dao
public interface BookDao {
@Insert
void insert(Bookinfo... book);
@Delete
void delete(Bookinfo... book);
@Query("DELETE FROM Bookinfo")
void deleteAll();
@Update
int update(Bookinfo... book);
@Query("SELECT * FROM bookinfo ")
List
@Query("SELECT * FROM bookinfo WHERE name = :name ORDER BY id DESC limit 1")
Bookinfo queryByName(String name);
}
④创建一个抽象类:BookDatabase
@Database(entities = {Bookinfo.class},version = 1,exportSchema = true)
public abstract class BookDatabase extends RoomDatabase {
//获取该数据库中某张表的持久化对象
public abstract BookDao bookDao(); }
⑤在自定义的Application类中声明书籍数据库的唯一实例。
public class RoomWriteActivity extends AppCompatActivity implements View.OnClickListener {
private EditText shuming;
private EditText zuozhe;
private EditText chubanshe;
private EditText jiage;
private BookDatabase bookDB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_room_write);
shuming = findViewById(R.id.shuming);
zuozhe = findViewById(R.id.zuozhe);
chubanshe = findViewById(R.id.chubanshe);
jiage = findViewById(R.id.jiage);
findViewById(R.id.btn_chaxun).setOnClickListener(this);
findViewById(R.id.btn_shanchu).setOnClickListener(this);
findViewById(R.id.btn_tianjia).setOnClickListener(this);
findViewById(R.id.btn_xiugai).setOnClickListener(this);
//从App实例中获取唯一的书籍持续化对象
bookDB = MyApplication.getInstance().getBookDB();
}
@Override
public void onClick(View view) {
String name=shuming.getText().toString();
String author=zuozhe.getText().toString();
String press=chubanshe.getText().toString();
String price=jiage.getText().toString();
int id = view.getId();
if (id == R.id.btn_tianjia) {
Bookinfo b1 = new Bookinfo();
b1.setName(name);
b1.setAuthor(author);
b1.setPress(press);
b1.setPrice(Double.parseDouble(price));
bookDao.insert(b1);
} else if (id == R.id.btn_xiugai) {
List
for(Bookinfo b:list){
Log.d("ning",b.toString());
}
} else if (id == R.id.btn_shanchu) {
} else if (id == R.id.btn_chaxun) {
}
}
}