Android学习笔记之多媒体

使用多媒体

通知

8.0+引入通知渠道

创建通知渠道

一旦创建,其重要性不可修改,HIGH是弹出横幅,MIN是没通知栏小图标

NotificationManager manager= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);//获取通知管理器,在管理器上操作
    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){//如果当前API版本>=8.0(8.0代号为字母O)
        NotificationChannel channel=new NotificationChannel("normal","Normal",NotificationManager.IMPORTANCE_DEFAULT);//创建通知渠道,参数为(渠道id,渠道名,重要性)
        manager.createNotificationChannel(channel);//添加到通知管理器
    }

创建点击通知后的跳转

Intent是立即执行,PendingIntent相当于延迟执行的Intent

方式一:

点击通知后跳转到AnotherActivity,再按返回键就会回到桌面

Intent intent = new Intent(this, AnotherActivity.class);
//参数为(上下文,请求码,Intent,flags)
PendingIntent pending = PendingIntent.getActivity(this, 0, intent, 0);

方式二:

点击通知后跳转到AnotherActivity,再按返回键会进入MainActivity

<activity
        android:name=".AnotherActivity"
        android:parentActivityName=".MainActivity" />
/*实现逻辑:初始时任务栈里只有MainActivity,点击通知后执行pendingIntent,使得AnotherActivity也进入任务栈*/
​
//创建1个任务栈构建器
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
//将在Manifest中指定好的父Activity加入到构建器中
stackBuilder.addParentStack(AnotherActivity.class);
//给构建器设定未来会发生的任务:执行intent
stackBuilder.addNextIntent(intent);
//得到要执行的任务intent的pending版本
PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, 0);

创建通知并发送

Button button=findViewById(R.id.button);
button.setOnClickListener((View v)->{
    Notification notification= new NotificationCompat.Builder(this,"normal")//使用compat可以兼容所有安卓版本,参数为(上下文,渠道id)
            .setContentTitle("通知标题")
            .setContentText("通知内容")
            .setSmallIcon(R.drawable.xxx)
            .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.xxx))//将图片解析为Bitmap对象
            .setContentIntent(pending)//点击通知后跳转到的页面
            .setAutoCancel(true)//点击通知后通知消失(或者是在AnotherActivity中写manager.cancel(1))
            .build();//创建通知对象
​
    manager.notify(1,notification);//通过管理器发送通知,参数为(通知id,通知对象)
});

还可以通过setStyle()指定更多样式,如大量文字,大图片,同一应用通知折叠InboxStyle,多媒体

.setStyle(new NotificationCompat.BigTextStyle().bigText("很长的一段文字"))

取消通知

//所有通知全部消失!!
manager.cancelAll();

调用相机

调用摄像头拍照并保存

public class MainActivity extends AppCompatActivity {
​
    public static String authority = "com.example.cameraalbumtest.fileprovider";
    ActivityMainBinding binding;
    Uri imageUri;
​
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.button.setOnClickListener((View v) -> {
​
            //先在代码中新建File对象,意为预期在当前应用在内置SD卡中的沙盒cache目录创建output_image.jpg
            File outputImage = new File(getExternalCacheDir(), "output_image.jpg");
            if (outputImage.exists()) {//存在则删除
                outputImage.delete();
            }
            try {
                outputImage.createNewFile();//不存在则创建
            } catch (IOException e) {
                e.printStackTrace();
            }
​
            
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                //Android 7.0+ File→Uri要使用这种方式,因为content URI更安全(content://...)
                //此步骤需要为本应用创造一个Authority为"包名.fileprovider"的ContentProvider
                //FileProvider是一种特殊的ContentProvider,其getUriForFile()方法获得file的content URI
                imageUri = FileProvider.getUriForFile(this, authority, outputImage);
            } else {
                //低版本使用file URI(file://...)
                Uri.fromFile(outputImage);
            }
​
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);将拍到的照片转存到指定Uri
            startActivityForResult(intent, 1);
        });
    }
​
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1) {
            //如果成功,说明在我们指定的imageUri处已经有了新拍的照片文件
            if (resultCode == RESULT_OK) {
                try {
                    //Uri→InputStream→Bitmap
                    //ContentResolver中的openInputStream(Uri uri)是一种把给定的Uri→InputStream方式
                    Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                    binding.imageView.setImageBitmap(bitmap);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

因为创建了ContentProvider,所以要注册

因为要使用content://uri替代file://uri,那么,content://的uri如何定义呢?总不能使用文件路径,因为目的就是安全,不暴露真实路径

所以,需要一个虚拟的路径对文件路径进行映射,所以需要编写个xml文件,通过path以及xml节点确定可访问的目录,通过name属性来映射真实的文件路径

<provider
        android:authorities="com.example.cameraalbumtest.fileprovider"//与程序定义的一致
        android:name="androidx.core.content.FileProvider"//name是固定的
        android:exported="false"
        android:grantUriPermissions="true">//是否允许授权文件的临时访问权限
    <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"<!--指定可以授权虚拟化的文件路径-->
            android:resource="@xml/file_paths" />
</provider>

file_paths支持的节点:

当这么声明以后,代码可以使用你所声明的当前文件夹以及其子文件夹

ValuePath
root-path/
files-path/data/data/<包名>/files
cache-path/data/data/<包名>/cache
external-path/storage/emulate/0
external-files-path/storage/emulate/0/Android/data/<包名>/files
external-cache-path/storage/emulate/0/Android/data/<包名>/cache

这里我们在xml目录下创建file_paths的内容

//每个节点都支持两个属性:name+path
//path:需要临时授权访问的路径(.或/代表所有路径) 
//name:就是你给这个访问路径起个名字
<paths>
    <external-path
            name="my_images"
            path="/" /><!--授权整个内置SD卡-->
</paths>

调用相册

binding.photo.setOnClickListener((View v) -> {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);//源码中说这两个是必须指定的
    intent.setType("image/*");//设置一个MIME类型(*/*,image/*,documents/*,audio/*,...)
    startActivityForResult(intent, 2);
});

BitmapFactory.decodeFileDescriptor似乎比BitmapFactory.decodeStream占用内存更小

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    ...
    else if (requestCode == 2) {
        if (resultCode == RESULT_OK && data != null) {
            Bitmap bitmap = null;
            try {
                Uri uri = data.getData();//返回的就是选择的文件content URI
                FileDescriptor fd = getContentResolver().openFileDescriptor(uri, "r").getFileDescriptor();
                bitmap = BitmapFactory.decodeFileDescriptor(fd);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            binding.imageView.setImageBitmap(bitmap);
        }
    }
}

播放音频

在main包下新建assets文件夹

API特点
MediaPlayer其他情况
SoundPool适用于APP经常需要播放固定的,密集的,短促的音效;CPU占用率低,延迟小;可以自行设定声音品质音量等;支持多个音频同时播放

MediaPlayer

  • start()对应开始

  • pause()对应暂停

  • reset()对应停止

  • stop()+release()对应程序结束

 

MediaPlayer mMediaPlayer = new MediaPlayer();
AssetManager assetManager;
​
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
        
    assetManager = getAssets();
    initMediaPlayer();
​
    binding.play.setOnClickListener((View v) -> {
        if (!mMediaPlayer.isPlaying())
            mMediaPlayer.start();
    });
​
    binding.pause.setOnClickListener((v) -> {
        if (mMediaPlayer.isPlaying())
            mMediaPlayer.pause();
    });
​
    binding.stop.setOnClickListener((v) -> {
        mMediaPlayer.reset();
        //停止播放后,准备好下一次播放的资源
        initMediaPlayer();
    });
}
​
public void initMediaPlayer() {
    try {
        AssetFileDescriptor afd = assetManager.openFd("music.mp3");
        FileDescriptor fd = afd.getFileDescriptor();
        //setDataSource()的其它重载也可以
        mMediaPlayer.setDataSource(fd, afd.getStartOffset(), afd.getLength());
        mMediaPlayer.prepare();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
​
@Override
protected void onDestroy() {
    ...
    //释放资源
    mMediaPlayer.stop();
    mMediaPlayer.release();
}

SoundPool

具体应用见BeatBox项目

//音频属性,指定音频的用途
AudioAttributes audioAttributes = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build();
​
//声音池
SoundPool mSoundPool = new SoundPool.Builder()
        .setMaxStreams(5)//最多容纳多少个音频流,设置太多小心内存限制
        .setAudioAttributes(audioAttributes)
        .build();

加载进声音池

AssetFileDescriptor afd=mAssetManager.openFd("music.mp3");
//把音频文件载入声音池
int soundId=mSoundPool.load(afd,1);//这个soundId播放要用,需要保留
afd.close();

播放的时候需要指定soundId,我这里将它放入了Sound对象

int soundId = sound.getSoundId();
int streamId = mSoundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f);//可以指定左右音量,循环,倍速等播放属性
mSoundPool.pause(streamId);//暂停播放

最后要释放内存

mSoundPool.release();

播放视频

VideoView本质上是MediaPlayer的封装

对视频格式的支持以及播放效率方面还存在着不足

 

Uri uri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.video);//固定写法
binding.videoView.setVideoURI(uri);
​
binding.play.setOnClickListener((v) -> {
    if (!binding.videoView.isPlaying())
        binding.videoView.start();//播放
});
​
binding.pause.setOnClickListener((v) -> {
    if (binding.videoView.isPlaying())
        binding.videoView.pause();//暂停
});
​
binding.replay.setOnClickListener((v) -> {
    binding.videoView.resume();//重播
});

最后释放内存

binding.videoView.suspend();

tips:获取视频资源Uri的Kotlin写法为

Uri uri = Uri.parse("android.resource://$packageName/${R.raw.video}");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. Android 的基本概念 Android 是一个开源的操作系统,主要用于移动设备,如智能手机、平板电脑等。它基于 Linux 内核,提供了丰富的应用程序框架和 API,支持多种开发语言,如 Java、C/C++、Kotlin 等。 Android 应用程序由多个组件组成,包括活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)和内容提供器(Content Provider)等。这些组件可以组合在一起,形成复杂的应用程序。 2. Android 应用程序开发 Android 应用程序开发主要使用 Java 编程语言和 Android SDK。开发工具包括 Android Studio、Eclipse 等。 Android 应用程序的结构包括布局文件、资源文件、Java 代码和清单文件等。布局文件用于定义应用程序的用户界面,资源文件包括图像、声音、样式、主题等,Java 代码实现应用程序的逻辑,清单文件描述应用程序的组件和权限等信息。 3. Android 应用程序的调试和测试 Android 应用程序的调试和测试可以使用 Android Studio 提供的调试工具,包括断点调试、日志记录等。还可以使用模拟器或真实设备进行测试。 4. Android 应用程序的发布 发布 Android 应用程序需要进行签名和打包操作,签名用于验证应用程序的身份和完整性,打包将应用程序打包成 APK 文件,可以上传到应用商店进行发布。 5. Android 应用程序的优化 Android 应用程序的优化包括优化布局、资源、代码和网络等方面,以提高应用程序的性能和用户体验。其中,布局优化包括使用布局最优化算法、使用自定义视图等;资源优化包括压缩资源、使用向量图形等;代码优化包括使用异步任务、使用缓存等;网络优化包括使用数据压缩、使用本地存储等。 6. Android 开发的挑战 Android 开发面临的挑战包括设备碎片化、安全问题、性能问题等。设备碎片化指的是不同设备的屏幕尺寸、分辨率、操作系统版本等不同,需要对应用程序进行适配;安全问题指的是应用程序需要保证用户数据的安全和隐私;性能问题指的是应用程序需要保证快速响应和流畅运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值