文章來源
接到一个项目需求,要求安卓手机通过usb线连接单反相机,读取其拍摄的照片及视频。
初步百度了解使用的技术、最快捷是看博客找到源码,能实现出功能的项目源码更是为妙。
GitHub - ynyao/cameraphoto: 通过mtp获取单反相机中的照片来自仓库的一个源码分享,接下来我将详细讲解源码内容的实现。
目錄結構
先忽略图中每个文件的红色叉叉,原因是笔者使用了非安卓开发软件打开了项目,之后会换一台电脑继续编写,注意:项目是用AndroidStudio3.0打开的,低版本可能出错。
看了一下目录,app文件夹是项目的启动项目,而photoview则是以libaray形式存在于项目中,相当于一个jar包吧,这种方式称为把别的项目当成lib导入项目中,就可以使用其项目的一些方法,接口。
導包情況
在观察一下build.grade中的依赖情况
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:support-v4:26.1.0'
implementation 'com.android.support:recyclerview-v7:26+'
implementation 'com.squareup.picasso:picasso:2+'
implementation 'io.reactivex.rxjava2:rxjava:2+'
implementation 'io.reactivex.rxjava2:rxandroid:2+'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
implementation project(':photoview')
}
当中引入了RxJava的RxAndroid包是为了解决异步操作问题,类似于AsyncTask
implementation 'io.reactivex.rxjava2:rxjava:2+'
implementation 'io.reactivex.rxjava2:rxandroid:2+'
RxAndroid2.0使用概述 - CSDN博客
Android响应式编程框架---RxJava&RxAndroid2.0使用笔记 - CSDN博客
啟動運行流程
说一下程序运行流程,先是启动MainActivity-->在活动中检查权限、是否用相机插入等,并先显示英特空的Fraggment界面。透过函数mService=new MTPService(this);实例化具体的操作对象,mtp是一种多媒体传输协议的简写,在MtpService实例化的同时,条件满足则构造方法里startScanPic开始做进一步的读取图片操作。
實現功能的核心代碼段
public void startScanPic(){ disposable = interval(8, TimeUnit.SECONDS) .onBackpressureDrop() .map(new Function() {
@Override
public List apply(Long aLong) throws Exception {
Log.d(TAG,"start1===" + aLong);
List list = new ArrayList();
if (mMtpDevice != null) {
MtpDeviceInfo mtpDeviceInfo = mMtpDevice.getDeviceInfo();
String deviceSeriNumber = null;
if (mtpDeviceInfo != null)
deviceSeriNumber = mtpDeviceInfo.getSerialNumber();
else
deviceSeriNumber = "xx";
int[] storageIds = mMtpDevice.getStorageIds();
if (storageIds == null) {
showToast(mContext,"获取相机存储空间失败");
return list;
}
for (int storageId : storageIds) {
//把儲存空間每個id對應的圖片數據賦值給 objectHandles
int[] objectHandles = mMtpDevice.getObjectHandles(storageId, MtpConstants.FORMAT_EXIF_JPEG, 0);
if(objectHandles==null){
showToast(mContext,"获取照片失败");
return list;
}
for (int objectHandle : objectHandles) {
MtpObjectInfo mtpobj = mMtpDevice.getObjectInfo(objectHandle);
if (mtpobj == null) {
continue;
}
long dateCreated=mtpobj.getDateCreated();
//最後通過 mMtpDevice.getThumbnail(objectHandle)獲得照片的比特流,後續就是將比特流轉文件File并保存在指定地址
byte[] bytes = mMtpDevice.getThumbnail(objectHandle);
filePath.setLength(0);
filePath.append(Environment.getExternalStorageDirectory().getAbsolutePath())
.append(File.separator)
.append("thumbCache")
.append(File.separator)
.append(String.valueOf(dateCreated))
.append(".jpg");
File fileJpg = new File(filePath.toString());
if (!fileJpg.exists() && bytes != null)
FileUtils.bytes2File(bytes, filePath.toString());
PicInfo info = new PicInfo();
info.setObjectHandler(objectHandle);
info.setmThumbnailPath(fileJpg.getAbsolutePath());
info.setmDateCreated(dateCreated);
info.setmImagePixWidth(mtpobj.getImagePixWidth());
info.setmImagePixHeight(mtpobj.getImagePixHeight());
info.setmImagePixDepth(mtpobj.getImagePixDepth());
info.setmThumbPixHeight(mtpobj.getThumbPixHeight());
info.setmThumbPixWidth(mtpobj.getThumbPixWidth());
info.setSequenceNumber(mtpobj.getSequenceNumber());
info.setKeyWords(mtpobj.getKeywords());
info.setmSerialNumber(deviceSeriNumber);
// if(Long.toString(mtpobj.getDateCreated()).startsWith("15")){
// mMtpDevice.deleteObject(objectHandle);
// }
list.add(info);
}
}
}
return list;
}
}).subscribeOn(Schedulers.io()) //线程调度器,将发送者运行在子线程
.observeOn(AndroidSchedulers.mainThread()) //接受者运行在主线程
.subscribe((Consumer) mContext);
}
//過程用了RxJava,return list到MainActivity中,再由活動中的方法實現圖片列表等
至此,PhotoView沒提過,其實就是一些自定義的View