Android Review---2

Android Review—2

零蚀

  • 数据持久化

    • 文件存储
      Context类提供了一个openFileOutput(“filename”, Mode)方法,可以将文件储存到指定的文件中,这个参数接收两个参数,

      • 第一个参数:文件名,在文件创建的时候用到的就是这个文件名,文件默认的存储的位置/data/data/< packagename >/files/ 目录下(没有时自建)。
      • 第二个参数:操作模式,主要有两种模式,MODE_PERIVATE(写入内容覆盖原内容),MODE_APPEND(内容追加)。其他两种MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE由于安全问题,已经在4.2被废除
    
    //创建文件or获取文件路径,名称;输出流;缓冲写入
     try {
            FileOutputStream outputStream = openFileOutput("fileName", MODE_PRIVATE);
            writer = new BufferedWriter(new OutputStreamWriter(outputStream));
            writer.write("写入数据");
        } catch (Exception e) {
    
        }finally {
            try {
                if(writer!=null)
                    writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    
    //读取文件,找到名称;输入流;缓冲读出
      try {
            FileInputStream input = openFileInput("filename");
            InputStreamReader streamReader = new InputStreamReader(input);
            reader = new BufferedReader(streamReader);
            String line = "";
            while ((line = reader.readLine()) != null) {
                Log.d(TAG, line);
            }
        } catch (Exception e) {
    
        } finally {
            
            try {
                if(read!=null)
                   reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
    
    • SharedPerferences存储

      • Context 类中的getSharedPrefences(“filename”,Context.MODE_PRIVATE)方法(不存在则创建)目前只有Context.MODE_PRIVATE一个模式,和传入0的效果一致,都是默认的模式,其他模式在6.0及以前全部废除。
      • Activity 类中的getPreference(Context.MODE_PRIVATE)方法,其中Activity中的getSharedPreference调用的还是Context中的。getPreference()和getSharedPreferences类似,不过没有文件名,因为,它将当前的 活动类名 直接作为SharedPreferences的文件名。
      • PreferenceManager 类中的getDefaultSharedPreference(context)静态方法,自动将当前的 应用包名 作为当前未命名的SharedPreferences文件名称,
      SharedPreferences example = getSharedPreferences("example", MODE_PRIVATE);
      //获取编译对象
      SharedPreferences.Editor edit = example.edit();
      //存入键值对的内容
      edit.putFloat("elem",1.1f);
      //提交
      edit.apply();
      //获取内容
      example.getFloat(key,value)
      

      存储路径:/data/data/< packagename > /shared_prefs/ 目录下的xml文件。

    • SQLite数据库存储
      安卓为了更好的使用管理数据库,专门提供了一个SQLiterOpenHelper 抽象类,SQLiteOpenHelper有两个实例方法getReadableDatabase(),getWritableDateBase()连个方法都可以创建或打开(升级)一个现有数据库,并返回一个可以对数据库操作读写的对象。不同的是,当控件磁盘已满等不能读写的情况,getReadableDateBase()返回的对象将只以 只读 的方式打开,getWritableDataBase()将出现异常。

    
    //数据库的实例
    
    public class MySQLiteOpenHelper extends SQLiteOpenHelper {
    /**
     * @param context
     * @param name      数据库名,使用的时候需要指定的数据库名字
     * @param factory   查询数据的时候会返回一个自定义Cursor,一般传入null
     * @param version    版本号
     */
    public MySQLiteOpenHelper(Context context,  String name,  SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }
    
    String dataBase="create table book(" +
            "id integer primary key autoincrement," +
            "author text," +
            "price real," +  //浮点
            "page integer," +
            "name text)"; //bolb 二进制
    @Override
    public void onCreate(SQLiteDatabase db) {
        //如过这里有符㷣个建表语句,且其中有一存在的表,则不会走create方法
        db.execSQL(dataBase);
    }
    
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }
    

    数据库的创建

    
    //数据库的创建等操作
    
        MySQLiteOpenHelper helper = new MySQLiteOpenHelper(this, "Database.db", null, 1);
        //如果数据库查找不存在,代码走到getReaderDataBase时候,就会调用实例里面的onCreate方法
        //如果数据库存在,则不会调用onCreate()方法。
        SQLiteDatabase readableDatabase = helper.getReadableDatabase();
    
    
    

    数据库的路径:/data/data/< packagename >/database/目录下

    找到sqlite.db文件可用以下命令操作

    1. sqlite3 datebase.db 打开数据
    2. .table 查看数据库中的表哥
    3. .schema 查看简表语句
    4. .exit / .quit 退出建表语句
    
    //数据库的升级
    为了新添加表格,先把数据库的版本号升级为更高级的版本号,当检测到版本号大于之前的版本号,则调用onUpdata方法
     @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("drop table if exists book");
        db.execSQL("drop table if exists newbook");
        onCreate(db)
    }
    
    //添加数据库
    ContentValues values = new ContentValues();
    values.put("key","value");
    readableDatabase.insert("tableName",null,values);
    
    //更新数据
    ContentValues values = new ContentValues();
    values.put("price",10);
        //将书名字改成价格
    readableDatabase.update("tablename",values,"name=?",new String[]{"bookname"});
        
    //删除数据
    readableDatabase.delete("tablename","price>?",new String[]{"500"});
    
    //查寻数据
         /**
         * @param table 查询表格的名字
         * @param column 指定列
         * @param selection 指定where的约束条件
         * @param selectionArgs 为where提供占位符的具体值
         * @param groupby 需要制定的groupby列
         * @param having 对groupby的进一步约束
         * @param orderby 指定查询结果的排序方式              
         */
        Cursor cursor = readableDatabase.query("tablename",
                null,
                null,
                null,
                null,
                null,
                null,
                null
        );
        
        while(cursor.moveToNext()=null){
            //获取位置索引,然后取值
            String name = cursor.getString(cursor.getColumnIndex("name"));
            int page = cursor.getInt(cursor.getColumnIndex("page"));
        }
        //一定要关闭游标
        cursor.close();
    
    
    • LitePals数据库

    Litepal的github地址


  • Content Provider

    • 权限
      危险权限需要手动处理,这里的ContextCompat.checkShelfPermission()来检测是否权限被申请,否则,
    if(ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)!= PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CALL_PHONE},1);
        }
        Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:10086" ));
        startActivity(intent);
    
    • 数据共享
      ,sharePerferences在4.2时已经废除了应用之间数据共享的功能,现在访问共享内容,要用到contentResolver,可以通过Context中的getContentResolver方法获得实例,contentResolver提供了增删改查的API,其中不接受表名等形参,只接受URI,URI建立唯一标识符,主要有authority和path构成,都是用来避免冲突。 authority 用于区分应用,一般用包名来命名, path 一般来区分表名,除此之外,还要将两个标示的结合的URI字符串上添加Content的特有标示content://,所以综合的标准写法为:
    content://< packagename >.provider/tablename
    

    contentResolver中的增删改查也都是接收URI对象,使用表名则不知道具体的应用对象。

        Uri uri=Uri.parse("content://<packagename>.provider/table1");
        //insert
        ContentValues values=new ContentValues();
        getContentResolver().insert(uri,values);
        //delete
        getContentResolver().delete(uri,"where=?",new String[]{"300"});
        //update
        getContentResolver().update(uri,values,"column1=? and column2=?",new String[]{"text","1"});
        //query
        getContentResolver().query(uri,"columnName","price>?",new String[]{"100"},"desc")
    
    
    • 案例
    if (ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS)
                !=PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this,new String[]{
                    Manifest.permission.READ_CONTACTS},1);
        }else{
            logContacts();
        }
    /**
     * 查询通讯录信息
     */
    private void logContacts(){
        Cursor query = getContentResolver()
                .query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                        null, null, null, null);
        while(query.moveToNext()){
            String name = query.getString(
                    query.getColumnIndex(
                            ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
            String number = query.getString(
                    query.getColumnIndex(
                            ContactsContract.CommonDataKinds.Phone.NUMBER));
    
            Log.e(TAG,"name:"+name+"/number:"+number);
        }
    }
    
    • 案例ContentProvider
    
    public class MyProvider extends ContentProvider {
        /**
         * 通常完成数据库的创建和升级
         * 只有当存在ContentRedolver访问应用程序的数据时才会触发
         * @return true provider初始化成功
        */
        @Override
        public boolean onCreate() {
            return false;
        }
    
        @Override
        public Cursor query(Uri uri,  String[] projection,  String  selection,  String[] selectionArgs,  String sortOrder) {
          return null;
        }
    
        /**
         *
         * @param uri 除了包名表名外还需要添加id,表示查询的是表中的某个数据,例如:
         *            "content://<packageName>.provider/table1/i001"
         *            "content://<packageName>.provider/table1/*"
         *            ⚠️ *  表示任意字符串
         *            ⚠️ #  表示任意长度的数字
         * @return   根据传入的内容uri,返回对应的MIME类型(文本,图片,音视频等)
        */
        @Override
        public String getType(Uri uri) {
            return null;
        }
    
    
        @Override
        public Uri insert(Uri uri,  ContentValues values) {
            return null;
    }
    
        @Override
        public int delete(Uri uri,  String selection,  String[] selectionArgs) {
            return 0;
    }
    
        @Override
        public int update(Uri uri,  ContentValues values,  String selection,  String[] selectionArgs) {
            return 0;
    
        }
    }
    
    

    内容提供者的构成

    public class MyProvider extends ContentProvider {
    
    //设置表内属性id
    public static final int TABLE1_ALL=0;
    public static final int TABLE1_ITEM=1;
    
    private static UriMatcher matcher;
    //添加期望内容,根据resolver传来的uri来判断是什么数据。
    static {
        matcher = new UriMatcher(UriMatcher.NO_MATCH);
        matcher.addURI("<packageName>","table",TABLE1_ALL);
        matcher.addURI("<packageName>","table/#",TABLE1_ITEM);
    }
    
    //根据uri进行数据查找
    @Override
    public Cursor query(Uri uri,  String[] projection,  String selection,  String[] selectionArgs,  String sortOrder) {
        switch (matcher.match(uri)){
            case TABLE1_ALL:
                //查询table1所有的内容
                break;
            case TABLE1_ITEM:
                //查询table1指定条内容
            default:
                break;
        }
        return null;
    }
    
    @Override
    public String getType(Uri uri) {
        switch (matcher.match(uri)){
            case TABLE1_ALL:
                return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
            case TABLE1_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
        }
        return null;
    }
    ........
    }
    
    

    getType()用来获取对象的对应MIME类型(必要)
    ⚠️ 开头必须以vnd开头
    ⚠️ 若URI是以路径结尾(表),则其后接android.cursor.dir/ 。如果是以id结尾(属性)则后接android.curdor.item/。
    ⚠️ 最后接上vnd..
    例:vnd.android.cursor.dir/vnd.com.example.app.provider.table1

    在每一个增删改查中编辑好,myOpenHelp.getReadableDatabase().insert()/delete()…操作,Manifest注册后,然后在其他应用可通过contentResolver可获得内容。


  • 通知

    Notification是Android的特色功能之一,可由系统服务所提供(NotificationManager) Context.getSystemService(Context.NOTIFICATION_SERVICE),各个版本的对通知这一功能多多少少都有改动,API并不稳定,由此用v4-support库提供的兼容的API。NotificationCompat.Builder(Context)此构造函数在API级别26中已弃用。请改用Notification.Builder(Context,String channel)。所有发布的通知必须指定NotificationChannel ID。如下:

    manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
        NotificationChannel channel = new NotificationChannel(
                            "channelID",
                            "channelName",
                         NotificationManager.IMPORTANCE_HIGH);
    
        manager.createNotificationChannel(channel);
    }
        Notification notification= new NotificationCompat.Builder(this,"channelID")
                .setContentTitle("Notification")
                //需要无色图,不能用RGB图
                .setSmallIcon(R.mipmap.ic_launcher)
                .setColor(200)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
                .build();
    
        manager.notify(1,notification);
        
        //.setLights(Color.GREEN,1000,1000)呼吸灯颜色
        //.setDefaults(NotificationCompat.DEFAULT_ALL)//震动呼吸声音
    

    在上有一个setSmallIcon其中设置的是状态栏的小图标,官网在新版中规定不能设置带有颜色的图片,只能用无RGB(纯白)的icon代替了。

    • 高级
      当通知的内容(多文字,大图片)在通知栏无法显示完整时,一般会显示省略号,但是需求显示时,可以通过以下方式进行设置,
    //文字过多(notification高度增加)
    .setStyle(new NotificationCompat.BigTextStyle()
                        .bigText("NotificationNotificationNotif" +
                                "icationNotificationNotification" +
                                "NotificationNotificationNotifica" +
                                "tionNotificationNotificationNotification"))
                                
    //显示大图(在notification下显示大图)
    .setStyle(new NotificationCompat.BigPictureStyle()
                        .bigPicture(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher)))
    

    设置Notification的等级(优先级),存在五个等级,依次递减

    1. PRIORITY_MAX
    2. PRIORITY_HIGH
    3. PRIORITY_DEFAULT
    4. PRIORITY_LOW
    5. PRIORITY_MIN

    代码如下

    .setPriority(NotificationCompat.PRIORITY_MAX)
    

总结: 系统管理对象+通道+通知的样式


  • 照相机

    • 调用系统相机
      调用系统相机在7.0之之前只要用URI的fromFile()将File转成URI对象来标示图片的真实地址,之后,用FileProvider.getUriForFile()方法,通过provider进行调用,由于URI的真实路径被认为是不安全的,会直接抛异常, FileProvider中会提供一个内容提供者,进行类似包装的的机制,对数据进行保护,可选择数据的共享,提高安全性。

      //这里存在缓存里,是因为方便,SD存储是危险权限,要动态申请
      File file=new File(getExternalCacheDir(),"out_put_img.png");
      try {
            if(file.exists()){
                file.delete();
            }
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
      
        Uri imgUri = Uri.fromFile(file);
        if(Build.VERSION.SDK_INT>=24){
            //authority=<packagename>.fileprovider
            imgUri= FileProvider.getUriForFile(this,"com.example.basejavademo.fileprovider",file);
        }
        Intent intent=new Intent("android.media.action.IMAGE_CAPTURE");
        //加入图片传输地址
        intent.putExtra(MediaStore.EXTRA_OUTPUT,imgUri);
        //启动活动
        startActivityForResult(intent,1);
      

      创建共享路径,用来指定uri的共享,

      // res文件下,新建file_paths.xml,name属性可以随便设置,路径/表示整个共享。
      <?xml version="1.0" encoding="utf-8"?>
      <paths xmlns:android="http://schemas.android.com/apk/res/android">
            <external-path
                name="my_image"
                path="/"/>
            </paths>
            
      

      注册provider, 指定共享的路径

      <provider
            android:authorities="<packagename>.fileprovider"
            android:name="android.support.v4.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
        </provider>
        
      

      在onActivityResult()来完成来处理返回的bitmap数据。

      
      protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        //Bitmap处理
        switch (requestCode){
            case 3:
                if (resultCode==RESULT_OK){
                    //判断手机版本号作出不同的处理
                    if(Build.VERSION.SDK_INT>=19){
                        //4.4以后的系统
                        //处理图拍 BitmapFactory.decodeFile(imagePath)
                        String imgPath=afterKitKat(data);
                    }else{
                        //4.4以前的系统
                        String imgPath=beforeKitKat(data);
                    }
                }
                break;
        }
      }
      
    • 从相册里选择照片
      ⚠️从4.4开始手机返回的不再是图片真实的uri了,而是封装之后的uri,此后必须解析这个uri才行。代码如下:
      授权

      if (ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)!=PackageManager.PERMISSION_GRANTED){
              ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},2);
          }else{
              openAlbum();
          }
          /*
      *
      * */
      @Override
      public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
          super.onRequestPermissionsResult(requestCode, permissions, grantResults);
      
          //获权后的操作
          switch (requestCode){
              case 2:
                  if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
                      openAlbum();
                  }
                  break;
          }
      }
      

    跳转相册

    //跳转相册
      private void openAlbum() {
          Intent intent=new Intent("android.intent.action.GET_CONTENT");
          intent.setType("image/*");
          startActivityForResult(intent,3);
      }
    

    跳转之后的处理

    @Override
      protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
          super.onActivityResult(requestCode, resultCode, data);
          //Bitmap处理
          switch (requestCode){
              case 3:
                  if (resultCode==RESULT_OK){
                      //判断手机版本号作出不同的处理
                      if(Build.VERSION.SDK_INT>=19){
                          //4.4以后的系统
                          //处理图拍 BitmapFactory.decodeFile(imagePath)
                          String imgPath=afterKitKat(data);
                      }else{
                          //4.4以前的系统
                          String imgPath=beforeKitKat(data);
                      }
                  }
                  break;
          }
    
      }
    

    获取图片的真实路径

    private String getImagePath(Uri uri,String selection) {
          String path = null;
          //通过Uri和selection来获取真实的图片路径
          Cursor cursor=getContentResolver().query(uri,null,selection,null,null);
          if (cursor!=null){
              if(cursor.moveToFirst()){
                  path=cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
              }
    
              cursor.close();
          }
          return path;
      }
    

    4.4之前的版本处理方式

     //4.4以前的处理。
      private String beforeKitKat(Intent intent) {
          Uri uri=intent.getData();
          String imagePath = getImagePath(uri, null);
          return imagePath;
      }
    

    4.4及以上的版本处理方式

     //4.4以后的处理。解析URI
      private String afterKitKat(Intent intent) {
          String imagePath=null;
          Uri uri=intent.getData();
          if(DocumentsContract.isDocumentUri(this,uri)){
              //如果是document类型的uri,则通过document id来处理
              String docId=DocumentsContract.getDocumentId(uri);
              if("com.android.providers.media.documents".equals(uri.getAuthority())){
                  String id =docId.split(":")[1];
                  String selection =MediaStore.Images.Media._ID+"="+id;
                  imagePath=getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection);
              }else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){
                  Uri contentUri=ContentUris.withAppendedId(
                          Uri.parse("content://downloads/public_downloads"),
                          Long.valueOf(docId));
                  imagePath=getImagePath(contentUri,null);
              }
          }else if ("content".equalsIgnoreCase(uri.getScheme())){
              //如果是content的类型的uri则普通处理
              imagePath=getImagePath(uri,null);
          }else if ("file".equalsIgnoreCase(uri.getScheme())){
              //如果是file类型的uri直接展示图片即可
              imagePath=uri.getPath();
          }
    
          return imagePath;
      }
    
    

  • 影音

    • 播放音频
      在安安卓中播放音频,一般是由MediaPlayer来实现的,提供了比较全面的方法,操作简单。
    停止方法名描述
    setDataSouce()设置播放的音视频位置
    prepare()开始播放前的,完成准备工作
    start()开始/继续
    pause()暂停播放
    reset()MediaPlayer重置为刚创建的状态
    seekto()指定位置开始
    stop()停止,调用这个方法后MediaPlayer无法再播放
    release()释放掉MediaPlayer相关的资源
    isPlaying()是否正在播放
    getDuration()获取载入音频的时长

    代码如下:

    try {
            MediaPlayer player=new MediaPlayer();
            //播放本地资源
            //player.setDataSource("/mnt/sdcard/test.mp3")
            //播放远程的资源文件
            //Uri uri = Uri.parse("http://**");
            //player.setDataSource(Context, uri);
            player.setDataSource("");
            player.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //结束
        if(player!=null){
            player.stop();
            player.release();
        }
    
    • 播放视频
      播放视频住哟啊是运用VideoView来实现的。这个类将显示和控制集于一身。主要的常用方法如下:
    方法名描述
    setVideoPath()设置文件位置
    start()开始/继续
    pause()暂停
    resume()视频从头开始
    seekto()指定位置开始播放
    isPlaying()是否在播放
    getDuration()获取载入文件的时常
    void setVideoPath(String path):以文件路径的方式设置VideoView播放的视频源。
    //Uri uri = Uri.parse("http://**");
    void setVideoURI(Uri uri):以Uri的方式设置VideoView播放的视频源,可以是网络Uri或本地Uri。
    

笔记资料来自书籍《第一行代码》郭霖,无商业目的
🔗 前言
🔗 Android Review 列表
🔗 Android Review—1
🔗 Android Review—3
🔗 Android Review—4
🔗 Android Review—5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零蚀zero eclipse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值