多个摄像通话类的APP,查看全数文件(包含隐藏)

1个摄像展现站点,有大约方便的后台管理。
上传摄像,保存原来的小说件名与储存文件名。
履新录制,成功更新时,删除旧摄像文件,将新的文件新闻存入数据库中。
剔除摄像,删除存于目录中的摄像,并删除数据库的记录。
改变目录时,移动文件至新目录中。
DataList控件展现摄像,还有分页成效。
寻找成效。
次第是.NET Framewrok 4.0, vb.net开发,数据库为SQL Server 二零零六 Odyssey2.

本篇文章研究旧版本升级到新本丑时,新成效怎样处理旧数据的现象。

劳动开启命令

下载: http://download.cnblogs.com/insus/ASPDOTNET/VJellyfish.zip

代码属于那几个App:
JusTalk,三个录像通话类的APP,本文说的是安卓。

service  服务  start/stop/stauts

 

所谓纪念功效,就是借(chao)鉴(xi)苹果系统中的系统相册的想起情势,还有一小点对微信的致(chao)敬(xi)。回想的始末是打电话中发出的录像和涂鸦数据(能够总结的知情为一种独特的摄像)。原本是分别四个列表展现的,并且没有区分是与什么人通话中发出的,而新的追思成效是要基于对方的账号来分类彰显的。

查看ip

本质上,那么些功能就是将原本的几个列表合并成贰个,并用新的平整分类。

ifconfig 

原来的七个列表的数据源都是文件,其实都在二个索引下,通过扩展名来区分属于哪个列表。目录在里面存款和储蓄的JusTalk目录下,卸载也不会去除,能够在系统相册中旁观这么些目录。以mp4为扩大名的就是录制的录制,而涂鸦会有二个文件名相同扩张名分裂的文本,只看jpg就好了。

 清屏

原效劳的弊病

从成品角度讲,全数的这几个剧情都以在与人通话进度中爆发的,但剧情变更后并不曾以人为本,没有记录是与什么人通话发生的,于是爆发了孤立的片段文件,用户进入列表只美观到缩略图,没有与人涉嫌。
从技术角度讲,原列表是一向遍历文件系统,并加以过滤,展现合适的始末,那在小数据量的时候并从未怎么严重的题材。但最坏情况下,目录下的文书过多,遍历2次也是很为难的,而且将文件列表保存在了内部存款和储蓄器里也加大了内部存款和储蓄器的施用。而且为了使列表能自动更新,监听文件系统变化,是3个很不美观的行为。

clear 

新的模型

首先要封存通话对象的消息,其次要缓解遍历文件的难题。这些APP中山高校量行使了Realm做为数据库使用,只需多个不难易行的数据模型就能缓解上述难题。

来妥贴前所在地方

对录像/涂鸦建模,创制2个数据表:

Name Type Description Attributes
date Date 创建时间 Indexed
uri String 所属账号uri Required
type String 类型(涂鸦或视频) Required
fileKey String 文件名相关 Required

对应的Java类:

public class RecollectionItem extends RealmObject {
    public static final String TABLE_NAME = "RecollectionItem";
    public static final String FIELD_URI = "uri";
    public static final String FIELD_DATE = "date";
    public static final String FIELD_TYPE = "type";
    public static final String FIELD_FILE_KEY = "fileKey";

    public static final String TYPE_DOODLE = "doodle";
    public static final String TYPE_VIDEO = "video";

    @Index
    private Date date;
    @Required
    private String uri;
    @Required
    private String type;
    @Required
    private String fileKey;
    // getter and setters...
}

pwd

对用户建模,再次创下建一个数据表:

基于须要,要有别于通话对象,类似于会话,表示各类用户(通话对象),模型如下:

Name Type Description Attributes
uri String 账号uri PrimaryKey
latestDate Date 最后一条记录的日期 Required
latestItem RecollectionItem 最后一个Item
items RealmList<RecollectionItem> 这个账号的所有Item

对应的Java类:

public class RecollectionGroup extends RealmObject {
    public static final String TABLE_NAME = "RecollectionGroup";
    public static final String FIELD_URI = "uri";
    public static final String FIELD_LATEST_DATE = "latestDate";
    public static final String FIELD_NAME = "name";

    public static final String FIELD_ITEMS = "items";
    public static final String FIELD_LATEST_ITEM = "latestItem";

    @PrimaryKey
    private String uri;
    @Required
    private Date latestDate;
    private String name;
    private RealmList<RecollectionItem> items;
    private RecollectionItem latestItem;
}

上述模型有过多冗余的字段:

  • Item中的uri:根据Realm的不二法门,Item是直接链接在Group中的,不必要三个外键来代表本身是哪位Group的。那里丰盛uri没有强力的说辞,大概的用途是只驾驭Item去探寻Group,这些Realm也提供了内置的方案去消除,但需求新本子的Realm,顺便吐槽一下Realm更新速度尤其快,未来新型版本已经3.6了,大家仍在应用2.3。
  • Group中的latestDate和latestItem,这四个都是足以从items的第①条去赢得的,不过Realm并没有提供SQL中复杂的表连接,想要用新型条目标日期对Group排序是从未有过一向的点子的,官方的说教是:你能够团结不论组合呀。

那些作用的特征是创新频率低,而且尚未大气的数目批量布署和换代,读取数据是展现在列表中的,没有复杂的追寻效果,简单直接。于是加几个冗余的字段会省很多事,这么些便利的局地是询问,插入和删除稍微费点事,须求共同更新冗余的字段。

这几个冗余使得查询会变得卓殊简单直接,这也是采纳多少冗余,或然说冗余到怎么程度的标杆。那里要提一下Realm的表征,Realm查询到的RealmResult<T>,并不会事先将兼具数据保存在内部存款和储蓄器里,占用的内部存款和储蓄器越发少,适合在滚动的列表中运用,滚动到哪里读取哪个地方。假如查询的时候要拓展复杂的操作,就要把贰个列表的具有目的引用都放到内部存款和储蓄器里依据规则过滤、排序和重组,来达到SQL中表连接的功能。简单的讲,正是达到规定的标准Adapter的数据源直接利用RealmResult<T>,而不遍历RealmResult<T>的功能。

// 插入
public static void addRecollectionItem(String uri, String type, String fileKey, String defaultName) {
    RealmHelper.executeTransaction(realm -> addRecollectionItemRaw(realm, uri, type, fileKey, new Date(), defaultName));
}
private static void addRecollectionItemRaw(Realm realm, String uri, String type, String fileKey, Date date, String defaultName) {
    RecollectionItem item = realm.createObject(RecollectionItem.class);
    item.setFileKey(fileKey);
    item.setDate(date);
    item.setType(type);
    item.setUri(uri);

    RecollectionGroup group = realm.where(RecollectionGroup.class)
            .equalTo(RecollectionGroup.FIELD_URI, uri)
            .findFirst();
    if (group == null) { // 没有对应的group自动创建一个新的
        group = realm.createObject(RecollectionGroup.class, uri);
    }
    group.setName(defaultName);
    // 这里按日期降序直接找到新插入的位置,插入后即排好序
    int position = findPositionToInsert(group.getItems(), item);
    if (position == 0) { // 注意更新latest
        group.setLatestDate(item.getDate());
        group.setLatestItem(item);
    }
    group.getItems().add(position, item);
}
private static int findPositionToInsert(RealmList<RecollectionItem> items, RecollectionItem item) {
    int n = items.size();
    for (int i = 0; i < n; i++) {
        if (item.getDate().compareTo(items.get(i).getDate()) > 0) {
            return i;
        }
    }
    return n;
}
// 删除
public static void deleteRecollectionItem(RecollectionGroup group, RecollectionItem item) {
    RealmHelper.executeTransaction(realm -> {
        // 如果删除的是最新的一条,记得更新group中的冗余数据,Item没有主键,这里用FileKey来判断
        boolean needUpdateLatest = item.getFileKey().equals(group.getLatestItem().getFileKey());
        item.deleteFromRealm();
        if (group.getItems().isEmpty()) {
            group.deleteFromRealm(); // 没有item直接删掉,会省很多事
        } else if (needUpdateLatest) {
            RecollectionItem latest = group.getItems().get(0);
            group.setLatestItem(latest);
            group.setLatestDate(latest.getDate());
        }
    });
}

切换目录

自动更新

Realm内置自动更新的编写制定,非凡有利于,只必要在界面中添加四个监听,发生变化时间接刷新列表即可,在此之前查询到的数码(RealmResult<T>)都会在触及监听后自动更新,也便是说在监听的回调中即可刷新Adapter。

// UI上有两级列表,一个是Group列表,一个是Item列表,本质上都是显示Item,故用同一个界面显示,使用参数区分一下。
if (isUserListMode()) {
    mRecollectionGroups = mRealm.where(RecollectionGroup.class)
            .findAllSorted(RecollectionGroup.FIELD_LATEST_DATE, Sort.DESCENDING);
    setTitleForActivity(getString(R.string.Memories));
} else { // 对一个Group的所有条目也使用Group列表,方便监听变化
    mRecollectionGroups = mRealm.where(RecollectionGroup.class)
            .equalTo(RecollectionGroup.FIELD_URI, mUri)
            .findAll();
    mRecollectionItems = mRecollectionGroups.size() > 0 ? mRecollectionGroups.get(0).getItems() : null;
    setTitleForActivity(getDisplayName(mRealm, mUri, mDefaultName));
}
// 只需要在Group列表上进行监听即可,因为每个Item的变化都会引起Group的变化(items字段)
mRecollectionGroups.addChangeListener(e -> {
    mAdapter.notifyDataSetChanged();
    configEmptyView();
});

cd

旧数据导入引发的题材

原效劳是从未有过数据库的,音讯都保存在文书自己里,比如文件名保存了类型音信,文件的创制时间属性保存了文件的创制时间。而新本子要求的用户消息,并不曾其他保存,对于在此之前版本发生的公文,是素有未曾办法分类到正确的用户的,所以利用了1个特种用户,将旧版本的数码都导入了进入。

旧版本发出的文本并简单处理,最有意思的是安卓删除app后并不会删除这一个存放文件的目录,之前设置的本子暴发的文书对新安装的次序也是可知的,并不一定卸载的正是旧版本。要是新本子产生了文本,然后卸载,然后再设置新本子,那一个时候,就须要把前边产生的公文导入到数据库中。

如果文件本人提供源源全数的音讯,那么就只能遵依旧数据一致去导入了。在开创文件的时候,是能够把这些效用须求的有着消息都保存到文件本身中的:

  • date:即文件成立的岁月,读取文件属性能够拿走。
  • uri:保存到文件名里,从文件名读取。
  • type:依照文件扩展名能够预计出来。

导入数据还有个难点需求考虑,正是导入的机遇,一般有三种做法,app运行时导入;另一种是应用时导入。三种做法各有利弊,假设是运行时导入,势供给拉开运维时间,大概占有AsyncTask线程;即使是在应用时导入,必然会延长第一遍选择时操作的年月,恐怕出现短暂的数量不一起状态。

我们选拔的方案是第一种,因为大家的app内(hen)容(duo)丰(la)富(ji),运转的时候做的事务越来越多,那些效应不是必须运营时就应用,就不要肇事了。于是决定在率先次打开列表界面包车型地铁时候创建新的线程导入。

其一策略后续也意识了有个别难点,比如在导入此前就发出的新的数量(通过拨打电话),等到导入的时候(打开列表),就供给区分哪些文件是新生成的,从而不导入这一个文件,避防发生两份相同的数量。

另多个难点,大家的数据库文件是按登录账号分隔的,二个账号登录看到的,换个账号是不是还是能见到?是还是不是还要导入一下。这几个题材就涉嫌到成品概念了,最终依据“不是同三个账号,不应有看到另一位的数量”的规则,不实行导入。(其实这么些原则是不堪推敲的:P)

翻看全体文件(包含隐藏)

至于Picasso的有个别选择体验

显示图片选取的是Picasso,对涂鸦生成的公文,展现的正是jpg文件,间接彰显没有毛病;而录像唯有八个mp4文件,必要体现摄像的缩略图,同样运用Picasso来显示就要自定义一些东西了。

Picasso的为主是加载(缓存)和出示,对于录制缩略图的题材,加载即透过录制文件总括第③帧的图像的经过,彰显就与经常图片相同没有分别了。所以要求自定义加载的有些,Picasso提供了RequestHandler来自定义请求的拍卖,即加载进程。

// 使用的时候直接传视频文件,与图片一样,不区分类型
public void onBindViewHolder(MyViewHolder holder, int position) {
    ...
    File file = new File(MyFavoriteManager.getFullPath(item.getFileKey()));
    Picasso.with(getContext())
            .load(file)
            .into(holder.mImageViewThumbnail);
    ...
 }
// 计算视频缩略图的RequestHandler
public class Mp4FileRequestHandler extends RequestHandler {
    @Override
    public boolean canHandleRequest(Request data) {
        Uri uri = data.uri;
        return uri.getScheme().toLowerCase().equals("file") && uri.getPath().toLowerCase().endsWith(".mp4");
    }
    @Override
    public Result load(Request data, int networkPolicy) throws IOException {
        Bitmap bitmap = getVideoFirstFrame(data.uri.getPath());
        return bitmap != null ? new Result(bitmap, Picasso.LoadedFrom.NETWORK) : null;
    }
    public static Bitmap getVideoFirstFrame(String path) {
        MediaMetadataRetriever media = new MediaMetadataRetriever();
        try {
            media.setDataSource(path);
            return media.getFrameAtTime(1);
        } catch (Exception e) {
            return null;
        }
    }
}
// 添加RequestHandler到Picasso,在app初始化的时候设置。
private void initializePicasso() {
    Picasso.setSingletonInstance(new Picasso.Builder(this)
            .addRequestHandler(new Mp4FileRequestHandler())
            .build());
}

ls -a

趟过的坑

Nexus 5(API
22)手提式有线电话机上海广播台频文件名含有特殊字符会不可能用Intent播放,其余系统一测试试过没有失水准,安卓种种版本系统都测试过也未曾难点。经过试验,将文件名中的”@”,”[“,”]”
去掉之后就没难题了,文件路径是透过编码的,不是编码的难题,原因未知。

但在调节和测试进度中窥见了部分好玩log:

E/StrictMode: file:// Uri exposed through Intent.getData()
  java.lang.Throwable: file:// Uri exposed through Intent.getData()
  at android.os.StrictMode.onFileUriExposed(StrictMode.java:1603)
  at android.net.Uri.checkFileUriExposed(Uri.java:2341)
  at android.content.Intent.prepareToLeaveProcess(Intent.java:7737)
  at android.app.Instrumentation.execStartActivity(Instrumentation.java:1495)
  at android.app.Activity.startActivityForResult(Activity.java:3745)

是 StrictMode
检查抛出的要命,然而并不是无力回天播放的由来,为了扑灭这些10分能够接纳FileProvider:
设若项目标 targetSdkVersion >=24,运维在Android
N系统中,那里应该生出3个有地位的相当:FileUriExposedException,要用FileProvider东西来拜会文件,具体参考那篇教您哪些促成拍照的官方教程:Taking
Photos
Simply

创设目录

mkdir

制造多级目录(递归成立)

mkdir  -p 123/abc 

 删除目录 (空)

rmdir  

除去目录

rm  -r 删除目录 -f 强制

rm -rf 强制删除目录

除去文件

rm

复制命令

cp

复制目录

cp -r

复制文件属性(内容 属性 时间 )

cp -a  

来得时间

date 

 剪切(如原来的文章件和指标文件在同一目录下 则为化名命令)

mv 

链接命令

ln [原文件][对象文件]

ln -s 创设软链接  (源文件一定要写相对路径)

文件搜索命令

locate [文件名]

在后台数据库中按文件名搜索 搜索速度快(只可以寻找文件名,只彰显四个)

/var/lib/mlocate

locate命令所搜索的后台数据库 更新频率 1天一更新

updatedb

履新数据库 

文本搜索规则 :/etc/updatedb.conf

 

常用目录

/ 根目录

/bin 命令保存目录 /usr/bin  (全数人都可访问) /sbin /usr/sbin root访问 

/boot 运营目录,运行有关文书

/dev 设备文件保留目录

/etc  配置文件目录(^_^etc停车) 

/home 用户家目录

/lib 系统库保存目录

/mnt 系统挂载目录

/media 挂载目录

/root 一流用户家目录

/tmp 近期目录

/proc 直接写入内部存款和储蓄器的

/sys

/usr 系统软件财富目录

/var 系统可变文书档案

 

相关文章