Android学习羁绊之多媒体开发

Posted 姓chen的大键哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android学习羁绊之多媒体开发相关的知识,希望对你有一定的参考价值。

文章目录


手机在我们的生活中正扮演着越来越重要的角色,各种娱乐方式都可以在手机上进行。众多的娱乐方式少不了强大的多媒体功能的支持,而android在这方面也做得非常出色。它提供了一系列的API,使得我们可以在程序中调用很多手机的多媒体资源,从而编写出更加丰富多彩的应用程序。

调用摄像头和相册

调用摄像头拍照

现在很多的应用都会要求用户上传一张图片来作为头像,这时打开摄像头拍张照是最简单快捷的。

启动摄像头

应用程序要启动手机的相机程序需要通过以下代码来实现:

// 启动相机程序
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, 1);
  • 使用Intent对象启动相机程序,Intent的action指定为android.media.action.IMAGE_CAPTURE
  • 调用Intent对象的putExtra() 方法将图片的输出地址的Uri对象作为值传入,以MediaStore.EXTRA_OUTPUT 作为键(与多媒体相关的可以从MediaStore这个类中得到)

指定照片存放路径

拍照需要指定图片存储路径,在上述代码中将Uri对象作为图片输出路径,通过以下代码来构建这个Uri对象:

private Uri imageUri;
// 创建File对象,用于存储拍照后的图片
File outputImage = new File(getExternalCacheDir(), "output_image.jpg");
try 
	if (outputImage.exists()) 
		outputImage.delete();
	
	outputImage.createNewFile();
 catch (IOException e) 
	e.printStackTrace();

if (Build.VERSION.SDK_INT >= 24) 
	imageUri = FileProvider.getUriForFile(MainActivity.this, "cn.chenjianlink.cameraalbumtest.fileprovider", outputImage);
 else 
	imageUri = Uri.fromFile(outputImage);

  • 照片属于文件,用File类临时保存。

  • getExternalCacheDir() 方法用于获取应用程序关联目录,具体的路径是:/sdcard/Android/data/<package name>/cache(这个目录是SD卡中专门用于存放当前应用缓存数据的位置,由于从Android 6.0系统开始,读写SD卡被列为了危险权限,如果将图片存放在SD卡的任何其他目录,都要进行运行时权限处理才行,而使用应用关联目录则可以跳过这一步)

  • Build.VERSION.SDK_INT用于获取当前系统的SDK版本

  • 如果运行设备的系统版本低于Android 7.0,就调用Uri的fromFile() 方法将File对象转换成Uri对象,这个Uri对象标识着output_image.jpg这张图片的本地真实路径。

  • 从Android 7.0系统开始,直接使用本地真实路径的Uri被认为是不安全的,会抛出一个FileUriExposedException异常。而FileProvider则是一种特殊的内容提供器,它使用了和内容提供器类似的机制来对数据进行保护,可以选择性地将封装过的Uri共享给外部,从而提高了应用的安全性。
    在系统版本高于Android 7.0的设备上,调用FileProvider的getUriForFile() 方法将File 对象转换成一个封装过的Uri对象,这个方法有三个参数:

public static Uri getUriForFile(@NonNull Context context, @NonNull String authority, @NonNull File file)
  • Context context:Context对象,一般传入调用的程序当前Activity
  • String authority:任意唯一的字符串,作为唯一标识
  • File file:将要转换的文件的file对象传入
  • 使用FileProvider,需要在AndroidManifest.xml中对这个内容提供器进行注册
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.chenjianlink.cameraalbumtest">
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
	<application
		android:allowBackup="true"
		android:icon="@mipmap/ic_launcher"
		android:label="@string/app_name"
		android:supportsRtl="true"
		android:theme="@style/AppTheme">
		...
		<provider
			android:name="android.support.v4.content.FileProvider"
			android:authorities="cn.chenjianlink.cameraalbumtest.fileprovider"
			android:exported="false"
			android:grantUriPermissions="true">
			<meta-data
				android:name="android.support.FILE_PROVIDER_PATHS"
				android:resource="@xml/file_paths" />
		</provider>
	</application>
</manifest>
  • 访问SD卡需要声明权限(Android 4.4系统以前)
  • android:authorities属性的值必须要和FileProvider.getUriForFile() 方法中的第二个参数一致
  • <provider>标签中使用<meta-data>来指定Uri的共享路径,引用@xml/file_paths 资源,这个文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
   	<external-path name="my_images" path="" />
</paths>

external-path用来指定Uri共享,name属性的值可以随便填,path属性的值表示共享的具体路径(设置为空值表示将整个SD卡共享)

照片显示

拍完照之后,一般需要回到应用程序中,则需要重写Activity类中的onActivityResult() 方法,在这个方法中执行相关逻辑。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
	//自定义逻辑

如果需要将拍摄好的照片在应用程序中显示,则需要使用调用BitmapFactory的
decodeStream() 方法将照片解析成Bitmap 对象,然后把它设置到ImageView中显示出来:

// 将拍摄的照片显示出来
private ImageView picture = (ImageView) findViewById(R.id.picture);
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
picture.setImageBitmap(bitmap);

还需要在布局文件中引入ImageView这个控件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="match_parent"
	android:layout_height="match_parent" >
	···
	<ImageView
		android:id="@+id/picture"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_gravity="center_horizontal" />
	
	···
</LinearLayout>

从相册中选取照片

应用程序调用照片还可从手机相册里

打开相册

用Intent对象打开相册,相关代码如下:

Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent, 1); // 打开相册

打开相册需要动态申请WRITE_EXTERNAL_STORAGE 这个危险权限,相册中的照片都是存储在SD卡上的,要从SD卡中读取照片就需要申请这个权限。WRITE_EXTERNAL_STORAGE 表示同时授予程序对SD卡读和写的能力。

相册图片显示

Android系统从4.4版本开始,选取相册中的图片不再返回图片真实的Uri了,而是一个封装过的Uri,因此如果是4.4版本以上的手机就需要对这个Uri进行解析才能获取照片的真实路径。

在Android 4.4以下系统获取图片真实路径:

private void handleImageBeforeKitKat(Intent data) 
	Uri uri = data.getData();
	String imagePath = getImagePath(uri, null);
	displayImage(imagePath);

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;

private void displayImage(String imagePath) 
	if (imagePath != null) 
		Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
		picture.setImageBitmap(bitmap);
	 else 
		//···
	

在Android 4.4以上系统获取图片真实路径:

@TargetApi(19)
private void handleImageOnKitKat(Intent data) 
	String imagePath = null;
	Uri uri = data.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]; // 解析出数字格式的id
			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();
	
	displayImage(imagePath); // 根据图片路径显示图片

  • 如果返回的Uri是document类型的话,那就取出document id 进行处理,如果不是的话,那就使用普通的方式处理。
  • 如果Uri的authority是media格式的话,document id还需要再进行一次解析,要通过字符串分割的方式取出后半部分才能得到真正的数字id。

播放多媒体文件

手机上最常见的休闲方式毫无疑问就是听音乐和看电影了,Android在播放音频和视频方面也是做了相当不错的支持,它提供了一套较为完整的API,使得开发者可以很轻松地编写出一个简易的音频或视频播放器。

播放音频

在Android中播放音频文件一般都是使用MediaPlayer 类来实现的,它对多种格式的音频文件提供了非常全面的控制方法,从而使得播放音乐的工作变得十分简单。

MediaPlayer常用方法如下:

MediaPlayer的工作流程如下:

  1. 创建出一个MediaPlayer对象
  2. 调用 setDataSource() 方法来设置音频文件的路径
  3. 调用prepare() 方法使MediaPlayer进入到准备状态
  4. 调用start() 方法开始b播放
  5. 播放音频,调用pause() 方法就会暂停播放,调用reset() 方法就会停止播放

在不再使用MediaPlayer时,分别调用stop() 方法和release() 方法,将与MediaPlayer相关的资源释放掉

播放视频

播放视频文件使用VideoView类来实现,这个类将视频的显示和控制集于一身。

VideoView的用法和MediaPlayer也比较类似,主要有以下常用方法:

VideoView的工作流程如下:

  1. 创建出一个VideoView对象
  2. 调用setVideoPath() 方法来设置音频文件的路径
  3. 调用start() 方法开始播放
  4. 播放视频,调用pause() 方法就会暂停播放

要在程序中显示播放的视频,需要在布局文件中添加VideoView这个控件。

以上是关于Android学习羁绊之多媒体开发的主要内容,如果未能解决你的问题,请参考以下文章

Android学习羁绊之Notification

Android学习羁绊之Service

Android多媒体之音频播放

Android多媒体之音频播放

Android学习羁绊之Material Design

Android高级终端开发学习笔记(《疯狂Android讲义》第11章-第17章)